Compare commits

...

246 Commits

Author SHA1 Message Date
nuoya 3e267c7796 Update cstore_allocspace.cpp 2023-10-04 21:24:54 +08:00
lucky 277e454f4e Merge branch 'master' of https://gitlink.org.cn/nuoya/openGauss-server 2023-10-04 13:24:05 +08:00
lucky 9608c283dc 关于“src/gausskernel/process"文件夹下的注释文件更新 2023-10-04 13:23:56 +08:00
noah c82c9fef60 Merge branch 'master' of https://gitlink.org.cn/nuoya/openGauss-server 2023-10-04 13:19:26 +08:00
noah 69597c7b86 process文件夹下文件注释更新 2023-10-04 13:19:12 +08:00
noah d53fdbf6bd storage文件夹注释文件更新 2023-10-04 13:11:22 +08:00
Nemoo 491e17288c Update streamTransportComm.cpp 2023-09-30 21:09:08 +08:00
Nemoo b2fcba5475 Update stream_cost.cpp 2023-09-30 21:08:36 +08:00
Nemoo da3888eed6 Update streamMain.cpp 2023-09-30 21:07:40 +08:00
Nemoo 46863f5435 Update streamCore.cpp 2023-09-30 21:07:12 +08:00
Nemoo 7f78629009 Update streamConsumer.cpp 2023-09-30 21:06:42 +08:00
Nemoo fb37311a96 Update execStream.cpp 2023-09-30 21:06:00 +08:00
nuoya e3e5b419bb Update buf_init.cpp 2023-09-30 18:45:41 +08:00
nuoya 421fc1209c Update buf_init.cpp 2023-09-30 18:45:26 +08:00
nuoya b02deb9a95 Update nas_am.cpp 2023-09-30 18:44:31 +08:00
noah 1dc7657220 Merge branch 'master' of https://gitlink.org.cn/nuoya/openGauss-server 2023-09-30 18:35:52 +08:00
noah b3e06e4ee1 about new work 2023-09-30 18:33:34 +08:00
Nemoo cc65d639ca Update threadpool_worker.cpp 2023-09-30 14:22:46 +08:00
Nemoo e1d6a2c7a5 Update threadpool_stream.cpp 2023-09-30 14:22:05 +08:00
Nemoo 77f7e80d9a Update threadpool_sessctl.cpp 2023-09-30 14:21:36 +08:00
Nemoo c78831340c Update threadpool_scheduler.cpp 2023-09-30 14:21:02 +08:00
Nemoo 844c260a3b Update threadpool_listener.cpp 2023-09-30 14:20:21 +08:00
Nemoo 6e808f3943 Update threadpool_group.cpp 2023-09-30 14:19:38 +08:00
Nemoo a4a404f207 Update threadpool_controler.cpp 2023-09-30 14:18:56 +08:00
Nemoo 93af93324b Update knl_thread.cpp 2023-09-30 14:17:40 +08:00
Nemoo 87d2bf33c7 Update knl_session.cpp 2023-09-30 14:16:39 +08:00
Nemoo 35c4859f34 Update knl_instance.cpp 2023-09-30 14:15:11 +08:00
noah de7a7777a0 about process storage upgrade 2023-09-30 13:51:23 +08:00
noah b57f14fe8a about process storage upgrade 2023-09-30 13:48:19 +08:00
lucky cb3246a733 来自:openGauss-server/src/gausskernel/process/postmaster 2023-09-25 16:39:32 +08:00
lucky 8df0769f58 -A 2023-09-25 16:31:25 +08:00
nuoya 108c7fd535 Update globalplancache_inval.cpp 2023-09-25 16:20:00 +08:00
nuoya 303eebba66 Update globalplancache_util.cpp 2023-09-25 16:19:40 +08:00
nuoya 1e0967d668 Update xlog_share_storage.cpp 2023-09-25 15:35:36 +08:00
nuoya 0c739ef4ba Update tcap_truncate.cpp 2023-09-25 15:35:09 +08:00
nuoya 365721c3c0 Update tcap_drop.cpp 2023-09-25 15:34:53 +08:00
nuoya 0f11f2c6f5 Update knl_usync.cpp 2023-09-25 15:34:28 +08:00
nuoya c8669968c1 Update segxlog.cpp 2023-09-25 15:34:03 +08:00
nuoya 84314ca86a Update segbuffer.cpp 2023-09-25 15:33:47 +08:00
nuoya a662088e7f Update inverse_ptr.cpp 2023-09-25 15:33:32 +08:00
nuoya cd8b7da3c0 Update extent_group.cpp 2023-09-25 15:33:17 +08:00
nuoya e2d08668b6 Update data_file.cpp 2023-09-25 15:33:01 +08:00
nuoya 36235f8e12 Update smgr.cpp 2023-09-25 15:32:44 +08:00
nuoya 639fb3990c Update segstore.cpp 2023-09-25 15:31:59 +08:00
nuoya 902c179c9d Update page_compression.cpp 2023-09-25 15:31:40 +08:00
nuoya fc4b67f9ea Update mmap_shared.cpp 2023-09-25 15:31:23 +08:00
nuoya a4317b822b Update md.cpp 2023-09-25 15:31:03 +08:00
nuoya fdf406b7d1 Update knl_uundofile.cpp 2023-09-25 15:30:46 +08:00
nuoya 196cd35e8a Update dorado_fd.cpp 2023-09-25 15:30:20 +08:00
nuoya da2a11dd1c Update obs_connector.cpp 2023-09-25 15:29:55 +08:00
Nemoo 6a29026dbd Update aggregatecmds.cpp 2023-08-27 22:09:56 +08:00
LuckYzaw a3dbaa662a Update alarmchecker.cpp 2023-08-27 16:18:21 +08:00
LuckYzaw 8abe360784 Update aiocompleter.cpp 2023-08-27 16:16:38 +08:00
nuoya e80e9b46c1 Update tcap_version.cpp 2023-08-27 16:13:15 +08:00
nuoya bcbebe4ccd Update tcap_manager.cpp 2023-08-27 16:06:13 +08:00
xiangxinyong 8de0f268bd Merge pull request 'Update parse_agg.cpp' (#12) from Efncm2qyz/openGauss-server:master into master 2022-10-30 14:18:54 +08:00
xiangxinyong 70a1854ad9 Merge pull request '测试提交合并' (#13) from litm12138/openGauss-server:master into master 2022-10-30 14:18:34 +08:00
xiangxinyong defd901624 Merge pull request '表达式计算函数注释' (#14) from Rorre/openGauss-server:master into master 2022-10-30 14:18:22 +08:00
xiangxinyong 7b49a450dc Merge pull request '增加有关加密方式的注释' (#16) from Egok4wryu/openGauss-server:master into master 2022-10-30 14:16:33 +08:00
Egok4wryu 01163f2e40 add the annotation in the function of GsSm3Encrypt 2022-10-20 11:31:29 +08:00
Egok4wryu 5eb65aa4ca add the annotation in the function of calcultate_encrypted_combined_password 2022-10-20 11:19:11 +08:00
Egok4wryu 95b7221f15 add the annotation in the function of pg_md5_encrypt 2022-10-20 11:13:26 +08:00
Egok4wryu 200c9b14a7 add the annotation of the function of calculate_encrypted_password 2022-10-20 11:08:02 +08:00
Egok4wryu 95cd0ad4c7 add the annotation of the encryption type of new SM3 2022-10-19 15:46:42 +08:00
Rorre 8790761995 Update execQual.cpp 2022-10-16 21:11:31 +08:00
litm12138 ae7a0bc797 Update create_server.py 2022-10-16 20:01:38 +08:00
litm12138 a6ce672068 Add test.txt 2022-10-16 19:55:57 +08:00
Efncm2qyz 9e915d9fbf Update parse_agg.cpp 2022-10-16 09:56:03 +08:00
xiangxinyong 76e3f4dad6 Merge pull request '对src/gausskernel/cbb/communication下cpp文件中一些函数的注释' (#9) from Eao3piq4e/openGauss-server:master into master 2022-08-20 22:57:03 +08:00
xiangxinyong 8e9089b6fe Merge pull request '新增了对文件的部分注释' (#10) from xinran/openGauss-server:master into master 2022-08-20 22:56:56 +08:00
xiangxinyong 4b7af41aff Merge pull request 'dbmind中sql语句执行文件的注释' (#11) from Eukanj827/openGauss-server:master into master 2022-08-20 22:56:41 +08:00
Eukanj827 313e3c4e00 Update gsql_execute.py 2022-08-15 10:26:07 +08:00
Eukanj827 43c3001415 Update gsql_execute.py 2022-08-15 10:21:41 +08:00
Eukanj827 d9cde8c89b Update execute_factory.py 2022-08-15 10:08:36 +08:00
Eukanj827 91a7306a88 Update execute_factory.py 2022-08-15 10:08:06 +08:00
Eukanj827 eb4c1bd953 Update driver_execute.py 2022-08-15 09:58:17 +08:00
xinran 991925bfe1 ADD file via upload 2022-08-14 22:30:25 +08:00
xinran 6021c26abe Delete 'src/gausskernel/dbmind/tools/components/sqldiag/algorithm/duration_time_model/template.py' 2022-08-14 22:29:48 +08:00
xinran 817264937d ADD file via upload 2022-08-14 22:28:40 +08:00
xinran 62e2170f65 Delete 'src/gausskernel/dbmind/tools/components/predictor/python/model.py' 2022-08-14 22:27:32 +08:00
Eao3piq4e 19adb21866 Update libcomm_lqueue.cpp 2022-08-13 11:25:18 +08:00
Eao3piq4e 3446cb0aba Update libcomm_client_ssl.cpp 2022-08-12 22:50:12 +08:00
Eao3piq4e 3c0a229006 Update libcomm_client_ssl.cpp 2022-08-12 11:56:28 +08:00
xiangxinyong 5f8ca172c7 Merge pull request '对src/gausskernel/cbb/communication/comm_proxy/目录下cpp文件中一些函数的注释' (#8) from Eao3piq4e/openGauss-server:master into master 2022-08-12 09:54:54 +08:00
Eao3piq4e 22b3c75a36 Update libcomm_client_ssl.cpp 2022-08-12 09:43:45 +08:00
Eao3piq4e 8f463f2f77 Update libcomm_adapter.cpp 2022-08-11 18:49:14 +08:00
Eao3piq4e 95fafe1ce0 Update libcomm_adapter.cpp 2022-08-11 18:33:56 +08:00
Eao3piq4e 59ad28eed7 Update libcomm_adapter.cpp 2022-08-11 18:33:10 +08:00
Eukanj827 4c345b1d31 Update gsql_execute.py 2022-08-11 17:32:33 +08:00
Eukanj827 62e6f3a028 Update execute_factory.py 2022-08-11 17:32:10 +08:00
Eukanj827 c7eae459f2 Update driver_execute.py 2022-08-11 17:31:27 +08:00
Eao3piq4e f9b4d9a702 Update mc_tcp.cpp 2022-08-10 19:19:24 +08:00
Eao3piq4e 11e6760776 Update mc_tcp.cpp 2022-08-10 19:16:31 +08:00
Eao3piq4e 272ebd125f Update mc_tcp.cpp 2022-08-10 18:37:51 +08:00
Eao3piq4e 3a2a690ee8 Update mc_tcp.cpp 2022-08-10 16:25:37 +08:00
Eao3piq4e d0a5914e3f Update mc_tcp.cpp 2022-08-10 15:58:22 +08:00
Eao3piq4e dc65ea0040 Update mc_tcp.cpp 2022-08-10 12:53:44 +08:00
Eao3piq4e 4728c88126 Update mc_tcp.cpp 2022-08-10 11:10:09 +08:00
Eao3piq4e cd677d1243 Update mc_tcp.cpp 2022-08-09 23:09:46 +08:00
Eao3piq4e 3fde3abb91 Update encrypt.cpp 2022-08-09 18:11:45 +08:00
Eao3piq4e 7679dfe93b Update asan_report.pl 2022-08-09 18:10:13 +08:00
Eao3piq4e 78d7d8e54f Update mc_tcp.cpp 2022-08-09 17:11:49 +08:00
Eao3piq4e 8cd2925f93 Update comm_proxy.cpp 2022-08-08 17:57:55 +08:00
Eao3piq4e a4e1b350cb Update comm_proxy.cpp 2022-08-08 17:38:07 +08:00
Eao3piq4e 32cdbbabff Update comm_proxy.cpp 2022-08-08 13:48:22 +08:00
Eao3piq4e cdc8d9748f Update comm_proxy.cpp 2022-08-08 13:45:30 +08:00
Eao3piq4e 9017a1a64e Update comm_proxy.cpp 2022-08-08 12:14:57 +08:00
xiangxinyong 751e1c4267 Merge pull request '对xtuner的注释进行了一些补充' (#6) from xinran/openGauss-server:master into master 2022-08-06 19:03:41 +08:00
xiangxinyong ac1d40fca2 Merge pull request '对src/gausskernel/cbb/communication/comm_proxy/目录下cpp文件中一些函数的注释' (#7) from Eao3piq4e/openGauss-server:master into master 2022-08-06 19:03:19 +08:00
Eao3piq4e a7af1741d2 Update comm_dfx.cpp 2022-08-05 18:42:54 +08:00
Eao3piq4e 8d84b37543 Update comm_core.cpp 2022-08-05 13:26:26 +08:00
Eao3piq4e bab8f82a0e Update comm_common.cpp 2022-08-05 11:52:39 +08:00
Eao3piq4e 3c1a1c2bef Update comm_common.cpp 2022-08-05 11:49:40 +08:00
Eao3piq4e 8902cfd59a Update comm_common.cpp 2022-08-05 10:40:56 +08:00
Eao3piq4e e7dd9fa2a1 Update comm_common.cpp 2022-08-05 09:09:08 +08:00
xinran 2812c3315c ADD file via upload 2022-08-04 21:55:05 +08:00
xinran 5c988a09f4 Delete 'src/gausskernel/dbmind/tools/components/xtuner/tuner/xtuner.py' 2022-08-04 21:50:56 +08:00
xiangxinyong bcfff28219 Merge pull request '对src/gausskernel/cbb/bbox/目录下cpp文件中一些函数的注释' (#4) from Eao3piq4e/openGauss-server:master into master 2022-08-04 20:58:13 +08:00
xiangxinyong 72a82e2182 Merge pull request 'dbmind中部分文件的注释' (#5) from Eukanj827/openGauss-server:master into master 2022-08-04 20:58:05 +08:00
Eukanj827 c7e4a74544 Update misc.py 2022-08-04 20:55:05 +08:00
Eukanj827 cf86dd46f5 Update alarm.py 2022-08-04 19:58:05 +08:00
Eukanj827 edf79014bf Update controller_aop.py 2022-08-04 19:16:50 +08:00
Eao3piq4e fb3e4435e0 Update comm_common.cpp 2022-08-04 18:05:18 +08:00
Eao3piq4e 88840f46d2 Update gs_bbox.cpp 2022-08-04 13:31:26 +08:00
Eao3piq4e 54de7afd7d Update gs_bbox.cpp 2022-08-04 12:35:06 +08:00
Eao3piq4e 907997d5a6 Update gs_bbox.cpp 2022-08-04 11:48:02 +08:00
Eao3piq4e 55ae2df08c Update gs_bbox.cpp 2022-08-04 09:41:37 +08:00
Eao3piq4e 340b61c47f Update bbox_threads.cpp 2022-08-03 23:40:40 +08:00
Eao3piq4e 4761d514e6 Update bbox_threads.cpp 2022-08-03 23:23:10 +08:00
Eao3piq4e 0ac4f9bd4b Update bbox_threads.cpp 2022-08-03 22:50:05 +08:00
Eukanj827 1a3fad4d43 Update edbmind.py 2022-08-03 21:11:28 +08:00
Eukanj827 730e66cdf0 Update config_utils.py 2022-08-03 21:00:46 +08:00
Eao3piq4e 00dd058431 Update bbox_threads.cpp 2022-08-03 17:58:44 +08:00
Eao3piq4e 36efc4cbb7 Update bbox_threads.cpp 2022-08-03 13:37:26 +08:00
Eao3piq4e 38ca00c31c Update bbox_threads.cpp 2022-08-03 12:39:48 +08:00
Eao3piq4e 2165635b9a Update bbox_print.cpp 2022-08-03 11:58:08 +08:00
Eukanj827 e06c03a579 Update __init__.py 2022-08-03 10:48:44 +08:00
xiangxinyong 4cd25670b5 Merge pull request 'sql慢查询注释' (#3) from Eukanj827/openGauss-server:master into master 2022-08-03 09:29:40 +08:00
xiangxinyong f70898ecf5 Merge pull request '对BBOX_DetermineMsb函数的更详细描述' (#2) from Eao3piq4e/openGauss-server:master into master 2022-08-03 09:29:29 +08:00
Eao3piq4e 95466298fa Update bbox_print.cpp 2022-08-02 23:34:06 +08:00
Eao3piq4e 119d5d39da Update bbox_print.cpp 2022-08-02 22:23:59 +08:00
Eao3piq4e 537cba6247 Update bbox_lib.cpp 2022-08-02 21:18:32 +08:00
Eao3piq4e 14c4068daa Update bbox_lib.cpp 2022-08-02 20:50:21 +08:00
Eao3piq4e cf8e1541b1 Update bbox_lib.cpp 2022-08-02 18:05:36 +08:00
Eao3piq4e 58d5c900c9 Update bbox_lib.cpp 2022-08-02 17:15:14 +08:00
Eao3piq4e 25332da4aa Update bbox_lib.cpp 2022-08-02 17:01:46 +08:00
Eukanj827 fd0098411a Update __init__.py 2022-08-02 14:54:35 +08:00
Eukanj827 b4f33b07cf Update average_base.py 2022-08-02 14:44:04 +08:00
Eukanj827 caccfc026d Update feature_model.py 2022-08-02 14:01:17 +08:00
Eao3piq4e 30621924cf Update bbox_lib.cpp 2022-08-02 12:55:12 +08:00
Eukanj827 abb0cee0fc Update __init__.py 2022-08-02 12:37:44 +08:00
Eao3piq4e 0c2af1d742 Update bbox_lib.cpp 2022-08-02 11:50:12 +08:00
Eao3piq4e 09e299ac8a Update bbox_lib.cpp 2022-08-02 10:14:02 +08:00
Eao3piq4e 7dc9903be8 Update bbox_elf_dump_base.cpp 2022-08-02 09:19:40 +08:00
xiangxinyong bc0dc304c8 合并来自罗格的代码注释
合并来自罗格的代码注释
2022-07-28 14:55:50 +08:00
Eao3piq4e 5f334a5656 Update encrypt.cpp 2022-07-28 12:05:27 +08:00
Eao3piq4e 4ec1513e05 Update crypt.cpp 2022-07-28 10:58:29 +08:00
Eao3piq4e fb0026b93b Update crypt.cpp 2022-07-28 10:49:45 +08:00
Eao3piq4e ce56fb62b6 Update asan_report.pl 2022-05-29 10:13:24 +08:00
Eao3piq4e 2b198387ab Update asan_report.pl 2022-05-29 10:07:32 +08:00
opengauss-bot 72f4d68de6
!1772 修复在部分场景下,非sysadmin用户执行gs_dump失败的问题
Merge pull request !1772 from pengjiong/drop_ext
2022-05-28 11:52:08 +00:00
TotaJ 62e3bed09b Fix gs_dump 2022-05-28 17:58:05 +08:00
opengauss-bot 7d4d95a5ad
!1764 函数使用internal类型的限制添加wm_concat
Merge pull request !1764 from 吕辉/wm_concat
2022-05-28 07:00:10 +00:00
opengauss-bot 240f61c595
!1740 compressByteConvert\compressDiffConvert内存判断当且仅当用户输入为true而不是判断用户是否使用该参数
Merge pull request !1740 from 吴岳川/I54KW0
2022-05-28 02:02:39 +00:00
opengauss-bot a0d1a547f8
!1741 【压缩特性】在回放过程中,假设表文件已经被创建而表_pca等文件未被创建,flags会被修改,丢失O_CREATE标记,导致备机回放失败
Merge pull request !1741 from 吴岳川/I56GGM
2022-05-28 01:43:32 +00:00
opengauss-bot df804a8a91
!1763 在support_extended_features场景下,支持删除插件
Merge pull request !1763 from pengjiong/drop_ext
2022-05-26 11:10:25 +00:00
TotaJ 91917dcfaf Fix drop extension. 2022-05-26 16:22:52 +08:00
lvhui e2163187ca add wm_concat in InternalAggIsSupported 2022-05-26 15:34:47 +08:00
opengauss-bot 1d7eadd778
!1762 [bugfix] 修复host_standby=off/wal_level=archive场景下failover/switchover出现tuple concurrently updated的问题
Merge pull request !1762 from 周斌/gsc_hoststandby
2022-05-26 01:31:22 +00:00
opengauss-bot b335ef9af7
!1761 [feature] 修复GSC函数接口内存重复统计问题
Merge pull request !1761 from 周斌/gsc_cache_duplicate
2022-05-26 01:30:00 +00:00
justbk 572d99d03c repair wal_level=archive in switchover/failover get tuple concurrently updated issue 2022-05-25 20:17:01 +08:00
opengauss-bot 0b0959fc69
!1760 DFX: add exit log in PercentileMain
Merge pull request !1760 from 吴岳川/I5696K
2022-05-25 11:25:07 +00:00
justbk 238ee19a59 repair gs_gsc_memory_detail memory info inaccurate issue 2022-05-25 15:43:39 +08:00
opengauss-bot d199d90a71
!1747 修复复合datum产生的record报错
Merge pull request !1747 from 仲夏十三/helper
2022-05-25 03:17:57 +00:00
opengauss-bot 2a77a3cebc
!1751 增加alter procedure帮助说明
Merge pull request !1751 from 仲夏十三/helper2
2022-05-25 03:15:27 +00:00
opengauss-bot 214d01ddb6
!1759 [bugfix] 修复备机GSC不缓存数据问题
Merge pull request !1759 from 周斌/no_gsc_cache
2022-05-25 02:40:49 +00:00
opengauss-bot 4f024161f0
!1758 [bugfix] 修复openGauss数据库关闭时pagewriter主线程退出失败的问题
Merge pull request !1758 from 周斌/master
2022-05-25 02:38:02 +00:00
wuyuechuan 82d5ac65b0 DFX: add exit log in PercentileMain 2022-05-25 10:13:36 +08:00
justbk 7edda5e2a4 repair GSC no cache issue 2022-05-24 21:47:43 +08:00
justbk e10c482649 repair opengauss close failed issue 2022-05-24 20:29:11 +08:00
opengauss-bot 0a29277070
!1739 修复wal2json插件编译时报logical.h文件中的DefElem类型未声明的错误
Merge pull request !1739 from 薛蒙恩/com_wal2json
2022-05-23 12:20:33 +00:00
xue_meng_en 96dad83a1d 修复wal2json插件编译时报logical.h文件中的DefElem类型未声明的错误 2022-05-23 17:27:49 +08:00
opengauss-bot d433ee097a
!1746 Masstree OOM feature
Merge pull request !1746 from Vinoth Veeraraghavan/master
2022-05-23 03:32:46 +00:00
ganyang a590e0b47f add alter procedure helper note 2022-05-23 11:00:17 +08:00
仲夏十三 7f8b900e69 Merge branch 'master' of gitee.com:opengauss/openGauss-server into helper 2022-05-20 07:40:19 +00:00
ganyang f14255f040 fix record type issue caused by composite datum 2022-05-20 15:38:19 +08:00
wuyuechuan 5af581f12a use `RetryDataFileIdOpenFile` to prevent the flags from being modified 2022-05-20 11:57:25 +08:00
Vinoth Veeraraghavan 26793e3bf2 Masstree out of memory feature:
1. Fix RCU bug in masstree (GC layer removal flow)
    2. Support pool allocation failure in masstree
    3. Add new masstree API changes
2022-05-20 10:46:52 +08:00
wuyuechuan dbcf479205 set compressByteConvert/compressDiffConvert to true when defElem is set to true 2022-05-18 14:41:15 +08:00
opengauss-bot ae11cad2c3
!1738 修复\h drop table 查看语法帮助显示语法错误
Merge pull request !1738 from 仲夏十三/helper
2022-05-17 09:50:24 +00:00
ganyang 57bc5c4a37 fix helper note 2022-05-17 10:22:29 +08:00
opengauss-bot fb449157d0
!1710 修复join结果不正确的bug
Merge pull request !1710 from Cross-罗/join_alias
2022-05-16 07:21:34 +00:00
opengauss-bot 6c5898646e
!1726 修复视图中包含自定义操作符时,获取视图定义失败的问题
Merge pull request !1726 from pengjiong/logical_core
2022-05-13 09:11:34 +00:00
TotaJ c1335bebc9 Fix deserialize func and view. 2022-05-13 14:55:39 +08:00
opengauss-bot b2c6e04eab
!1725 重命名b_sql_plugin
Merge pull request !1725 from 仲夏十三/dolphin
2022-05-12 08:34:58 +00:00
ganyang 16d32d3978 rename b_sql_plugin 2022-05-12 14:30:24 +08:00
opengauss-bot 3e14e85fdf
!1698 添加对asianux平台支持
Merge pull request !1698 from zhangminjie/master
2022-05-10 07:35:19 +00:00
opengauss-bot 438bba32ff
!1705 修复 master 分支 和 3.0.0 分支创建 postgis_raster 扩展失败问题
Merge pull request !1705 from sungwu/master
2022-05-10 06:07:23 +00:00
opengauss-bot d82eba4790
!1721 【bugfix】修复interval分区exchange失败的问题
Merge pull request !1721 from cchen676/master1
2022-05-10 02:19:45 +00:00
arcoalien@qq.com 4b7080c8ef [回合主线]fix bug interval分区exchange失败 2022-05-09 20:55:12 +08:00
zhangminjie1997 5e1d9b79f6 添加对asianux平台支持 2022-05-09 11:40:22 +08:00
opengauss-bot 51c355c7b6
!1699 fix core of function pg_create_physical_replication_slot_extern
Merge pull request !1699 from 胡正超/repslot
2022-05-09 02:48:37 +00:00
luozihao 4c0a495a18 fixed the bug of join 2022-05-05 09:45:50 +08:00
wusong cbb8fd609b fix #I51OZS 修复在扩展中无法调用已创建函数问题,sort_candidate_func_list 函数实现 bug 2022-04-27 20:15:23 +08:00
opengauss-bot 475170ca0c
!1703 修复plpgsql在使用b_sql_plugin插件的情况下没有正确使用插件语法解析器的bug
Merge pull request !1703 from Cross-罗/plpgsql
2022-04-27 11:01:46 +00:00
l00584793 7f9a1f5428 Fix the bug that the pgplsql invokes the syntax parser error. 2022-04-27 16:35:38 +08:00
opengauss-bot c04e377cdc
!1701 修复升级后subbinary为空导致发布订阅报错问题
Merge pull request !1701 from 薛蒙恩/pubsub_bnrupg
2022-04-27 02:08:51 +00:00
opengauss-bot 6380ca0f4d
!1702 修复分区表issue
Merge pull request !1702 from 仲夏十三/333
2022-04-26 12:43:21 +00:00
kidhasdream 4898f04a3c change fastcheck 2022-04-26 20:06:11 +08:00
ganyang 11a1a5c9ad fix partition error 2022-04-26 19:15:23 +08:00
opengauss-bot a30ae9d5ad
!1700 修复ustore二级分区以及生成列场景下,发布订阅数据同步失败的问题
Merge pull request !1700 from pengjiong/logical_core
2022-04-26 07:24:18 +00:00
xue_meng_en 0b70c7ab70 修复升级后subbinary为空导致发布订阅报错问题 2022-04-26 11:29:41 +08:00
TotaJ f719422916 Fix ustore bug in publication. 2022-04-26 09:35:24 +08:00
opengauss-bot 88dcc7f99e
!1697 修复由于CurrentResourceOwner为NULL导致的逻辑解码core问题
Merge pull request !1697 from pengjiong/logical_core
2022-04-25 11:59:17 +00:00
gentle_hu dbd9958443 fix core of function pg_create_physical_replication_slot_extern 2022-04-25 19:09:58 +08:00
TotaJ 38b5a0ac95 Fix logical decode core. 2022-04-25 17:48:53 +08:00
opengauss-bot a548f5c3c6
!1675 修复发布订阅死锁问题
Merge pull request !1675 from 薛蒙恩/pubsub_deadlock
2022-04-24 01:53:42 +00:00
opengauss-bot eb4e54d4eb
!1678 openGauss 发布订阅支持gs_probackup备份恢复后连接不断开
Merge pull request !1678 from chenxiaobin/probackup
2022-04-22 02:42:08 +00:00
chenxiaobin19 3f7a909887 gs_probackup支持备份发布订阅的逻辑复制槽 2022-04-18 20:56:52 +08:00
xue_meng_en d8b89ceea5 修复发布订阅死锁问题 2022-04-16 19:34:20 +08:00
opengauss-bot 2250adfd4b
!1671 Implementing Monte Carlo Tree Search Algorithm
Merge pull request !1671 from liuly/master
2022-04-15 07:36:01 +00:00
flyly fd34b5da2a Implementing Monte Carlo Tree Search Algorithm 2022-04-14 17:11:09 +08:00
opengauss-bot 7f97d633f1
!1665 发布订阅支持以二进制格式发送数据和发布端主备切换不断开
Merge pull request !1665 from 薛蒙恩/pubsub330
2022-04-12 01:32:52 +00:00
opengauss-bot 6ba4c95f5f
!1666 修复enable_global_syscache关闭时连接B兼容性数据库的core问题
Merge pull request !1666 from chenxiaobin/fixbcore
2022-04-11 11:10:58 +00:00
xue_meng_en 5755feee5d 发布订阅支持主备切换不断开 2022-04-11 10:14:20 +08:00
opengauss-bot cd5d44d66c
!1668 gs_ctl添加-C参数适配流式容灾
Merge pull request !1668 from zhangxubo/master
2022-04-08 08:37:04 +00:00
zhang_xubo 2d657cddbe 添加参数适配流式容灾 2022-04-08 15:13:35 +08:00
chenxiaobin19 ccd674c159 修复enable_global_syscache关闭时连接B兼容性数据库的core问题 2022-04-08 11:11:07 +08:00
xue_meng_en 8292238381 发布订阅支持以二进制格式发送数据 2022-04-07 11:08:45 +08:00
opengauss-bot 5054ddd002
!1661 修复sql插件切换数据库插件依然存在
Merge pull request !1661 from 仲夏十三/bb0aa02e
2022-04-06 08:26:34 +00:00
ganyang 48a5033c18 fix space error 2022-04-06 10:32:10 +08:00
ganyang 811a9afcd9 optimize judgement 2022-04-02 16:44:36 +08:00
ganyang bb0aa02eb0 fix plugin hook issue 2022-04-02 15:01:58 +08:00
opengauss-bot 02c14696bf
!1657 【轻量级 PR】:修复mxid_to_string在TopMemoryContext分配内存的问题
Merge pull request !1657 from chenxiaobin/N/A
2022-04-01 07:59:00 +00:00
chenxiaobin 877f0b69e3
修复mxid_to_string在TopMemoryContext分配内存的问题 2022-04-01 06:53:42 +00:00
opengauss-bot 120ddcc6d8
!1654 【轻量级 PR】:修复CheckTimeout TimestampTz和float类型运算导致的精度丢失问题
Merge pull request !1654 from chenxiaobin/N/A
2022-04-01 06:17:39 +00:00
opengauss-bot 2cc80a5727
!1656 回滚pg_stat_get_wal_senders的升级
Merge pull request !1656 from 胡正超/master
2022-04-01 03:38:06 +00:00
gentle_hu 6d8bec171c revert upgrade of pg_stat_get_wal_senders() 2022-04-01 10:42:18 +08:00
chenxiaobin 9f7da01725
修复CheckTimeout TimestampTz和float类型运算导致的精度丢失问题 2022-04-01 02:17:51 +00:00
opengauss-bot ea938c947e
!1635 修复om安装时gs_guc设置 '' 给sync_standby_names解析错误的问题。
Merge pull request !1635 from 胡正超/anyxmaster
2022-03-31 14:53:24 +00:00
opengauss-bot 498d7ac08d
!1652 修复主备升级到3.0.0时失败的问题
Merge pull request !1652 from 胡正超/fixanyxupgrade
2022-03-31 14:33:55 +00:00
gentle_hu 40b717d9dd mv new column of pg_stat_get_wal_senders to tail 2022-03-31 21:29:49 +08:00
gentle_hu 94aeb766e5 fix issue of sync_standby_names='' 2022-03-31 20:08:33 +08:00
opengauss-bot 8577a61c7b
!1649 revert “发布订阅支持以二进制格式发送数据和主备切换不断开”
Merge pull request !1649 from 薛蒙恩/pubsub_revert
2022-03-31 11:33:59 +00:00
xue_meng_en 59db2a14cc Revert "发布订阅支持以二进制格式发送数据"
This reverts commit 71f53e575c.
2022-03-31 14:55:10 +08:00
xue_meng_en e7795cd01c Revert "发布订阅支持主备切换不断开"
This reverts commit aadf88ecc6.
2022-03-31 14:54:58 +08:00
256 changed files with 43871 additions and 16749 deletions

View File

@ -72,7 +72,7 @@ select_package_command
export PLAT_FORM_STR=$(sh "${ROOT_DIR}/src/get_PlatForm_str.sh")
if [ "${PLAT_FORM_STR}"x == "Failed"x -o "${PLAT_FORM_STR}"x == ""x ]
then
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64) platform."
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
exit 1;
fi
@ -96,16 +96,21 @@ elif [[ "$PLAT_FORM_STR" =~ "kylin" ]]; then
if [ "$PLATFORM_ARCH"X == "aarch64"X ];then
GAUSSDB_EXTRA_FLAGS=" -D__USE_NUMA"
fi
elif [[ "$PLAT_FORM_STR" =~ "asianux" ]]; then
dist_version="Asianux"
if [ "$PLATFORM_ARCH"X == "aarch64"X ];then
GAUSSDB_EXTRA_FLAGS=" -D__USE_NUMA"
fi
else
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64) platform."
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
echo "Kernel is $kernel"
exit 1
fi
##add platform architecture information
if [ "$PLATFORM_ARCH"X == "aarch64"X ] ; then
if [ "$dist_version" != "openEuler" ] && [ "$dist_version" != "EulerOS" ] && [ "$dist_version" != "Kylin" ] ; then
echo "We only support NUMA on openEuler(aarch64), EulerOS(aarch64), Kylin(aarch64) platform."
if [ "$dist_version" != "openEuler" ] && [ "$dist_version" != "EulerOS" ] && [ "$dist_version" != "Kylin" ] && [ "$dist_version" != "Asianux" ]; then
echo "We only support NUMA on openEuler(aarch64), EulerOS(aarch64), Kylin(aarch64), Asianux platform."
exit 1
fi
fi

View File

@ -26,6 +26,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterOperator SYSTEM "alter_operator.sgml">
<!ENTITY alterOperatorClass SYSTEM "alter_opclass.sgml">
<!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
<!ENTITY alterProcedure SYSTEM "alter_procedure.sgml">
<!ENTITY alterRole SYSTEM "alter_role.sgml">
<!ENTITY alterSchema SYSTEM "alter_schema.sgml">
<!ENTITY alterServer SYSTEM "alter_server.sgml">

View File

@ -0,0 +1,37 @@
<refentry id="sql-alterprocedure">
<indexterm zone="sql-alterprocedure">
<primary>ALTER PROCEDURE</primary>
</indexterm>
<refmeta>
<refentrytitle>ALTER PROCEDURE</refentrytitle>
<manvolnum>7</manvolnum>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>ALTER PROCEDURE</refname>
<refpurpose>change the definition of a procedure</refpurpose>
</refnamediv>
<refsynopsisdiv>
<synopsis>
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
<replaceable class="parameter">action</replaceable> [ ... ] [ RESTRICT ]
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
RENAME TO <replaceable>new_name</replaceable>
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
SET SCHEMA <replaceable>new_schema</replaceable>
<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
SET <replaceable class="parameter">configuration_parameter</replaceable> { TO | = } { <replaceable class="parameter">value</replaceable> | DEFAULT }
SET <replaceable class="parameter">configuration_parameter</replaceable> FROM CURRENT
RESET <replaceable class="parameter">configuration_parameter</replaceable>
RESET ALL
</synopsis>
</refsynopsisdiv>
</refentry>

View File

@ -11,7 +11,7 @@
<refsynopsisdiv>
<synopsis>
DROP TABLE [ IF EXISTS ]
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ]};
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ];
</synopsis>
</refsynopsisdiv>
</refentry>

View File

@ -27,7 +27,14 @@
#include "securec_check.h"
#include "cipher.h"
#include "crypt.h"
/*
function name: crypt_malloc_zero
description: Distribute internal memory
arguments: An integer that designates the size of internal memory distributed
return value: A pointer of type void*
NoteIf the size of internal memory distributed is zero, it's unreasonable. The size should be greater than zero.
At the same time, if malloc fails, program would exit.
*/
void* crypt_malloc_zero(size_t size)
{
void* ret = NULL;

View File

@ -34,6 +34,14 @@
static int check_key_num(const char* password);
static void create_child_dir(const char* pathdir);
/*
function name: check_path
description: Check if the string delivered has the character that should not be included
arguments: A pointer to string that its type is const char
return value: void
Notenone
*/
void check_path(const char *path_name)
{
const char* danger_character_list[] = {"|",
@ -69,6 +77,14 @@ void check_path(const char *path_name)
}
}
/*
function name: check_key_num
description: Check if the password is a null string, if so, then the password is invalid.
At the same time, the function check if the length of password exceeds MAX_CRYPT_LEN, if so, print the error.
arguments: A pointer to string that its type is const char
return value: An integer that its type is static int
NoteThe length of password should not be zero, and never exceeds MAX_CRYPT_LEN
*/
static int check_key_num(const char* password)
{
int key_len = 0;

View File

@ -1229,6 +1229,12 @@ parse_next_sync_groups(char **pgroup, char *result)
static int
transform_az_name(char *config_value, char *allAZString, int allAZStringBufLen, const char *data_dir)
{
if (strcmp(config_value, "''") == 0) {
errno_t rc = strncpy_s(allAZString, allAZStringBufLen, config_value, strlen(config_value));
securec_check_c(rc, "\0", "\0");
return SUCCESS;
}
char *azString = NULL;
char *buf = allAZString;
int buflen = allAZStringBufLen;

View File

@ -5923,7 +5923,7 @@ int main(int argc, char** argv)
&option_index)) != -1)
#endif
#else
while ((c = getopt_long(argc, argv, "b:cD:e:fi:G:l:m:M:N:o:O:p:P:r:R:v:x:sS:t:u:U:wWZ:dqL:T:Q:", long_options,
while ((c = getopt_long(argc, argv, "b:cD:e:fi:G:l:m:M:N:o:O:p:P:r:R:v:x:sS:t:u:U:wWZ:C:dqL:T:Q:", long_options,
&option_index)) != -1)
#endif
#endif

View File

@ -233,7 +233,7 @@ char* all_data_nodename_list = NULL;
const uint32 USTORE_UPGRADE_VERSION = 92368;
const uint32 PACKAGE_ENHANCEMENT = 92444;
const uint32 SUBSCRIPTION_VERSION = 92580;
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92607;
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
#ifdef DUMPSYSLOG
char* syslogpath = NULL;
@ -4454,16 +4454,7 @@ void getSubscriptions(Archive *fout)
}
if (!isExecUserSuperRole(fout)) {
res = ExecuteSqlQuery(fout,
"SELECT count(*) FROM pg_subscription "
"WHERE subdbid = (SELECT oid FROM pg_catalog.pg_database"
" WHERE datname = current_database())",
PGRES_TUPLES_OK);
uint64 n = (res != NULL) ? strtoul(PQgetvalue(res, 0, 0), NULL, 10) : 0;
if (n > 0) {
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
}
PQclear(res);
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
return;
}
@ -10795,6 +10786,11 @@ static void dumpDirectory(Archive* fout)
char* dirpath = NULL;
char* diracl = NULL;
if (!isExecUserSuperRole(fout)) {
write_msg(NULL, "WARNING: directory not dumped because current user is not a superuser\n");
return;
}
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
@ -21404,6 +21400,11 @@ static void dumpSynonym(Archive* fout)
PQExpBuffer q;
PQExpBuffer delq;
if (!isExecUserSuperRole(fout)) {
write_msg(NULL, "WARNING: synonym not dumped because current user is not a superuser\n");
return;
}
selectSourceSchema(fout, "pg_catalog");
query = createPQExpBuffer();
printfPQExpBuffer(query,

View File

@ -31,6 +31,9 @@
it will be backuped up in external dirs */
parray *pgdata_nobackup_dir = NULL;
/* list of logical replication slots */
parray *logical_replslot = NULL;
static int standby_message_timeout_local = 10 ; /* 10 sec = default */
static XLogRecPtr stop_backup_lsn = InvalidXLogRecPtr;
static XLogRecPtr stop_stream_lsn = InvalidXLogRecPtr;
@ -89,10 +92,11 @@ static void backup_cleanup(bool fatal, void *userdata);
static void *backup_files(void *arg);
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs);
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs,
bool backup_replslots);
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup,
PGNodeInfo *nodeInfo, PGconn *conn);
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots);
static void pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, PGNodeInfo *nodeInfo);
static int checkpoint_timeout(PGconn *backup_conn);
@ -558,7 +562,7 @@ static void sync_files(parray *database_map, const char *database_path, parray *
* Move files from 'pgdata' to a subdirectory in 'backup_path'.
*/
static void
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs)
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs, bool backup_replslots)
{
int i;
char database_path[MAXPGPATH];
@ -591,7 +595,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
securec_check_c(rc, "\0", "\0");
/* Call pg_start_backup function in openGauss connect */
pg_start_backup(label, smooth_checkpoint, &current, nodeInfo, backup_conn);
pg_start_backup(label, smooth_checkpoint, &current, nodeInfo, backup_conn, backup_replslots);
/* Obtain current timeline */
#if PG_VERSION_NUM >= 90600
@ -624,10 +628,10 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
/* list files with the logical path. omit $PGDATA */
if (fio_is_remote(FIO_DB_HOST))
fio_list_dir(backup_files_list, instance_config.pgdata,
true, true, false, backup_logs, true, 0);
true, true, false, backup_logs, true, 0, backup_replslots);
else
dir_list_file(backup_files_list, instance_config.pgdata,
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST);
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST, backup_replslots);
/*
* Get database_map (name to oid) for use in partial restore feature.
@ -749,6 +753,11 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
}
pgdata_nobackup_dir = NULL;
if (logical_replslot) {
free_dir_list(logical_replslot);
}
logical_replslot = NULL;
/* Cleanup */
if (backup_list)
{
@ -849,7 +858,7 @@ static void do_after_backup()
*/
int
do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
bool no_validate, bool no_sync, bool backup_logs)
bool no_validate, bool no_sync, bool backup_logs, bool backup_replslots)
{
PGconn *backup_conn = NULL;
PGNodeInfo nodeInfo;
@ -925,7 +934,7 @@ do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
add_note(&current, set_backup_params->note);
/* backup data */
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs);
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs, backup_replslots);
pgut_atexit_pop(backup_cleanup, NULL);
/* compute size of wal files of this backup stored in the archive */
@ -1034,13 +1043,15 @@ confirm_block_size(PGconn *conn, const char *name, int blcksz)
*/
static void
pg_start_backup(const char *label, bool smooth, pgBackup *backup,
PGNodeInfo *nodeInfo, PGconn *conn)
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots)
{
PGresult *res;
const char *params[2];
uint32 lsn_hi;
uint32 lsn_lo;
int ret;
int i;
XLogRecPtr startLsn;
params[0] = label;
@ -1068,7 +1079,33 @@ pg_start_backup(const char *label, bool smooth, pgBackup *backup,
XLogDataFromLSN(ret, PQgetvalue(res, 0, 0), &lsn_hi, &lsn_lo);
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
/* Calculate LSN */
backup->start_lsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
startLsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
if (backup_replslots) {
logical_replslot = parray_new();
/* query for logical replication slots of subscriptions */
res = pgut_execute(conn,
"SELECT slot_name, restart_lsn FROM pg_catalog.pg_get_replication_slots()"
"WHERE slot_type = 'logical' AND plugin = 'pgoutput'", 0, NULL);
if (PQntuples(res) == 0) {
elog(LOG, "logical replication slots for subscriptions not found");
} else {
XLogRecPtr repslotLsn;
for (i = 0; i < PQntuples(res); i++) {
XLogDataFromLSN(ret, PQgetvalue(res, i, 1), &lsn_hi, &lsn_lo);
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
repslotLsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
startLsn = Min(startLsn, repslotLsn);
char* slotname = pg_strdup(PQgetvalue(res, i, 0));
parray_append(logical_replslot, slotname);
}
elog(WARNING, "logical replication slots for subscriptions will be backed up. "
"If don't use them after restoring, please drop them to avoid affecting xlog recycling.");
}
}
backup->start_lsn = startLsn;
PQclear(res);
}

View File

@ -42,13 +42,6 @@ const char *pgdata_exclude_dir[] =
(const char *)"pg_stat_tmp",
(const char *)"pgsql_tmp",
/*
* It is generally not useful to backup the contents of this directory even
* if the intention is to restore to another master. See backup.sgml for a
* more detailed description.
*/
(const char *)"pg_replslot",
/* Contents removed on startup, see dsm_cleanup_for_mmap(). */
(const char *)"pg_dynshmem",
@ -68,7 +61,7 @@ const char *pgdata_exclude_dir[] =
(const char *)"pg_subtrans",
/* end of list */
NULL, /* pg_log will be set later */
NULL, /* pg_log and pg_replslot will be set later */
NULL
};
@ -128,17 +121,20 @@ may be removed int the future */
static int pgCompareString(const void *str1, const void *str2);
static char dir_check_file(pgFile *file, bool backup_logs);
static char dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots);
static char check_in_tablespace(pgFile *file, bool in_tablespace);
static char check_db_dir(pgFile *file);
static char check_digit_file(pgFile *file);
static char check_nobackup_dir(pgFile *file);
static void dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
bool exclude, bool follow_symlink, bool backup_logs,
bool skip_hidden, int external_dir_num, fio_location location);
bool skip_hidden, int external_dir_num, fio_location location,
bool backup_replslots);
static void opt_path_map(ConfigOption *opt, const char *arg,
TablespaceList *list, const char *type);
char check_logical_replslot_dir(const char *rel_path);
/* Tablespace mapping */
static TablespaceList tablespace_dirs = {NULL, NULL};
/* Extra directories mapping */
@ -538,7 +534,7 @@ db_map_entry_free(void *entry)
void
dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink,
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num,
fio_location location)
fio_location location, bool backup_replslots)
{
pgFile *file;
@ -565,7 +561,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
parray_append(files, file);
dir_list_file_internal(files, file, root, exclude, follow_symlink,
backup_logs, skip_hidden, external_dir_num, location);
backup_logs, skip_hidden, external_dir_num, location, backup_replslots);
if (!add_root)
pgFileFree(file);
@ -589,7 +585,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
* - datafiles
*/
static char
dir_check_file(pgFile *file, bool backup_logs)
dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots)
{
int i;
int sscanf_res;
@ -652,6 +648,29 @@ dir_check_file(pgFile *file, bool backup_logs)
}
}
/*
* Backup pg_replslot if it is specified.
* It is generally not useful to backup the contents of this directory even
* if the intention is to restore to another master. See backup.sgml for a
* more detailed description.
*/
if (!backup_replslots) {
if (strcmp(file->rel_path, PG_REPLSLOT_DIR) == 0) {
/* Skip */
elog(VERBOSE, "Excluding directory content: %s", file->rel_path);
return CHECK_EXCLUDE_FALSE;
}
} else {
/*
* Check file that under pg_replslot and judge whether it
* belonged to logical replication slots for subscriptions.
*/
if (strcmp(file->rel_path, PG_REPLSLOT_DIR) != 0 &&
path_is_prefix_of_path(PG_REPLSLOT_DIR, file->rel_path)) {
return check_logical_replslot_dir(file->rel_path);
}
}
ret = check_nobackup_dir(file);
if (ret != -1) { /* -1 means need backup */
return ret;
@ -749,6 +768,35 @@ static char check_nobackup_dir(pgFile *file)
return ret;
}
char check_logical_replslot_dir(const char *rel_path)
{
char ret = CHECK_FALSE;
int i = 0;
char *tmp = pg_strdup(rel_path);
char *p;
#define DIRECTORY_DELIMITER "/"
if (logical_replslot) {
/* extract slot name from rel_path, such as sub1 from pg_replslot/sub1/snap */
p = strtok(tmp, DIRECTORY_DELIMITER);
if (p != NULL) {
p = strtok(NULL, DIRECTORY_DELIMITER);
}
for (i = 0; p != NULL && i < (int)parray_num(logical_replslot); i++) {
char *slotName = (char *)parray_get(logical_replslot, i);
if (strcmp(p, slotName) == 0) {
pfree(tmp);
return CHECK_TRUE;
}
}
} else {
ret = CHECK_TRUE;
}
pfree(tmp);
return ret;
}
static char check_db_dir(pgFile *file)
{
char ret = -1;
@ -889,7 +937,8 @@ bool SkipSomeDirFile(pgFile *file, struct dirent *dent, bool skipHidden)
static void
dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
bool exclude, bool follow_symlink, bool backup_logs,
bool skip_hidden, int external_dir_num, fio_location location)
bool skip_hidden, int external_dir_num, fio_location location,
bool backup_replslots)
{
DIR *dir;
struct dirent *dent;
@ -937,7 +986,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
if (exclude)
{
check_res = dir_check_file(file, backup_logs);
check_res = dir_check_file(file, backup_logs, backup_replslots);
if (check_res == CHECK_FALSE)
{
/* Skip */
@ -963,7 +1012,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
*/
if (S_ISDIR(file->mode))
dir_list_file_internal(files, file, child, exclude, follow_symlink,
backup_logs, skip_hidden, external_dir_num, location);
backup_logs, skip_hidden, external_dir_num, location, backup_replslots);
}
if (errno && errno != ENOENT)

View File

@ -51,6 +51,7 @@ typedef struct
bool exclusive_backup;
bool skip_hidden;
int external_dir_num;
bool backup_replslots;
} fio_list_dir_request;
typedef struct
@ -1794,7 +1795,7 @@ cleanup:
/* Compile the array of files located on remote machine in directory root */
void fio_list_dir(parray *files, const char *root, bool exclude,
bool follow_symlink, bool add_root, bool backup_logs,
bool skip_hidden, int external_dir_num)
bool skip_hidden, int external_dir_num, bool backup_replslots)
{
fio_header hdr;
fio_list_dir_request req;
@ -1811,6 +1812,7 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
req.exclusive_backup = exclusive_backup;
req.skip_hidden = skip_hidden;
req.external_dir_num = external_dir_num;
req.backup_replslots = backup_replslots;
hdr.cop = FIO_LIST_DIR;
hdr.size = sizeof(req);
@ -1870,7 +1872,14 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
securec_check_ss_c(nRet, "\0", "\0");
}
/*
* Check file that under pg_replslot and judge whether it
* belonged to logical replication slots for subscriptions.
*/
if (backup_replslots && strcmp(buf, PG_REPLSLOT_DIR) != 0 &&
path_is_prefix_of_path(PG_REPLSLOT_DIR, buf) && check_logical_replslot_dir(file->rel_path) != 1) {
continue;
}
parray_append(files, file);
}
@ -1914,7 +1923,7 @@ static void fio_list_dir_impl(int out, char* buf)
dir_list_file(file_files, req->path, req->exclude, req->follow_symlink,
req->add_root, req->backup_logs, req->skip_hidden,
req->external_dir_num, FIO_LOCAL_HOST);
req->external_dir_num, FIO_LOCAL_HOST, req->backup_replslots);
/* send information about files to the main process */
for (i = 0; i < (int)parray_num(file_files); i++)

View File

@ -163,5 +163,7 @@ extern z_off_t fio_gzseek(gzFile f, z_off_t offset, int whence);
extern const char* fio_gzerror(gzFile file, int *errnum);
#endif
extern char check_logical_replslot_dir(const char *rel_path);
#endif

View File

@ -154,6 +154,7 @@ void help_pg_probackup(void)
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
printf(_(" [--remote-libpath=libpath]\n"));
printf(_(" [--ttl=interval] [--expire-time=time]\n"));
printf(_(" [--backup-pg-replslot]\n"));
printf(_(" [--help]\n"));
printf(_("\n %s restore -B backup-path --instance=instance_name\n"), PROGRAM_NAME);
@ -420,6 +421,7 @@ static void help_backup(void)
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
printf(_(" [--remote-libpath=libpath]\n"));
printf(_(" [--ttl=interval] [--expire-time=time]\n\n"));
printf(_(" [--backup-pg-replslot]\n"));
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" --instance=instance_name name of the instance\n"));
@ -441,6 +443,7 @@ static void help_backup(void)
printf(_(" --note=text add note to backup\n"));
printf(_(" (example: --note='backup before app update to v13.1')\n"));
printf(_(" --archive-timeout=timeout wait timeout for WAL segment archiving (default: 5min)\n"));
printf(_(" --backup-pg-replslot] backup of '%s' directory\n"), PG_REPLSLOT_DIR);
printf(_("\n Logging options:\n"));
printf(_(" --log-level-console=log-level-console\n"));

View File

@ -77,6 +77,7 @@ int rw_timeout = 0;
/* backup options */
bool backup_logs = false;
bool backup_replslots = false;
bool smooth_checkpoint;
char *remote_agent;
static char *backup_note = NULL;
@ -186,6 +187,7 @@ static ConfigOption cmd_options[] =
{ 'b', 145, "wal", &delete_wal, SOURCE_CMD_STRICT },
{ 'b', 146, "expired", &delete_expired, SOURCE_CMD_STRICT },
{ 's', 172, "status", &delete_status, SOURCE_CMD_STRICT },
{ 'b', 186, "backup-pg-replslot", &backup_replslots, SOURCE_CMD_STRICT},
{ 'b', 147, "force", &force, SOURCE_CMD_STRICT },
{ 'b', 148, "compress", &compress_shortcut, SOURCE_CMD_STRICT },
@ -550,7 +552,7 @@ static int do_actual_operate()
elog(ERROR, "required parameter not specified: BACKUP_MODE "
"(-b, --backup-mode)");
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs);
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs, backup_replslots);
}
case RESTORE_CMD:
return do_restore_or_validate(current.backup_id,

View File

@ -69,6 +69,7 @@ extern const char *PROGRAM_FULL_PATH;
#define HEADER_MAP "page_header_map"
#define HEADER_MAP_TMP "page_header_map_tmp"
#define PG_RELATIVE_TBLSPC_DIR "pg_location"
#define PG_REPLSLOT_DIR "pg_replslot"
/* Timeout defaults */
#define ARCHIVE_TIMEOUT_DEFAULT 300

View File

@ -54,6 +54,9 @@ extern bool smooth_checkpoint;
it will be backuped up in external dirs */
extern parray *pgdata_nobackup_dir;
/* list of logical replication slots */
extern parray *logical_replslot;
/* remote probackup options */
extern char* remote_agent;
@ -89,7 +92,7 @@ extern const char *pgdata_exclude_dir[];
/* in backup.c */
extern int do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
bool no_validate, bool no_sync, bool backup_logs);
bool no_validate, bool no_sync, bool backup_logs, bool backup_replslots);
extern BackupMode parse_backup_mode(const char *value);
extern const char *deparse_backup_mode(BackupMode mode);
extern void process_block_change(ForkNumber forknum, const RelFileNode rnode,
@ -239,7 +242,8 @@ extern const char* deparse_compress_alg(int alg);
/* in dir.c */
extern void dir_list_file(parray *files, const char *root, bool exclude,
bool follow_symlink, bool add_root, bool backup_logs,
bool skip_hidden, int external_dir_num, fio_location location);
bool skip_hidden, int external_dir_num, fio_location location,
bool backup_replslots = false);
extern void create_data_directories(parray *dest_files,
const char *data_dir,
@ -432,7 +436,8 @@ extern int fio_send_file(const char *from_fullpath, const char *to_fullpath, FIL
pgFile *file, char **errormsg);
extern void fio_list_dir(parray *files, const char *root, bool exclude, bool follow_symlink,
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num);
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num,
bool backup_replslots = false);
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);

View File

@ -8641,7 +8641,7 @@
),
AddFuncGroup(
"pg_stat_get_wal_senders", 1,
AddBuiltinFunc(_0(3099), _1("pg_stat_get_wal_senders"), _2(0), _3(false), _4(true), _5(pg_stat_get_wal_senders), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(22, 20, 23, 25, 25, 25, 25, 1184, 1184, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 23, 25, 25), _22(22, 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(22, "pid", "sender_pid", "local_role", "peer_role", "peer_state", "state", "catchup_start", "catchup_end", "sender_sent_location", "sender_write_location", "sender_flush_location", "sender_replay_location", "receiver_received_location", "receiver_write_location", "receiver_flush_location", "receiver_replay_location", "sync_percent", "sync_state", "sync_group", "sync_priority", "sync_most_available", "channel"), _24(NULL), _25("pg_stat_get_wal_senders"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("statistics: information about currently active replication"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0))
AddBuiltinFunc(_0(3099), _1("pg_stat_get_wal_senders"), _2(0), _3(false), _4(true), _5(pg_stat_get_wal_senders), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(21, 20, 23, 25, 25, 25, 25, 1184, 1184, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 25, 25), _22(21, 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(21, "pid", "sender_pid", "local_role", "peer_role", "peer_state", "state", "catchup_start", "catchup_end", "sender_sent_location", "sender_write_location", "sender_flush_location", "sender_replay_location", "receiver_received_location", "receiver_write_location", "receiver_flush_location", "receiver_replay_location", "sync_percent", "sync_state", "sync_priority", "sync_most_available", "channel"), _24(NULL), _25("pg_stat_get_wal_senders"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("statistics: information about currently active replication"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0))
),
AddFuncGroup(
"pg_stat_get_wlm_ec_operator_info", 1,

View File

@ -6230,7 +6230,7 @@ Datum GetPartBoundaryByTuple(Relation rel, HeapTuple tuple)
return Timestamp2Boundarys(rel, Align2UpBoundary(value, partMap->intervalValue, boundaryTs));
}
Oid AddNewIntervalPartition(Relation rel, void* insertTuple)
Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL)
{
Relation pgPartRel = NULL;
Oid newPartOid = InvalidOid;
@ -6327,7 +6327,13 @@ Oid AddNewIntervalPartition(Relation rel, void* insertTuple)
*/
CommandCounterIncrement();
UpdatePgObjectChangecsn(RelationGetRelid(rel), rel->rd_rel->relkind);
/*
* If add interval partition in the DDL, do not need to change the csn
* because the scn has been changed in the DDL.
*/
if (!isDDL) {
UpdatePgObjectChangecsn(RelationGetRelid(rel), rel->rd_rel->relkind);
}
return newPartOid;
}
@ -7113,7 +7119,7 @@ int lookupHBucketid(oidvector *buckets, int low, int2 bktId)
* Description :
* Notes :
*/
Oid heapTupleGetPartitionId(Relation rel, void *tuple)
Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL)
{
Oid partitionid = InvalidOid;
@ -7140,7 +7146,7 @@ Oid heapTupleGetPartitionId(Relation rel, void *tuple)
(errcode(ERRCODE_NO_DATA_FOUND), errmsg("inserted partition key does not map to any table partition")));
} break;
case PART_AREA_INTERVAL: {
return AddNewIntervalPartition(rel, tuple);
return AddNewIntervalPartition(rel, tuple, isDDL);
} break;
case PART_AREA_LIST: {
ereport(ERROR,

View File

@ -47,7 +47,8 @@ static void InternalAggIsSupported(const char *aggName)
"json_agg",
"json_object_agg",
"st_summarystatsagg",
"st_union"
"st_union",
"wm_concat"
};
uint len = lengthof(supportList);

View File

@ -52,7 +52,7 @@ static_assert(sizeof(false) == sizeof(char), "illegal bool size");
static struct HTAB* nameHash = NULL;
static struct HTAB* oidHash = NULL;
/* for b_sql_plugin */
/* for dolphin */
struct HTAB* b_nameHash = NULL;
struct HTAB* b_oidHash = NULL;
@ -118,7 +118,7 @@ static const FuncGroup* NameHashTableAccess(HASHACTION action, const char* name,
Assert(name != NULL);
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.b_sql_plugin) {
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.dolphin) {
result = (HashEntryNameToFuncGroup *)hash_search(b_nameHash, &temp_name, action, &found);
} else {
result = (HashEntryNameToFuncGroup *)hash_search(nameHash, &temp_name, action, &found);
@ -144,7 +144,7 @@ static const Builtin_func* OidHashTableAccess(HASHACTION action, Oid oid, const
bool found = false;
Assert(oid > 0);
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.b_sql_plugin) {
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.dolphin) {
result = (HashEntryOidToBuiltinFunc *)hash_search(b_oidHash, &oid, action, &found);
} else {
result = (HashEntryOidToBuiltinFunc *)hash_search(oidHash, &oid, action, &found);

View File

@ -68,7 +68,7 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
/* Get slotname */
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subslotname, &isnull);
if (unlikely(isnull)) {
if (!isnull) {
sub->slotname = pstrdup(NameStr(*DatumGetName(datum)));
} else {
sub->slotname = NULL;
@ -92,10 +92,10 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subbinary, &isnull);
if (unlikely(isnull)) {
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("null binary for subscription %u", subid)));
sub->binary = false;
} else {
sub->binary = DatumGetBool(datum);
}
sub->binary = DatumGetBool(datum);
ReleaseSysCache(tup);

View File

@ -309,6 +309,7 @@ bool pg_md5_encrypt(const char* passwd, const char* salt, size_t salt_len, char*
{
size_t passwd_len = strlen(passwd);
errno_t rc = EOK;
/* the length of salt and password is <= SIZE_MAX */
#ifndef WIN32
if (unlikely(passwd_len >= SIZE_MAX - salt_len)) {
return false;
@ -322,6 +323,7 @@ bool pg_md5_encrypt(const char* passwd, const char* salt, size_t salt_len, char*
char* crypt_buf = (char*)malloc(passwd_len + salt_len + 1);
bool ret = false;
/* the buffer is not exist */
if (crypt_buf == NULL)
return false;

View File

@ -772,6 +772,15 @@ bool pg_sha256_encrypt_for_md5(const char* password, const char* salt, size_t sa
return true;
}
/*
* @Description: calculate the encrypted password for GsSm3.
* @const char* password : the password need be encrypted.
* @const char* salt_s : the content fo the slat.
* @size_t salt_len : the length fo the slat.
* @char* buf : the buffer to store the encrypted key with GsSm3.
* @char* client_key_buf : the buffer to store the key of client.
* @int iteration_count : to record the number of the iteration.
*/
bool GsSm3Encrypt(
const char* password, const char* salt_s, size_t salt_len, char* buf, char* client_key_buf, int iteration_count)
{
@ -799,6 +808,7 @@ bool GsSm3Encrypt(
}
password_len = strlen(password);
/* Tranform string(64Bytes) to binary(32Bytes) */
sha_hex_to_bytes32(salt, (char*)salt_s);
/* calculate k */
pkcs_ret = PKCS5_PBKDF2_HMAC((char*)password,

View File

@ -70,6 +70,7 @@
THR_LOCAL bool skip_read_extern_fields = false;
#define IS_DATANODE_BUT_NOT_SINGLENODE (IS_PGXC_DATANODE && !IS_SINGLE_NODE)
/*
* Macros to simplify reading of different kinds of fields. Use these
* wherever possible to reduce the chance for silly typos. Note that these
@ -401,24 +402,27 @@ THR_LOCAL bool skip_read_extern_fields = false;
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = _readBitmapset()
#define READ_TYPEINFO_FIELD(fldname) \
do { \
if (local_node->fldname >= FirstBootstrapObjectId) { \
IF_EXIST(exprtypename) \
{ \
char* exprtypename = NULL; \
char* exprtypenamespace = NULL; \
token = pg_strtok(&length); \
token = pg_strtok(&length); \
exprtypename = nullable_string(token, length); \
token = pg_strtok(&length); \
token = pg_strtok(&length); \
exprtypenamespace = nullable_string(token, length); \
local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \
pfree_ext(exprtypename); \
pfree_ext(exprtypenamespace); \
} \
} \
#define READ_TYPEINFO_FIELD(fldname) \
do { \
if (local_node->fldname >= FirstBootstrapObjectId) { \
IF_EXIST(exprtypename) \
{ \
char* exprtypename = NULL; \
char* exprtypenamespace = NULL; \
token = pg_strtok(&length); \
token = pg_strtok(&length); \
exprtypename = nullable_string(token, length); \
token = pg_strtok(&length); \
token = pg_strtok(&length); \
exprtypenamespace = nullable_string(token, length); \
/* No need to reset field on CN or singlenode, keep pg_strtok() for forward compatibility */ \
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \
} \
pfree_ext(exprtypename); \
pfree_ext(exprtypenamespace); \
} \
} \
} while (0)
#define READ_TYPEINFO(typePtr) \
@ -493,9 +497,30 @@ THR_LOCAL bool skip_read_extern_fields = false;
token = pg_strtok(&length); \
token = pg_strtok(&length); \
funcnamespace = nullable_string(token, length); \
if (IS_PGXC_DATANODE && !skip_read_extern_fields) { \
local_node->fldname = \
get_func_oid(funcname, get_namespace_oid(funcnamespace, false), (Expr*)local_node); \
bool notfound = false; \
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) { \
Oid funcoid = InvalidOid; \
do { \
Oid nspid = get_namespace_oid(funcnamespace, true); \
if (!OidIsValid(nspid)) { \
notfound = true; \
break; \
} \
funcoid = get_func_oid(funcname, nspid, (Expr*)local_node); \
} while (0); \
if (notfound || !OidIsValid(funcoid)) { \
ereport(ERROR, \
(errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_OBJECT), \
errmsg("Cannot identify function %s.%s while deserializing field.", \
funcname, funcnamespace), \
errdetail("Function with oid %u or its namespace may be renamed", \
local_node->fldname), \
errhint("Please rebuild column defalt expression, views etc. that are" \
" related to this renamed object."), \
errcause("Object renamed after recorded as nodetree."), \
erraction("Rebuild relevant object."))); \
} \
local_node->fldname = funcoid; \
} \
pfree_ext(funcname); \
pfree_ext(funcnamespace); \
@ -525,7 +550,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
token = pg_strtok(&length); \
token = pg_strtok(&length); \
oprrightname = nullable_string(token, length); \
if (IS_PGXC_DATANODE) { \
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
namespaceId = get_namespace_oid(opnamespace, false); \
oprleft = get_typeoid(namespaceId, oprleftname); \
oprright = oprleft; \
@ -568,7 +593,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
token = pg_strtok(&length); \
token = pg_strtok(&length); \
oprrightname = nullable_string(token, length); \
if (IS_PGXC_DATANODE) { \
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
namespaceId = get_namespace_oid(opnamespace, false); \
oprleft = get_typeoid(namespaceId, oprleftname); \
oprright = oprleft; \
@ -2126,14 +2151,21 @@ static FuncExpr* _readFuncExpr(void)
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("NULL seqNamespace for nextval()")));
}
if (!IS_PGXC_COORDINATOR && !skip_read_extern_fields) {
Oid seqid = get_valid_relname_relid(seqNamespace, seqName);
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) {
Oid seqid = get_valid_relname_relid(seqNamespace, seqName, true);
Const* firstArg = (Const*)linitial(local_node->args);
if (OidIsValid(seqid)) {
Const* firstArg = (Const*)linitial(local_node->args);
if (firstArg != NULL) {
firstArg->constvalue = ObjectIdGetDatum(seqid);
}
} else {
ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("Cannot identify sequence %s.%s while deserializing field.", seqNamespace, seqName),
errdetail("Sequence with oid %u or its namespace may be renamed",
DatumGetObjectId(firstArg->constvalue)),
errhint("Please rebuild column defalt expression, views etc. that are related to this sequence"),
errcause("Object renamed after recorded as nodetree."), erraction("Rebuild relevant object.")));
}
}
pfree_ext(seqName);

View File

@ -0,0 +1,33 @@
analyze.cpp
CMakeLists.txt
gram.xml
gram.y
hint_gram.y
hint_scan.l
keywords.cpp
kwlookup.cpp
LIST.TXT
Makefile
parser.cpp
parse_agg.cpp
parse_clause.cpp
parse_coerce.cpp
parse_collate.cpp
parse_compatibility.cpp
parse_cte.cpp
parse_expr.cpp
parse_func.cpp
parse_hint.cpp
parse_merge.cpp
parse_node.cpp
parse_oper.cpp
parse_param.cpp
parse_relation.cpp
parse_startwith.cpp
parse_target.cpp
parse_type.cpp
parse_utilcmd.cpp
README
scan.l
scansup.cpp
新建文本文档.bat

File diff suppressed because it is too large Load Diff

View File

@ -1454,7 +1454,14 @@ FuncCandidateList sort_candidate_func_list(FuncCandidateList oldCandidates)
}
candidates[smallestIndex] = NULL;
}
for (int i = 0; i < size; i++) {
if (candidates[i] != NULL) {
lastCandidate->next = candidates[i];
lastCandidate = lastCandidate->next;
}
}
lastCandidate->next = NULL;
pfree(candidates);
return sortedCandidates;
}

View File

@ -0,0 +1 @@
DIR *.* /B >LIST.TXT

View File

@ -61,7 +61,7 @@ void GlobalBaseDefCache::RemoveElemFromBucket(GlobalBaseEntry *base)
if (is_relation) {
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
uint64 rel_size = GetRelEstimateSize(entry);
pg_atomic_fetch_sub_u64(&m_base_space, rel_size);
pg_atomic_fetch_sub_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
m_db_entry->MemoryEstimateSub(rel_size);
} else {
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
@ -77,7 +77,7 @@ void GlobalBaseDefCache::AddHeadToBucket(Index hash_index, GlobalBaseEntry *base
if (is_relation) {
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
uint64 rel_size = GetRelEstimateSize(entry);
pg_atomic_fetch_add_u64(&m_base_space, rel_size);
pg_atomic_fetch_add_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
m_db_entry->MemoryEstimateAdd(rel_size);
} else {
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
@ -400,4 +400,4 @@ GlobalBaseDefCache::GlobalBaseDefCache(Oid db_oid, bool is_shared, GlobalSysDBCa
m_base_space = 0;
m_obj_locks = NULL;
m_db_entry = entry;
}
}

View File

@ -659,7 +659,27 @@ void GlobalSysDBCache::InitSysCacheRelIds()
*/
void GlobalSysDBCache::RefreshHotStandby()
{
if (!EnableGlobalSysCache()) {
return;
}
hot_standby = (t_thrd.postmaster_cxt.HaShmData->current_mode != STANDBY_MODE || XLogStandbyInfoActive());
if (hot_standby || !m_is_inited) {
return;
}
/* clean all */
for (int hash_index = 0; hash_index < m_nbuckets; hash_index ++) {
PthreadRWlockRdlock(LOCAL_SYSDB_RESOWNER, &m_db_locks[hash_index]);
for (Dlelem * elt = DLGetTail(m_bucket_list.GetBucket(hash_index)); elt != NULL;) {
GlobalSysDBCacheEntry *entry = (GlobalSysDBCacheEntry *)DLE_VAL(elt);
elt = DLGetPred(elt);
entry->ResetDBCache<true>();
}
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, &m_db_locks[hash_index]);
}
if (m_global_shared_db_entry != NULL) {
m_global_shared_db_entry->ResetDBCache<true>();
}
}
void GlobalSysDBCache::Init(MemoryContext parent)
@ -1272,9 +1292,18 @@ int ResizeHashBucket(int origin_nbucket, DynamicHashBucketStrategy strategy)
return cc_nbuckets;
}
void NotifyGscRecoveryStarted()
{
if (!EnableGlobalSysCache()) {
return;
}
g_instance.global_sysdbcache.recovery_finished = false;
}
void NotifyGscRecoveryFinished()
{
if (EnableGlobalSysCache()) {
g_instance.global_sysdbcache.recovery_finished = true;
}
}
}

View File

@ -183,7 +183,7 @@ void GlobalSysTabCache::InvalidTuples(int cache_id, uint32 hash_value, bool rese
/* maybe upgrade from version before v5r2c00, the cacheid is out of order
* whatever, we cache nothing except relmap, so just ignore the catcache invalmsg */
if (unlikely(!g_instance.global_sysdbcache.recovery_finished) && m_global_systupcaches[cache_id] == NULL) {
if (unlikely(!g_instance.global_sysdbcache.recovery_finished && m_global_systupcaches[cache_id] == NULL)) {
return;
}

View File

@ -74,7 +74,7 @@ Partition LocalPartDefCache::SearchPartitionFromGlobalCopy(Oid part_oid)
if (!g_instance.global_sysdbcache.hot_standby) {
return NULL;
}
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
if (unlikely(!IsPrimaryRecoveryFinished())) {
return NULL;
}
uint32 hash_value = oid_hash((void *)&(part_oid), sizeof(Oid));
@ -165,7 +165,7 @@ static bool IsPartOidStoreInGlobal(Oid part_oid)
if (!g_instance.global_sysdbcache.hot_standby) {
return false;
}
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
if (unlikely(!IsPrimaryRecoveryFinished())) {
return false;
}
if (g_instance.global_sysdbcache.StopInsertGSC()) {
@ -456,4 +456,4 @@ Partition LocalPartDefCache::PartitionIdGetPartition(Oid part_oid, StorageType s
}
return pd;
}
}

View File

@ -433,7 +433,7 @@ LocalCatCTup *LocalSysTupCache::SearchTupleFromGlobal(Datum *arguments, uint32 h
bool bypass_gsc = HistoricSnapshotActive() ||
m_global_systupcache->enable_rls ||
!g_instance.global_sysdbcache.hot_standby ||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
unlikely(!IsPrimaryRecoveryFinished());
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
global_ct = m_global_systupcache->SearchTupleFromFile(hash_value, arguments, true);
} else {
@ -585,7 +585,7 @@ LocalCatCList *LocalSysTupCache::SearchListFromGlobal(int nkeys, Datum *argument
bool bypass_gsc = HistoricSnapshotActive() ||
m_global_systupcache->enable_rls ||
!g_instance.global_sysdbcache.hot_standby ||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
unlikely(!IsPrimaryRecoveryFinished());
GlobalCatCList *global_cl;
if (invalid_entries.ExistList() || bypass_gsc) {
global_cl = m_global_systupcache->SearchListFromFile(hash_value, nkeys, arguments, true);
@ -703,7 +703,7 @@ LocalCatCTup *LocalSysTupCache::SearchTupleFromGlobalForProcAllArgs(
bool bypass_gsc = HistoricSnapshotActive() ||
m_global_systupcache->enable_rls ||
!g_instance.global_sysdbcache.hot_standby ||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
unlikely(!IsPrimaryRecoveryFinished());
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
global_ct = m_global_systupcache->SearchTupleFromFileWithArgModes(hash_value, arguments, argModes, true);
} else {

View File

@ -93,7 +93,7 @@ Relation LocalTabDefCache::SearchRelationFromGlobalCopy(Oid rel_oid)
if (!g_instance.global_sysdbcache.hot_standby) {
return NULL;
}
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
if (unlikely(!IsPrimaryRecoveryFinished())) {
return NULL;
}
uint32 hash_value = oid_hash((void *)&(rel_oid), sizeof(Oid));
@ -190,7 +190,7 @@ static bool IsRelOidStoreInGlobal(Oid rel_oid)
if (!g_instance.global_sysdbcache.hot_standby) {
return false;
}
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
if (unlikely(!IsPrimaryRecoveryFinished())) {
return false;
}
if (g_instance.global_sysdbcache.StopInsertGSC()) {
@ -1137,4 +1137,4 @@ void LocalTabDefCache::ResetInitFlag()
m_is_inited_phase3 = false;
m_db_id = InvalidOid;
}
}

View File

@ -1723,7 +1723,7 @@ char* get_relname_relid_extend(
extern bool StreamTopConsumerAmI();
/* same as get_relname_relid except we check for cache invalidation here */
Oid get_valid_relname_relid(const char* relnamespace, const char* relname)
Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok)
{
Oid nspid = InvalidOid;
Oid oldnspid = InvalidOid;
@ -1747,7 +1747,10 @@ Oid get_valid_relname_relid(const char* relnamespace, const char* relname)
if (EnableLocalSysCache()) {
thrd_inval_count = t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter;
}
nspid = get_namespace_oid(relnamespace, false);
nspid = get_namespace_oid(relnamespace, nsp_missing_ok);
if (!OidIsValid(nspid)) {
return InvalidOid;
}
relid = get_relname_relid(relname, nspid);
/*
* In bootstrap processing mode, we don't bother with locking

View File

@ -59,7 +59,7 @@ bool open_join_children = true;
bool will_shutdown = false;
/* hard-wired binary version number */
const uint32 GRAND_VERSION_NUM = 92607;
const uint32 GRAND_VERSION_NUM = 92606;
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
@ -101,7 +101,7 @@ const uint32 PRIVS_DIRECTORY_VERSION_NUM = 92460;
const uint32 COMMENT_RECORD_PARAM_VERSION_NUM = 92484;
const uint32 SCAN_BATCH_MODE_VERSION_NUM = 92568;
const uint32 PUBLICATION_VERSION_NUM = 92580;
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92607;
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
/* Version number of the guc parameter backend_version added in V500R001C20 */
const uint32 V5R1C20_BACKEND_VERSION_NUM = 92305;

View File

@ -65,7 +65,7 @@
#ifdef ENABLE_MULTIPLE_NODES
#include "tsdb/compaction/compaction_entry.h"
#endif /* ENABLE_MULTIPLE_NODES */
#endif /* ENABLE_MULTIPLE_NODES */
#include "access/ustore/knl_undoworker.h"
#define DIRECTORY_LOCK_FILE "postmaster.pid"
@ -84,47 +84,31 @@ Alarm alarmItemTooManyDbUserConn[1] = {ALM_AI_Unknown, ALM_AS_Normal, 0, 0, 0, 0
* ----------------------------------------------------------------
*/
void ReportAlarmTooManyDbUserConn(const char* roleName)
void ReportAlarmTooManyDbUserConn(const char *roleName)
{
AlarmAdditionalParam tempAdditionalParam;
// Initialize the alarm item
AlarmItemInitialize(alarmItemTooManyDbUserConn,
ALM_AI_TooManyDbUserConn,
alarmItemTooManyDbUserConn->stat,
NULL,
alarmItemTooManyDbUserConn->lastReportTime,
alarmItemTooManyDbUserConn->reportCount);
AlarmItemInitialize(alarmItemTooManyDbUserConn, ALM_AI_TooManyDbUserConn, alarmItemTooManyDbUserConn->stat, NULL,
alarmItemTooManyDbUserConn->lastReportTime, alarmItemTooManyDbUserConn->reportCount);
// fill the alarm message
WriteAlarmAdditionalInfo(&tempAdditionalParam,
g_instance.attr.attr_common.PGXCNodeName,
"AllDatabases",
const_cast<char*>(roleName),
alarmItemTooManyDbUserConn,
ALM_AT_Fault,
const_cast<char*>(roleName));
WriteAlarmAdditionalInfo(&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "AllDatabases",
const_cast<char *>(roleName), alarmItemTooManyDbUserConn, ALM_AT_Fault,
const_cast<char *>(roleName));
// report the alarm
AlarmReporter(alarmItemTooManyDbUserConn, ALM_AT_Fault, &tempAdditionalParam);
}
void ReportResumeTooManyDbUserConn(const char* roleName)
void ReportResumeTooManyDbUserConn(const char *roleName)
{
AlarmAdditionalParam tempAdditionalParam;
// Initialize the alarm item
AlarmItemInitialize(alarmItemTooManyDbUserConn,
ALM_AI_TooManyDbUserConn,
alarmItemTooManyDbUserConn->stat,
NULL,
alarmItemTooManyDbUserConn->lastReportTime,
alarmItemTooManyDbUserConn->reportCount);
AlarmItemInitialize(alarmItemTooManyDbUserConn, ALM_AI_TooManyDbUserConn, alarmItemTooManyDbUserConn->stat, NULL,
alarmItemTooManyDbUserConn->lastReportTime, alarmItemTooManyDbUserConn->reportCount);
// fill the resume message
WriteAlarmAdditionalInfo(&tempAdditionalParam,
g_instance.attr.attr_common.PGXCNodeName,
"AllDatabases",
const_cast<char*>(roleName),
alarmItemTooManyDbUserConn,
ALM_AT_Resume);
WriteAlarmAdditionalInfo(&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "AllDatabases",
const_cast<char *>(roleName), alarmItemTooManyDbUserConn, ALM_AT_Resume);
// report the alarm
AlarmReporter(alarmItemTooManyDbUserConn, ALM_AT_Resume, &tempAdditionalParam);
}
@ -137,13 +121,8 @@ void ReportAlarmDataInstLockFileExist()
// Initialize the alarm item
AlarmItemInitialize(alarmItem, ALM_AI_DataInstLockFileExist, ALM_AS_Reported, NULL);
// fill the alarm message
WriteAlarmAdditionalInfo(&tempAdditionalParam,
g_instance.attr.attr_common.PGXCNodeName,
"",
"",
alarmItem,
ALM_AT_Fault,
g_instance.attr.attr_common.PGXCNodeName);
WriteAlarmAdditionalInfo(&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "", "", alarmItem,
ALM_AT_Fault, g_instance.attr.attr_common.PGXCNodeName);
// report the alarm
AlarmReporter(alarmItem, ALM_AT_Fault, &tempAdditionalParam);
}
@ -156,8 +135,8 @@ void ReportResumeDataInstLockFileExist()
// Initialize the alarm item
AlarmItemInitialize(alarmItem, ALM_AI_DataInstLockFileExist, ALM_AS_Normal, NULL);
// fill the alarm message
WriteAlarmAdditionalInfo(
&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "", "", alarmItem, ALM_AT_Resume);
WriteAlarmAdditionalInfo(&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "", "", alarmItem,
ALM_AT_Resume);
// report the alarm
AlarmReporter(alarmItem, ALM_AT_Resume, &tempAdditionalParam);
}
@ -167,29 +146,28 @@ void ReportResumeDataInstLockFileExist()
* ----------------------------------------------------------------
*/
void SetDatabasePath(const char* path)
void SetDatabasePath(const char *path)
{
/* This should happen only once per process */
Assert(!u_sess->proc_cxt.DatabasePath);
u_sess->proc_cxt.DatabasePath =
MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), path);
u_sess->proc_cxt.DatabasePath = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), path);
}
/*
* Set data directory, but make sure it's an absolute path. Use this,
* never set t_thrd.proc_cxt.DataDir directly.
*/
void SetDataDir(const char* dir)
void SetDataDir(const char *dir)
{
AssertArg(dir);
/* If presented path is relative, convert to absolute */
char* newm = make_absolute_path(dir);
char *newm = make_absolute_path(dir);
char real_newm[PATH_MAX + 1] = {'\0'};
char* DataDir = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), MAXPGPATH);
char *DataDir = (char *)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), MAXPGPATH);
if (realpath(newm, real_newm) == NULL) {
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED),errmsg("invalid path:%s", dir)));
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED), errmsg("invalid path:%s", dir)));
}
errno_t rc = strncpy_s(DataDir, MAXPGPATH, real_newm, MAXPGPATH - 1);
securec_check(rc, "\0", "\0");
@ -217,8 +195,8 @@ void ChangeToDataDir(void)
AssertState(t_thrd.proc_cxt.DataDir);
if (chdir(t_thrd.proc_cxt.DataDir) < 0)
ereport(FATAL,
(errcode_for_file_access(), errmsg("could not change directory to \"%s\": %m", t_thrd.proc_cxt.DataDir)));
ereport(FATAL, (errcode_for_file_access(),
errmsg("could not change directory to \"%s\": %m", t_thrd.proc_cxt.DataDir)));
}
/*
@ -231,9 +209,9 @@ void ChangeToDataDir(void)
* should happen before doing ChangeToDataDir(), else the user will probably
* not like the results.
*/
char* make_absolute_path(const char* path)
char *make_absolute_path(const char *path)
{
char* newm = NULL;
char *newm = NULL;
size_t tmplen;
/* Returning null for null input is convenient for some callers */
@ -242,16 +220,16 @@ char* make_absolute_path(const char* path)
}
if (!is_absolute_path(path)) {
char* buf = NULL;
char *buf = NULL;
size_t buflen;
buflen = MAXPGPATH;
for (;;) {
#ifdef FRONTEND
buf = (char*)malloc(buflen);
buf = (char *)malloc(buflen);
#else
buf = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), buflen);
buf = (char *)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), buflen);
#endif
if (buf == NULL)
@ -259,8 +237,7 @@ char* make_absolute_path(const char* path)
if (getcwd(buf, buflen) != NULL) {
break;
}
else if (errno == ERANGE) {
} else if (errno == ERANGE) {
#ifdef FRONTEND
free(buf);
#else
@ -280,9 +257,9 @@ char* make_absolute_path(const char* path)
tmplen = strlen(buf) + strlen(path) + 2;
#ifdef FRONTEND
newm = (char*)malloc(tmplen);
newm = (char *)malloc(tmplen);
#else
newm = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), tmplen);
newm = (char *)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), tmplen);
#endif
if (newm == NULL)
@ -321,12 +298,16 @@ Oid GetAuthenticatedUserId(void)
*
* Note: there's no SetUserId() anymore; use SetUserIdAndSecContext().
*/
/*
* Oid便
*/
Oid GetUserId(void)
{
// 检查当前用户标识符是否有效
if (!OidIsValid(u_sess->misc_cxt.CurrentUserId)) {
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
errmsg("Current user id is invalid. Please try later.")));
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Current user id is invalid. Please try later.")));
}
// 返回当前用户标识符
return u_sess->misc_cxt.CurrentUserId;
}
@ -438,7 +419,7 @@ bool exist_logic_cluster()
* show_nodegroup_mode - return node group mode as sting.
* The function is only used in guc.cpp.
*/
const char* show_nodegroup_mode(void)
const char *show_nodegroup_mode(void)
{
modify_nodegroup_mode();
@ -484,7 +465,7 @@ const int GetCustomParserId()
* get_current_lcgroup_name - get current logic group name.
* The function return NULL in datanode because datanode don't see pgxc_group.
*/
const char* get_current_lcgroup_name()
const char *get_current_lcgroup_name()
{
if (IS_PGXC_COORDINATOR && u_sess->attr.attr_common.current_logic_cluster_name == NULL &&
OidIsValid(u_sess->misc_cxt.current_logic_cluster) && t_thrd.proc_cxt.postgres_initialized) {
@ -493,8 +474,8 @@ const char* get_current_lcgroup_name()
if (HeapTupleIsValid(groupTup)) {
rform = (Form_pgxc_group)GETSTRUCT(groupTup);
u_sess->attr.attr_common.current_logic_cluster_name = MemoryContextStrdup(
SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), NameStr(rform->group_name));
u_sess->attr.attr_common.current_logic_cluster_name =
MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), NameStr(rform->group_name));
ReleaseSysCache(groupTup);
}
}
@ -521,9 +502,9 @@ static void set_current_lcgroup_oid(Oid group_oid)
* show_show_lcgroup_name - show current logic group name.
* The function is only used in guc.cpp.
*/
const char* show_lcgroup_name()
const char *show_lcgroup_name()
{
const char* name = get_current_lcgroup_name();
const char *name = get_current_lcgroup_name();
return (name == NULL) ? "" : name;
}
@ -614,7 +595,7 @@ Oid get_pgxc_logic_groupoid(Oid roleid)
* Obtain PGXC Logic Group Oid for rolename
* Return Invalid Oid if group does not exist
*/
Oid get_pgxc_logic_groupoid(const char* rolename)
Oid get_pgxc_logic_groupoid(const char *rolename)
{
bool isNull = false;
Datum aclDatum;
@ -693,7 +674,7 @@ static void RegisterNodeGroupCacheCallback()
* and perhaps restored is indeed invalid. We have to be able to get
* through AbortTransaction without asserting in case InitPostgres fails.
*/
void GetUserIdAndSecContext(Oid* userid, int* sec_context)
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
{
*userid = u_sess->misc_cxt.CurrentUserId;
*sec_context = u_sess->misc_cxt.SecurityRestrictionContext;
@ -727,7 +708,7 @@ bool InSecurityRestrictedOperation(void)
* pljava. We allow the userid to be set, but only when not inside a
* security restriction context.
*/
void GetUserIdAndContext(Oid* userid, bool* sec_def_context)
void GetUserIdAndContext(Oid *userid, bool *sec_def_context)
{
*userid = u_sess->misc_cxt.CurrentUserId;
*sec_def_context = InLocalUserIdChange();
@ -737,9 +718,8 @@ void SetUserIdAndContext(Oid userid, bool sec_def_context)
{
/* We throw the same error SET ROLE would. */
if (InSecurityRestrictedOperation())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot set parameter \"%s\" within security-restricted operation", "role")));
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot set parameter \"%s\" within security-restricted operation", "role")));
u_sess->misc_cxt.CurrentUserId = userid;
@ -800,7 +780,7 @@ static void DecreaseUserCountReuse(Oid roleid, bool ispoolerreuse)
/*
* Initialize user identity during normal backend startup
*/
void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid useroid)
void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid useroid)
{
HeapTuple roleTup;
Form_pg_authid rform;
@ -813,8 +793,7 @@ void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid usero
* exist yet, and they should be owned by openGauss anyway.
*/
if (IsBootstrapProcessingMode()) {
ereport(
ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("IsBootstrapProcessingMode")));
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("IsBootstrapProcessingMode")));
}
/* In pooler stateless reuse mode, to reset session userid */
@ -824,10 +803,10 @@ void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid usero
if (!isUserOidInvalid) {
AssertState(false);
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Abnormal process. UserOid has been reseted. Current userOid[%u], reset username is %s,"
"useroid is %u",
u_sess->misc_cxt.AuthenticatedUserId, rolename, useroid)));
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Abnormal process. UserOid has been reseted. Current userOid[%u], reset username is %s,"
"useroid is %u",
u_sess->misc_cxt.AuthenticatedUserId, rolename, useroid)));
}
}
@ -850,7 +829,7 @@ void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid usero
oldcontext = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR));
if (u_sess->proc_cxt.MyProcPort->user_name)
pfree_ext(u_sess->proc_cxt.MyProcPort->user_name);
u_sess->proc_cxt.MyProcPort->user_name = pstrdup((char*)GetSuperUserName((char*)userName));
u_sess->proc_cxt.MyProcPort->user_name = pstrdup((char *)GetSuperUserName((char *)userName));
(void)MemoryContextSwitchTo(oldcontext);
rolename = u_sess->proc_cxt.MyProcPort->user_name;
}
@ -863,23 +842,20 @@ void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid usero
securec_check_ss(rc, "", "");
rolename = roleIdStr;
}
/*
* Audit user login
* it's unsafe to deal with plugins hooks as dynamic lib may be released
/*
* Audit user login
* it's unsafe to deal with plugins hooks as dynamic lib may be released
*/
if (!(g_instance.status > NoShutdown) && user_login_hook) {
user_login_hook(u_sess->proc_cxt.MyProcPort->database_name, rolename, false, true);
}
int rcs = snprintf_truncated_s(details,
sizeof(details),
"login db(%s) failed-the role(%s)does not exist",
u_sess->proc_cxt.MyProcPort->database_name,
rolename);
int rcs = snprintf_truncated_s(details, sizeof(details), "login db(%s) failed-the role(%s)does not exist",
u_sess->proc_cxt.MyProcPort->database_name, rolename);
securec_check_ss(rcs, "\0", "\0");
pgaudit_user_login(FALSE, u_sess->proc_cxt.MyProcPort->database_name, details);
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("Invalid username/password,login denied.")));
ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("Invalid username/password,login denied.")));
}
rform = (Form_pg_authid)GETSTRUCT(roleTup);
@ -925,9 +901,8 @@ void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid usero
}
if (!rform->rolcanlogin)
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("role \"%s\" is not permitted to login", rolename)));
ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("role \"%s\" is not permitted to login", rolename)));
/*
* Check connection limit for this role.
@ -943,7 +918,7 @@ void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid usero
CountUserBackends(roleid) > rform->rolconnlimit) {
ReportAlarmTooManyDbUserConn(rolename);
ereport(FATAL,
(errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("too many connections for role \"%s\"", rolename)));
(errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("too many connections for role \"%s\"", rolename)));
} else if (!u_sess->misc_cxt.AuthenticatedUserIsSuperuser) {
ReportResumeTooManyDbUserConn(rolename);
}
@ -951,8 +926,8 @@ void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid usero
/* Record username and superuser status as GUC settings too */
SetConfigOption("session_authorization", rolename, PGC_BACKEND, PGC_S_OVERRIDE);
SetConfigOption(
"is_sysadmin", u_sess->misc_cxt.AuthenticatedUserIsSuperuser ? "on" : "off", PGC_INTERNAL, PGC_S_OVERRIDE);
SetConfigOption("is_sysadmin", u_sess->misc_cxt.AuthenticatedUserIsSuperuser ? "on" : "off", PGC_INTERNAL,
PGC_S_OVERRIDE);
ReleaseSysCache(roleTup);
}
@ -968,14 +943,15 @@ void InitializeSessionUserIdStandalone(void)
*/
#ifdef ENABLE_MULTIPLE_NODES
AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess() || IsJobSchedulerProcess() || IsJobWorkerProcess() ||
AM_WAL_SENDER || IsTxnSnapCapturerProcess() || IsTxnSnapWorkerProcess() || IsUndoWorkerProcess() ||
CompactionProcess::IsTsCompactionProcess() || IsRbCleanerProcess() || IsRbWorkerProcess() ||
t_thrd.role == PARALLEL_DECODE || t_thrd.role == LOGICAL_READ_RECORD);
#else /* ENABLE_MULTIPLE_NODES */
AM_WAL_SENDER || IsTxnSnapCapturerProcess() || IsTxnSnapWorkerProcess() || IsUndoWorkerProcess() ||
CompactionProcess::IsTsCompactionProcess() || IsRbCleanerProcess() || IsRbWorkerProcess() ||
t_thrd.role == PARALLEL_DECODE || t_thrd.role == LOGICAL_READ_RECORD);
#else /* ENABLE_MULTIPLE_NODES */
AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess() || IsJobSchedulerProcess() || IsJobWorkerProcess() ||
AM_WAL_SENDER || IsTxnSnapCapturerProcess() || IsTxnSnapWorkerProcess() || IsUndoWorkerProcess() || IsRbCleanerProcess() ||
IsRbWorkerProcess() || t_thrd.role == PARALLEL_DECODE || t_thrd.role == LOGICAL_READ_RECORD);
#endif /* ENABLE_MULTIPLE_NODES */
AM_WAL_SENDER || IsTxnSnapCapturerProcess() || IsTxnSnapWorkerProcess() || IsUndoWorkerProcess() ||
IsRbCleanerProcess() || IsRbWorkerProcess() || t_thrd.role == PARALLEL_DECODE ||
t_thrd.role == LOGICAL_READ_RECORD);
#endif /* ENABLE_MULTIPLE_NODES */
/* In pooler stateless reuse mode, to reset session userid */
if (!ENABLE_STATELESS_REUSE) {
@ -1011,8 +987,8 @@ void SetSessionAuthorization(Oid userid, bool is_superuser)
if (!t_thrd.xact_cxt.bInAbortTransaction && userid != u_sess->misc_cxt.AuthenticatedUserId &&
!u_sess->misc_cxt.AuthenticatedUserIsSuperuser && !superuser())
ereport(
ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set session authorization")));
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set session authorization")));
SetSessionUserId(userid, is_superuser);
@ -1077,10 +1053,10 @@ void SetCurrentRoleId(Oid roleid, bool is_superuser)
/*
* Get user name from user oid
*/
char* GetUserNameFromId(Oid roleid)
char *GetUserNameFromId(Oid roleid)
{
HeapTuple tuple;
char* result = NULL;
char *result = NULL;
tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
@ -1093,10 +1069,10 @@ char* GetUserNameFromId(Oid roleid)
return result;
}
char* GetUserNameById(Oid roleid)
char *GetUserNameById(Oid roleid)
{
HeapTuple tuple;
char* result = NULL;
char *result = NULL;
tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
@ -1109,7 +1085,6 @@ char* GetUserNameById(Oid roleid)
return result;
}
/* -------------------------------------------------------------------------
* Interlock-file support
*
@ -1130,7 +1105,7 @@ char* GetUserNameById(Oid roleid)
*/
static void UnlinkLockFile(int status, Datum filename)
{
char* fname = (char*)DatumGetPointer(filename);
char *fname = (char *)DatumGetPointer(filename);
if (fname != NULL) {
if (unlink(fname) != 0) {
@ -1154,7 +1129,7 @@ static void UnLockPidLockFile(int status, Datum fileDes)
}
}
static void CreatePidLockFile(const char* filename)
static void CreatePidLockFile(const char *filename)
{
int fd = -1;
char pid_lock_file[MAXPGPATH] = {0};
@ -1162,7 +1137,8 @@ static void CreatePidLockFile(const char* filename)
securec_check_ss(rc, "", "");
if ((fd = open(pid_lock_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) == -1) {
ereport(FATAL, (errcode_for_file_access(), errmsg("could not create or open lock file \"%s\": %m", pid_lock_file)));
ereport(FATAL,
(errcode_for_file_access(), errmsg("could not create or open lock file \"%s\": %m", pid_lock_file)));
}
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
@ -1180,7 +1156,7 @@ static void CreatePidLockFile(const char* filename)
* amPostmaster is used to determine how to encode the output PID.
* isDDLock and refName are used to determine what error message to produce.
*/
static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLock, const char* refName)
static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLock, const char *refName)
{
int fd = -1;
char buffer[MAXPGPATH * 2 + 256];
@ -1189,7 +1165,7 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
int encoded_pid;
pid_t other_pid;
pid_t my_pid, my_p_pid, my_gp_pid;
const char* envvar = NULL;
const char *envvar = NULL;
/* Grab a file lock to establish our priority to process postmaster.pid */
if (isDDLock) {
@ -1290,18 +1266,15 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
if (lstat(filename, &filenameStat) >= 0) {
if (0 == filenameStat.st_size) {
if (remove(filename) < 0)
ereport(FATAL,
(errcode_for_file_access(),
errmsg("bogus lock file \"%s\",could not unlink it : %m", filename)));
ereport(FATAL, (errcode_for_file_access(),
errmsg("bogus lock file \"%s\",could not unlink it : %m", filename)));
continue;
}
}
ereport(FATAL,
(errmsg("bogus data in lock file \"%s\": \"%s\", please kill the "
"instance process, than remove the damaged lock file",
filename,
buffer)));
ereport(FATAL, (errmsg("bogus data in lock file \"%s\": \"%s\", please kill the "
"instance process, than remove the damaged lock file",
filename, buffer)));
}
/*
@ -1338,24 +1311,20 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
{
ReportAlarmDataInstLockFileExist();
ereport(FATAL,
(errcode(ERRCODE_LOCK_FILE_EXISTS),
errmsg("lock file \"%s\" already exists", filename),
isDDLock
? ((encoded_pid < 0)
? errhint("Is another openGauss (PID %d) running in data directory \"%s\"?",
(int)other_pid,
refName)
: errhint("Is another postmaster (PID %d) running in data directory \"%s\"?",
(int)other_pid,
refName))
: ((encoded_pid < 0) ? errhint("Is another openGauss (PID %d) \
ereport(
FATAL,
(errcode(ERRCODE_LOCK_FILE_EXISTS), errmsg("lock file \"%s\" already exists", filename),
isDDLock
? ((encoded_pid < 0)
? errhint("Is another openGauss (PID %d) running in data directory \"%s\"?",
(int)other_pid, refName)
: errhint("Is another postmaster (PID %d) running in data directory \"%s\"?",
(int)other_pid, refName))
: ((encoded_pid < 0) ? errhint("Is another openGauss (PID %d) \
using socket file \"%s\"?",
(int)other_pid,
refName)
: errhint("Is another postmaster (PID %d) using socket file \"%s\"?",
(int)other_pid,
refName))));
(int)other_pid, refName)
: errhint("Is another postmaster (PID %d) using socket file \"%s\"?",
(int)other_pid, refName))));
}
}
}
@ -1372,7 +1341,7 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
* error.
*/
if (isDDLock != false) {
char* ptr = buffer;
char *ptr = buffer;
unsigned long id1, id2;
int lineno;
@ -1385,17 +1354,15 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
if (ptr != NULL && sscanf_s(ptr, "%lu %lu", &id1, &id2) == 2) {
if (PGSharedMemoryIsInUse(id1, id2)) {
ereport(FATAL,
(errcode(ERRCODE_LOCK_FILE_EXISTS),
errmsg("pre-existing shared memory block "
"(key %lu, ID %lu) is still in use",
id1,
id2),
errhint("If you're sure there are no old "
"server processes still running, remove "
"the shared memory block "
"or just delete the file \"%s\".",
filename)));
ereport(FATAL, (errcode(ERRCODE_LOCK_FILE_EXISTS),
errmsg("pre-existing shared memory block "
"(key %lu, ID %lu) is still in use",
id1, id2),
errhint("If you're sure there are no old "
"server processes still running, remove "
"the shared memory block "
"or just delete the file \"%s\".",
filename)));
}
}
}
@ -1406,12 +1373,10 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
* would-be creators.
*/
if (unlink(filename) < 0)
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not remove old lock file \"%s\": %m", filename),
errhint("The file seems accidentally left over, but "
"it could not be removed. Please remove the file "
"by hand and try again.")));
ereport(FATAL, (errcode_for_file_access(), errmsg("could not remove old lock file \"%s\": %m", filename),
errhint("The file seems accidentally left over, but "
"it could not be removed. Please remove the file "
"by hand and try again.")));
}
ReportResumeDataInstLockFileExist();
@ -1422,8 +1387,8 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
* both datadir and socket lockfiles; although more stuff may get added to
* the datadir lockfile later.
*/
char* unixSocketDir = NULL;
char* pghost = gs_getenv_r("PGHOST");
char *unixSocketDir = NULL;
char *pghost = gs_getenv_r("PGHOST");
if (pghost != NULL) {
check_backend_env(pghost);
}
@ -1438,18 +1403,13 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
}
}
int rc = snprintf_s(buffer,
sizeof(buffer),
sizeof(buffer) - 1,
"%d\n%s\n%ld\n%d\n%s\n",
amPostmaster ? (int)my_pid : -((int)my_pid),
t_thrd.proc_cxt.DataDir,
(long)t_thrd.proc_cxt.MyStartTime,
g_instance.attr.attr_network.PostPortNumber,
int rc = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d\n%s\n%ld\n%d\n%s\n",
amPostmaster ? (int)my_pid : -((int)my_pid), t_thrd.proc_cxt.DataDir,
(long)t_thrd.proc_cxt.MyStartTime, g_instance.attr.attr_network.PostPortNumber,
#ifdef HAVE_UNIX_SOCKETS
unixSocketDir
unixSocketDir
#else
""
""
#endif
);
@ -1467,13 +1427,13 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
pgstat_report_waitevent(WAIT_EVENT_LOCK_FILE_CREATE_WRITE);
if (strlen(buffer) > 0) {
if ((unsigned int)(write(fd, buffer, strlen(buffer))) != strlen(buffer)) {
int save_errno = errno;
int save_errno = errno;
close(fd);
(void)unlink(filename);
/* if write didn't set errno, assume problem is no disk space */
errno = save_errno ? save_errno : ENOSPC;
ereport(FATAL, (errcode_for_file_access(), errmsg("could not write lock file \"%s\": %m", filename)));
close(fd);
(void)unlink(filename);
/* if write didn't set errno, assume problem is no disk space */
errno = save_errno ? save_errno : ENOSPC;
ereport(FATAL, (errcode_for_file_access(), errmsg("could not write lock file \"%s\": %m", filename)));
}
}
pgstat_report_waitevent(WAIT_EVENT_END);
@ -1501,7 +1461,7 @@ static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLoc
* Arrange for automatic removal of lockfile at proc_exit.
*/
{
char* ptr = NULL;
char *ptr = NULL;
ptr = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), filename);
on_proc_exit(UnlinkLockFile, PointerGetDatum(ptr));
}
@ -1522,7 +1482,7 @@ void CreateDataDirLockFile(bool amPostmaster)
/*
* Create a lockfile for the specified Unix socket file.
*/
void CreateSocketLockFile(const char* socketfile, bool amPostmaster, bool is_create_psql_sock)
void CreateSocketLockFile(const char *socketfile, bool amPostmaster, bool is_create_psql_sock)
{
char lockfile[MAXPGPATH];
@ -1532,8 +1492,7 @@ void CreateSocketLockFile(const char* socketfile, bool amPostmaster, bool is_cre
CreateLockFile(lockfile, amPostmaster, false, socketfile);
/* Save name of lockfile for TouchSocketLockFile */
errno_t rcs = strcpy_s((is_create_psql_sock ? u_sess->misc_cxt.socketLockFile : u_sess->misc_cxt.hasocketLockFile),
MAXPGPATH,
lockfile);
MAXPGPATH, lockfile);
securec_check_c(rcs, "\0", "\0");
}
@ -1545,7 +1504,7 @@ void CreateSocketLockFile(const char* socketfile, bool amPostmaster, bool is_cre
* from being removed by overenthusiastic /tmp-directory-cleaner daemons.
* (Another reason we should never have put the socket file in /tmp...)
*/
void TouchSocketLockFileInternel(const char* socketLockFile)
void TouchSocketLockFileInternel(const char *socketLockFile)
{
/* Do nothing if we did not create a socket... */
if (socketLockFile[0] != '\0') {
@ -1589,12 +1548,12 @@ void TouchSocketLockFile(void)
* Caution: this erases all following lines. In current usage that is OK
* because lines are added in order. We could improve it if needed.
*/
void AddToDataDirLockFile(int target_line, const char* str)
void AddToDataDirLockFile(int target_line, const char *str)
{
int fd = -1;
int len;
int lineno;
char* ptr = NULL;
char *ptr = NULL;
char buffer[BLCKSZ];
fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
@ -1623,11 +1582,8 @@ void AddToDataDirLockFile(int target_line, const char* str)
for (lineno = 1; lineno < target_line; lineno++) {
if ((ptr = strchr(ptr, '\n')) == NULL) {
ereport(LOG,
(errmsg("incomplete data in \"%s\": found only %d newlines while trying to add line %d",
DIRECTORY_LOCK_FILE,
lineno - 1,
target_line)));
ereport(LOG, (errmsg("incomplete data in \"%s\": found only %d newlines while trying to add line %d",
DIRECTORY_LOCK_FILE, lineno - 1, target_line)));
close(fd);
return;
}
@ -1683,15 +1639,15 @@ void AddToDataDirLockFile(int target_line, const char* str)
*
* If compatible, return. Otherwise, ereport(FATAL).
*/
void ValidatePgVersion(const char* path)
void ValidatePgVersion(const char *path)
{
char full_path[MAXPGPATH];
FILE* file = NULL;
FILE *file = NULL;
int ret;
long file_major, file_minor;
long my_major = 0, my_minor = 0;
char* endptr = NULL;
const char* version_string = PG_VERSION;
char *endptr = NULL;
const char *version_string = PG_VERSION;
errno_t rc;
my_major = strtol(version_string, &endptr, 10);
@ -1707,9 +1663,8 @@ void ValidatePgVersion(const char* path)
if (file == NULL) {
if (errno == ENOENT)
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"%s\" is not a valid data directory", path),
errdetail("File \"%s\" is missing.", full_path)));
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("\"%s\" is not a valid data directory", path),
errdetail("File \"%s\" is missing.", full_path)));
else
ereport(FATAL, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", full_path)));
}
@ -1717,23 +1672,17 @@ void ValidatePgVersion(const char* path)
ret = fscanf_s(file, "%ld.%ld", &file_major, &file_minor);
if (ret != 2)
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"%s\" is not a valid data directory", path),
errdetail("File \"%s\" does not contain valid data.", full_path),
errhint("You might need to initdb.")));
ereport(FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("\"%s\" is not a valid data directory", path),
errdetail("File \"%s\" does not contain valid data.", full_path),
errhint("You might need to initdb.")));
FreeFile(file);
if (my_major != file_major || my_minor != file_minor)
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("database files are incompatible with server"),
errdetail("The data directory was initialized by PostgreSQL version %ld.%ld, "
"which is not compatible with this version %s.",
file_major,
file_minor,
version_string)));
ereport(FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("database files are incompatible with server"),
errdetail("The data directory was initialized by PostgreSQL version %ld.%ld, "
"which is not compatible with this version %s.",
file_major, file_minor, version_string)));
}
/* -------------------------------------------------------------------------
@ -1746,12 +1695,12 @@ void ValidatePgVersion(const char* path)
* 'gucname': name of GUC variable, for error reports
* 'restricted': if true, force libraries to be in $libdir/plugins/
*/
static void load_libraries(const char* libraries, const char* gucname, bool restricted)
static void load_libraries(const char *libraries, const char *gucname, bool restricted)
{
char* rawstring = NULL;
List* elemlist = NULL;
char *rawstring = NULL;
List *elemlist = NULL;
int elevel;
ListCell* l = NULL;
ListCell *l = NULL;
if (libraries == NULL || libraries[0] == '\0') {
return; /* nothing to do */
@ -1783,21 +1732,21 @@ static void load_libraries(const char* libraries, const char* gucname, bool rest
elevel = LOG;
foreach (l, elemlist) {
char* tok = (char*)lfirst(l);
char* filename = NULL;
char *tok = (char *)lfirst(l);
char *filename = NULL;
errno_t rc;
filename = pstrdup(tok);
if (strcmp(filename, "security_plugin") == 0 && WorkingGrandVersionNum < 92076) {
if (strcmp(filename, "security_plugin") == 0 && WorkingGrandVersionNum < 92076) {
continue;
}
}
canonicalize_path(filename);
/* If restricting, insert $libdir/plugins if not mentioned already */
if (restricted && first_dir_separator(filename) == NULL) {
char* expanded = NULL;
char *expanded = NULL;
expanded = (char*)palloc(strlen("$libdir/plugins/") + strlen(filename) + 1);
expanded = (char *)palloc(strlen("$libdir/plugins/") + strlen(filename) + 1);
rc = strcpy_s(expanded, strlen("$libdir/plugins/") + strlen(filename) + 1, "$libdir/plugins/");
securec_check_c(rc, "\0", "\0");
rc = strcat_s(expanded, strlen("$libdir/plugins/") + strlen(filename) + 1, filename);
@ -1818,12 +1767,11 @@ static void load_libraries(const char* libraries, const char* gucname, bool rest
/*
* process shared preloaded libraries internal
*/
void
process_shared_preload_libraries_internal(void)
void process_shared_preload_libraries_internal(void)
{
#ifdef ENABLE_MULTIPLE_NODES
if (is_streaming_engine_available()) {
load_libraries("streaming", "shared_preload_libraries", false);
load_libraries("streaming", "shared_preload_libraries", false);
}
#endif
return;
@ -1848,7 +1796,7 @@ void process_local_preload_libraries(void)
load_libraries(u_sess->attr.attr_common.local_preload_libraries_string, "local_preload_libraries", true);
}
void pg_bindtextdomain(const char* domain)
void pg_bindtextdomain(const char *domain)
{
#ifdef ENABLE_NLS
@ -1875,7 +1823,8 @@ void Reset_Pseudo_CurrentUserId(void)
* During connection obtaining, the agent_send_connection_params_parallel function
* is used to synchronize the version number.
*/
void register_backend_version(uint32 backend_version){
void register_backend_version(uint32 backend_version)
{
if (IsBootstrapProcessingMode() || IsInitProcessingMode() || !IS_PGXC_COORDINATOR) {
return;
}
@ -1890,7 +1839,7 @@ void register_backend_version(uint32 backend_version){
ereport(ERROR, (errcode(ERRCODE_SET_QUERY), errmsg("backend_version is a error value: %d", backend_version)));
}
securec_check_ss_c(ret, "\0", "\0");
if (PoolManagerSetCommand(POOL_CMD_GLOBAL_SET, sql_tmp, "backend_version") < 0){
if (PoolManagerSetCommand(POOL_CMD_GLOBAL_SET, sql_tmp, "backend_version") < 0) {
ereport(ERROR, (errmodule(MOD_TRANS_HANDLE), errcode(ERRCODE_SET_QUERY), errmsg("ERROR SET backend_version")));
}
}
@ -1898,8 +1847,8 @@ void register_backend_version(uint32 backend_version){
/*
* Check whether the version contains the backend_version parameter.
*/
bool contain_backend_version(uint32 version_number) {
return ((version_number >= V5R1C20_BACKEND_VERSION_NUM &&
version_number < V5R2C00_START_VERSION_NUM) ||
bool contain_backend_version(uint32 version_number)
{
return ((version_number >= V5R1C20_BACKEND_VERSION_NUM && version_number < V5R2C00_START_VERSION_NUM) ||
(version_number >= V5R2C00_BACKEND_VERSION_NUM));
}

View File

@ -2712,8 +2712,8 @@ void PostgresInitializer::InitExtensionVariable()
}
/* check whether the extension has been created */
const char* b_sql_plugin = "b_sql_plugin";
u_sess->attr.attr_sql.b_sql_plugin = CheckIfExtensionExists(b_sql_plugin);
const char* dolphin = "dolphin";
u_sess->attr.attr_sql.dolphin = CheckIfExtensionExists(dolphin);
}
void PostgresInitializer::FinishInit()

View File

@ -10340,7 +10340,16 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
oldCxt = MemoryContextSwitchTo(u_sess->plsql_cxt.curr_compile_context->compile_tmp_cxt);
u_sess->plsql_cxt.plpgsql_yylloc = plpgsql_yylloc;
(void) raw_parser(stmt);
RawParserHook parser_hook= raw_parser;
#ifndef ENABLE_MULTIPLE_NODES
if (u_sess->attr.attr_sql.dolphin) {
int id = GetCustomParserId();
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
parser_hook = (RawParserHook)g_instance.raw_parser_hook[id];
}
}
#endif
(void)parser_hook(stmt, NULL);
MemoryContextSwitchTo(oldCxt);
/* Restore former ereport callback */

View File

@ -44,11 +44,22 @@ static int g_iPosBlackList = 0;
/* array store for black list */
static BBOX_BLACKLIST_STRU g_stBlackList[BBOX_BLACK_LIST_COUNT_MAX];
/*
* Determines whether the byte order of the local machine is large or small
* return : ELFDATA2LSB - large
* : ELFDATA2MSB - small
*/
/*
function name: BBOX_DetermineMsb
description: The function should judge the mode that PC uses to store data is Big-endian/Little-endian.
arguments: void
return value: An integer that indicates the mode is Big-endian/Little-endian,
if it is ELFDATA2LSB, the mode is Little-endian,
if it is ELFDATA2MSB, the mode is Big-endian.
noteThe way that this function judge the mode that PC uses to store data is through a union variable unProbe,
at first we give its first member variable sShortInt a value BBOX_MSB_LSB_INT of type short, then its second
member variable cSplit[sizeof(short)] equaling to cSplit[2] would have the equal value of the first. Finally we
just need to compare BBOX_LITTER_BITS and BBOX_HIGH_BITS, namely the low byte and high byte of
BBOX_MSB_LSB_INT, with unProbe.cSplit[0] and unProbe.cSplit[1], if they are correspondingly equal, the mode is
Little-endian, else is the Big-endian.
date: 2022/8/2
contact tel: 18720816902
*/
int BBOX_DetermineMsb(void)
{
union INT_PROBE {

View File

@ -51,8 +51,19 @@ struct PIPE_IDS {
static struct PIPE_IDS astPipeIds[BBOX_MAX_PIDS];
/*
* compare string pszSrc and pszTarget
*/
function name: bbox_strncmp
description: To compare two substrings, the pointers pszSrc and pszTarget store their host strings'addresses.
arguments: Two pointers of type const char*, pointing to two strings needed to be compared.
An integer indicates the number of characters at the former of two strings that
will be compared.
return value: Type s32, an interger.
If it's zero, then the former substrings of string pszSrc and pszTarget are same,
else it indicates the difference between the first two characters that these two
strings can't match.
noteThe two pointers shouldn't be null. The last argument shouldn't less than zero.
date: 2022/8/2
contact tel: 18720816902
*/
s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count)
{
signed char cRes = 0;
@ -68,8 +79,20 @@ s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count)
}
/*
* compare string pszSrc and pszTarget
*/
function name: bbox_strcmp
description: compare two strings, the pointer pszSrc and pszTarget store their addresses.
arguments: Two pointers of type const char*, pointing to two strings needed to be compared.
An integer indicates the number of characters at the former of two strings that
will be compared.
return value: Type s32, an interger.
If it's zero, then the former substrings of string pszSrc and pszTarget are same,
else if it's 1, then it indicates between first two characters that these two
strings can't match, the character of first string that pszSrc points is greater,
else if it's -1, the character of second string that pszTarget points is greater.
noteThe two pointers shouldn't be null. The last argument shouldn't less than zero.
date: 2022/8/2
contact tel:same
*/
s32 bbox_strcmp(const char* pszSrc, const char* pszTarget)
{
unsigned char c1, c2;
@ -90,8 +113,15 @@ s32 bbox_strcmp(const char* pszSrc, const char* pszTarget)
}
/*
* get the length of string pszString
*/
function name: bbox_strlen
description: Calculate the length of string.
arguments: An pointer that indicates the address of a string.
return value: Type s32, an integer indicating the length of string.
note: the length of string=(address of the last character not '\0'-address of the first character)/sizeof(char), and sizeof(char)
equals to 1, so the length of string=(address of the last character not '\0'-address of the first character).
date: 2022/8/2
contact tel:same
*/
s32 bbox_strlen(const char* pszString)
{
const char* pszTemp = NULL;
@ -105,8 +135,16 @@ s32 bbox_strlen(const char* pszString)
}
/*
* get the length of string pszString
*/
function name: bbox_strnlen
description: Calculate the length of string, but having some restrictive conditions.
arguments: An pointer that indicates the address of a string.
And an integer that indicates the maxlenth.
return value: Type s32, an integer indicating the length of string.
note: If the length of string exceed the argument count, then return the length of string,
else return the argument count.
date: 2022/8/2
contact tel:same
*/
s32 bbox_strnlen(const char* pszString, s32 count)
{
const char* pszTemp = NULL;
@ -119,8 +157,16 @@ s32 bbox_strnlen(const char* pszString, s32 count)
}
/*
* convert a string to interger
*/
function name: bbox_atoi
description: Convert a string that includes continuous digital characters to an integer,
if the first character of the string is '-', then we will return a negative result.
arguments: An pointer that indicates the address of a string.
return value: Type s32, an integer indicating the result of string converted.
note: I think the function isn't perfect, though it's not a core function. For example, what about
the condition that the first character of the string is '+'?
date: 2022/8/2
contact tel:same
*/
s32 bbox_atoi(const char* pszString)
{
s32 n = 0;
@ -140,10 +186,18 @@ s32 bbox_atoi(const char* pszString)
return iNeg ? -n : n;
}
/*
* compare memory
*/
function name: bbox_memcmp
description: Compare former count bytes in ASCII of data stored in two areas that pointers cs and ct direct.
arguments: Two pointers to areas of memory, and an integer indicating the max counts compared.
return value: Type s32, an integer.
If the value returned is 0, then the data stored in two areas destined are same,
else if is 1, then between two first data in ASCII of byte different, cs's is greater,
else if is -1, then ct's is greater.
note: The two pointers should not be null, it's dangerous.
date: 2022/8/2
contact tel: same
*/
s32 bbox_memcmp(const void* cs, const void* ct, s32 count)
{
const unsigned char *su1 = NULL;
@ -159,8 +213,18 @@ s32 bbox_memcmp(const void* cs, const void* ct, s32 count)
}
/*
* search string l2 in l1
*/
function name: bbox_strstr
description: Judge if the string s2 directs is substring of string s1 directs.
arguments: Two pointers of type const char*, pointing to two strings.
return value: Type char*, a pointer. Actually it's a address, if s2 directs a
null string, then return the address of the first character of s1,
if the string s2 directs isn't substring of string s1 directs, return
null, if the string s2 directs is substring of string s1 directs, then return
the address of first character matched.
note: The two pointers should not be null, it's dangerous.
date: 2022/8/2
contact tel: same
*/
char* bbox_strstr(const char* s1, const char* s2)
{
int l1, l2;
@ -182,8 +246,17 @@ char* bbox_strstr(const char* s1, const char* s2)
}
/*
* make a directory
*/
function name: bbox_mkdir
description: We distinguish parent directory and child directory through character '/',
normally through a for loop, we can make sure all directories above the directory
we want to creat exist, finally we will creat the flag directory after its parent.
arguments: A pointers of type const char*, pointing to one strings, which indicates the filename and its full path.
return value: An integer of type s32, if it's RET_ERR, then we fail to make a directory, else if it's RET_OK then we succeed.
note: Take care the last non-null character of the string needed to be '/', and once if flag directory's
ancestors aren't exist, the function return RET_ERR.
date: 2022/8/2
contact tel: same
*/
s32 bbox_mkdir(const char* pszDir)
{
char szDirName[BBOX_TMP_LEN_32 * 16];
@ -228,8 +301,16 @@ s32 bbox_mkdir(const char* pszDir)
}
/*
* search free pipe id
*/
function name: bbox_GetFreePid
description: Through a for loop, we search a free pipe in a structure array, to an array element if its
member variable isUsed's value is 0, we return the array element's another member variable
stPid's address.
arguments: void
return value: An pointer of type struct PIPE_ID* or NULL.
note: none
date: 2022/8/2
contact tel: same
*/
struct PIPE_ID* bbox_GetFreePid(void)
{
u32 i;
@ -245,8 +326,14 @@ struct PIPE_ID* bbox_GetFreePid(void)
}
/*
* Release the occupied pipeid
*/
function name: bbox_PutPid
description: Release the occupied pipe.
arguments: A pointer of type struct PIPE_ID*.
return value: void
note: If the argument pointer is null, then there is no need to free the storage, the function ends.
date: 2022/8/2
contact tel: same
*/
void bbox_PutPid(struct PIPE_ID* pstPid)
{
struct PIPE_IDS* pstPids = NULL;
@ -261,8 +348,16 @@ void bbox_PutPid(struct PIPE_ID* pstPid)
}
/*
* find available pipe id by file handle
*/
function name: bbox_FindPid
description: In all occupied pipes, the function search the flag pipe through compare all structure
array elements's member variable stPid's member variable iFd with the function
argument iFd, if they are equal, then return the addres of this array elements.
arguments: An integer that indicates a file's file handle.
return value: A pointer of type struct PIPE_ID* or NULL.
note: none
date: 2022/8/2
contact tel: same
*/
struct PIPE_ID* bbox_FindPid(int iFd)
{
u32 i;
@ -281,8 +376,17 @@ struct PIPE_ID* bbox_FindPid(int iFd)
}
/*
* run popen
*/
function name: sys_popen
description: The function gets a free pipe by function bbox_GetFreePid, if normally, then creat a pipe
through sys_pipe, andcreat a child process through function sys_fork, execute a shell command
to run a process.
arguments: One pointer to a string that represents command line, another pointer of type const char*
indicates that the file file handle directs is used in the this mode.
return value: A pointer of type struct PIPE_ID* or NULL.
note: The string that indicates pszMode should only be "r" or "w",
date: 2022/8/2
contact tel: same
*/
s32 sys_popen(char* pszCmd, const char* pszMode)
{
struct PIPE_ID* volatile stCurPid = NULL;
@ -387,8 +491,15 @@ s32 sys_popen(char* pszCmd, const char* pszMode)
}
/*
* close file handle
*/
function name: sys_pclose
description: The function has an contrary action to function sys_popen, it close the pipe
that sys_popen open.
arguments: iFd, an integer that indicates a file handle.
return value: An integer that indicates the final status of the process working before.
note: none
date: 2022/8/2
contact tel: same
*/
int sys_pclose(s32 iFd)
{
struct PIPE_ID* pstCur = NULL;
@ -411,8 +522,17 @@ int sys_pclose(s32 iFd)
}
/*
* list file in directory
*/
function name: bbox_listdir
description: The function list all files below this path in directory.
arguments: The first argument is a pointer to a string representing a file path, all files below
this path will be listed in directory. The second argument is a pointer to a callback
function. The last is a pointer of type void*, it indicates a command line.
return value: An integer that indicates the result of function, if normal, it's RET_OK, else
it's RET_ERR.
note: The path that the first argument represents should be absolute path, take care.
date: 2022/8/2
contact tel: same
*/
s32 bbox_listdir(const char* pstPath, BBOX_LIST_DIR_CALLBACK callback, void* pArgs)
{
struct linux_dirent* pstEntry = NULL;

View File

@ -57,23 +57,37 @@ void bbox_initlog(int iLogScreen)
}
/*
* convert int to string
*/
function name: bbox_itoc
description: Convert an integer to a character.
arguments: An integer needed to be converted.
return value: An character that corresponds to the function's integer argument.
note: The integer argument can be converted in radices more than decimalism.
date: 2022/8/2
contact tel: 18720816902
*/
inline char bbox_itoc(u8 sNum)
{
return (char)((sNum < 10) ? (sNum + 48) : (sNum + 87));
}
/*
* convert int to string
* in : pCallback - call back function
* ptr - private data to call this function
* piCount - offset pointer
* iSize - buffer size
* uNum - the variable to convert
* sSys - type of variable
* isNeg - is negative
* return : need call back
*/
function name: bbox_put_dox
description: Conversion of number systems.
arguments: The first argument pCallback is a pointer to a callback function, we
use it to reverse the final result. The second argument is a pointer of
type void* used as a argument of function pCallback. The third argument
piCount is a pointer of type int, an offset pointer, also be used as a argument
of pCallback. The fourth argument is an integer of 32 bits, it indicates the buffer
size pCallback uses.The fifth argument uNum is a decimal integer that will
be converted to an integer in another radix. The sixth argument is used as
base to conversion of number systems. The last argument indicates the integer
after converted is a negative integer or not.
return value: An integer, indicating if the function pCallback work successfully.
note: The argument uNum should be a positive integer, after conversion of number systems
the sign will be appended to string's tail.
date: 2022/8/2
contact tel: 18720816902
*/
s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iSize, u64 uNum, s32 sSys, s32 isNeg)
{
s64 i = 0;
@ -108,15 +122,21 @@ s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iS
return iRet;
}
/*
* simple signal-safe function vsnprintf
* in : pCallback - call back function
* ptr - private data to call this function
* iSize - buffer size
* pFmt - format type
* ap - parameter list pointer¸ñʽ
* return : length of string
*/
function name: bbox_vsnprintf
description: The function is used to print string in corresponding array.
arguments: The first argument is a pointer to a callback function, the next is a
pointer to private data to call this function, also to buffer.
The third is used to destine buffer size. The forth is used to destine
the print format of deferent string, the last is a pointer to variable parameter list.
return value: An integer, if iSize is big enough, then the return value is the length of
string been written in destined memory successfully, not include '\0',
if function makes errors, the return value is a negative integer.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const char* pFmt, va_list ap)
{
@ -235,13 +255,20 @@ s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const c
}
/*
* call back function of snprintf_s
* in : c - string to calculate
* pPtr - pointer to buffer
* piCount - count of character
* iSize - limit of length
* return : length of string
*/
function name: bbox_SnprintCallback
description: The function is used to print string in corresponding array, usually
used as the first argument of function bbox_vsnprintf.
arguments: The first argument is a character waited to be written into buffer that
pPtr directs, the second argument directs a buffer area, the third is a
pointer to an integera used to record the count to call this callback function,
at the same time, it represents the count of characters written into buffer, it's
a pointer so that we can conveniently modify data storedin it. The last
argument destines the size of buffer, it represents the limit of length.
return value: An integer, if written successfully, it's RET_OK, else it's RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
s32 bbox_SnprintCallback(char c, void* pPtr, s32* piCount, s32 iSize)
{
char** pszBuff = (char**)pPtr;

View File

@ -64,8 +64,14 @@ u8 g_szAltStackMem[BBOX_ALT_STACKSIZE]; /* independent thread stack memory */
BBOX_ATOMIC_STRU g_isBusy = BBOX_ATOMIC_INIT(0); /* whether deal with core file. */
/*
* reserved count bytes on current stack, and set 0
*/
function name: BBOX_ReserveZeroStack
description: The function creat a empty stack, and its size depend on argument count.
arguments: An integer of type s32, namely int, it destines the storage of stack.
return value: void
note: The stack this function creats is actually a character array.
date: 2022/8/3
contact tel: 18720816902
*/
void BBOX_ReserveZeroStack(s32 count)
{
char buff[count];
@ -95,8 +101,14 @@ s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...)
}
/*
* get count of thread
*/
function name: BBOX_GetTaskNumber
description: When get a path to specific process, this function will return count of threads below it.
arguments: A pointer of type char*, including a path to specific process.
return value: An integer that indicates the count of threads below specific process.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
s32 BBOX_GetTaskNumber(char* szTaskPath)
{
struct kernel_stat stProcSB = {0};
@ -130,8 +142,17 @@ s32 BBOX_GetTaskNumber(char* szTaskPath)
}
/*
* get thread pid
*/
function name: BBOX_GetTaskId
description: When get a path to specific process, this function will return count of threads below it.
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
we use it as a structure array to store requisite thread infomation, the next argument destines
the max size of the array that the first argument destines. The last argument is a pointer of type
char*, including a path to specific process.
return value: An integer that indicates the count of threads stored in structure array.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
s32 BBOX_GetTaskId(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iSize, char* szTaskPath)
{
s32 iProc = -1;
@ -214,13 +235,19 @@ errout:
}
/*
* a ptrace debug thread
* in : TASK_ATTACH_INFO - thread information
* iPidCount - count of thread information
* iDoPtraceCheck - check if ptrace success
* return : 0 - success
* err code - failed
*/
function name: BBOX_PtraceAttachPid
description: The function is used to check the process whose id stored in structure array pstTaskInfo work normally.
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
it is used as a structure array that has stored requisite thread infomation, the next argument destines
the size of the array that the first argument destines, namely how many elements the array has.
The last argument is an integer to decide if need to check if the trace to destined process
work normally, if normal, corresponding element of array pstTaskInfo's member variable cIsAttached
will change from 0 to 1.
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck)
{
u32 i;
@ -272,13 +299,18 @@ s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s3
}
/*
* cancel ptrace debug thread
* in : TASK_ATTACH_INFO - thread information
* iPidCount - count of thread information
* iDoPtraceCheck - check if ptrace success
* return : 0 - success
* err code - failed
*/
function name: BBOX_DetachAllThread
description: The function is used to cancel checking the process whose id stored in structure array pstTaskInfo
work normally, "work normally" means in array pstTaskInfo corresponding element's member
variable cIsAttached's value is 1.
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
it is used as a structure array that has stored requisite thread infomation, the next argument destines
the size of the array that the first argument destines, namely how many elements the array has.
return value: void
note: none
date: 2022/8/3
contact tel: 18720816902
*/
void BBOX_DetachAllThread(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount)
{
u32 i;
@ -323,12 +355,18 @@ void BBOX_CheckResumeThread(void* pArgs)
}
/*
* ptrace thread and run function.
* in : pstArgs - information of callback function
* iMaxThreadCount - max count of thread
* pszProcSelfTask - /proc/[pid]/task of current tracked thread.
* return 0 if success else err code.
*/
function name: BBOX_PtraceAndRun
description: When get a path to specific process, this function will trace the threads below it, and get the
information for example how many threads work normally then store it in pstArgs.
arguments: The first argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
what matters is its member variable callback function pointer, the next argument destines
the max count of the thread. The last argument is a pointer of type char*, including a path
to specific process.
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char* pszProcSelfTask)
{
struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount];
@ -407,8 +445,15 @@ errout:
}
/*
* print log information if export failed.
*/
function name: BBOX_PrintFailedLog
description: Write log infomation into specific file, if errors arise, print the infomation about errors.
arguments: The only argument is a pointer of type const char* to a filename string, if this file doesn't
exist, we will creat a new file named it.
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
void BBOX_PrintFailedLog(const char* pFileName)
{
ssize_t iRet = 0;
@ -437,8 +482,15 @@ void BBOX_PrintFailedLog(const char* pFileName)
}
/*
* export thread information.
*/
function name: BBOX_ListThread
description: Export thread information.
arguments: The only argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
what matters is its member variable callback function pointer and thread infomation.
return value: void
note: none
date: 2022/8/3
contact tel: 18720816902
*/
void BBOX_ListThread(struct BBOX_ListParams* pstArgs)
{
pid_t ppid = 0;
@ -545,12 +597,18 @@ errout:
}
/*
* get return value of child process
* in : iClonePid - PID of child process
* pstArgs - parameter
* iCloneErrno - err code
* return 0 if success else failed.
*/
function name: BBOX_GetClonePidResult
description: The function get the status of child process at first, then according to it assign pstArgs's
member variables iError and iResult appropriate values.
arguments: The first argument is a integer named iClonePid, it represents the pid of child process.
The second argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
what matters is its member variable callback function pointer and thread infomation.
The third argument is a integer indicating error code.
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 iCloneErrno)
{
s32 iStatus = 0;

View File

@ -57,6 +57,22 @@ BlacklistItem g_blacklist_items[] = {
{DATA_WRITER_QUEUE, "DATA_WRITER_QUEUE", false}
};
/*
function name: coredump_handler
description: When a program is abnormal, but the exception appears in the core of process and wasn't caught,
The function will generate a file to store the information about memory of process, status of register
and running stack.
arguments: The first argument is an integer indicating signal code that usually used in program of processing
signal as variable.
The second argument is a structure pointer of type siginfo_t*, the memory that this pointer
directs stores comprehensive information about signal, for example, which process sends
and which user sends.
The third argument is a pointer of type void*, other kinds of pointers can directly used here.
return value: void
note: none
date: 2022/8/4
contact tel: 18720816902
*/
static void coredump_handler(int sig, siginfo_t *si, void *uc)
{
static volatile int64 first_tid = INVALID_TID;
@ -84,8 +100,19 @@ static void coredump_handler(int sig, siginfo_t *si, void *uc)
}
/*
* bbox_handler - handle signal conditions for bbox
*/
function name: bbox_handler
description: Handle signal conditions for bbox.
arguments: The first argument is an integer indicating signal code that usually used in program of processing
signal as variable.
The second argument is a structure pointer of type siginfo_t*, the memory that this pointer
directs stores comprehensive information about signal, for example, which process sends
and which user sends.
The third argument is a pointer of type void*, other kinds of pointers can directly used here.
return value: void
note: none
date: 2022/8/4
contact tel: 18720816902
*/
static void bbox_handler(int sig, siginfo_t *si, void *uc)
{
static volatile int64 first_tid = INVALID_TID;
@ -125,8 +152,16 @@ static void bbox_handler(int sig, siginfo_t *si, void *uc)
}
/*
* get_bbox_coredump_pattern_path - get the core dump path from the file "/proc/sys/kernel/core_pattern"
*/
function name: get_bbox_coredump_pattern_path
description: Get the core dump file's path from the file "/proc/sys/kernel/core_pattern".
arguments: The first argument is a pointer to string, we use it to store core dump file's path acquired
from the file "/proc/sys/kernel/core_pattern", the next argument is the number of characters
reading from the file "/proc/sys/kernel/core_pattern", all len-1 characters or less if appear '\n'.
return value: void
note: none
date: 2022/8/4
contact tel: 18720816902
*/
static void get_bbox_coredump_pattern_path(char* path, Size len)
{
FILE* fp = NULL;
@ -156,7 +191,17 @@ static void get_bbox_coredump_pattern_path(char* path, Size len)
}
}
/* compute directory into which bbox dump core files are saved. */
/*
function name: build_bbox_corepath
description: Get the core dump file's path.
arguments: The first argument is a pointer to string, we use it to store core dump file's path,
the next argument is the size of the path's name, the last argument is a pointer
to string that indicates maybe store a path to configure the core dump file.
return value: void
note: none
date: 2022/8/4
contact tel: 18720816902
*/
static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *config_path)
{
struct stat stat_buf;
@ -232,6 +277,15 @@ void assign_bbox_corepath(const char* newval, void* extra)
return;
}
/*
function name: show_bbox_dump_path
description: Get the dump file's path.
arguments: void
return value: A pointer of type const char*, directing the path to dump or NULL.
note: none
date: 2022/8/4
contact tel: 18720816902
*/
const char* show_bbox_dump_path(void)
{
const char* path = g_bbox_dump_path;
@ -239,6 +293,15 @@ const char* show_bbox_dump_path(void)
return (path != NULL) ? path : "";
}
/*
function name: split_string_into_blacklist
description: Get all strings been divided into character ',' in source string.
arguments: A pointer of type const char*, directing the source string.
return value: A pointer of type static List*.
note: none
date: 2022/8/4
contact tel: 18720816902
*/
static List* split_string_into_blacklist(const char* source)
{
List *result = NIL;
@ -264,7 +327,6 @@ static List* split_string_into_blacklist(const char* source)
return result;
}
bool check_bbox_blacklist(char** newval, void** extra, GucSource source)
{
if (t_thrd.proc_cxt.MyProcPid != PostmasterPid)
@ -402,10 +464,15 @@ void bbox_blacklist_remove(BlacklistIndex item, void* addr)
}
/*
* @Description: check the value from environment variablethe to prevent command injection.
* @in input_env_value : the input value need be checked.
*
*/
function name: CheckFilenameValid
description: Check if the filename is in line with norms, or if dangerous characters appear
the filename is invalid.
arguments: A pointer to string indicating filename.
return value: An integer, if function works normally, the value is RET_OK, else it's RET_ERR.
note: none
date: 2022/8/4
contact tel: 18720816902
*/
int CheckFilenameValid(const char* inputEnvValue)
{
const int maxLen = 1024;

View File

@ -45,6 +45,15 @@
static bool CommCheckFilterMatch(const char *filter, int len, const char *ip, int port);
/*
function name: SetCPUAffinity
description: The function set the affinity of CPU or CPUs destined by argument cpu_id.
arguments: An integer representing the id of one CPU or more.
return value: void
note: none
date: 2022/8/5
contact: 18720816902
*/
void SetCPUAffinity(int cpu_id)
{
cpu_set_t mask;
@ -270,6 +279,15 @@ IPAddrType CommLibNetGetIPType(unsigned int ip)
#define CMD_STR_MAX 512
#define CMD_OUTPUT_BUFFER_SIZE 1024
/*
function name: CommCheckLtranProcess
description: The function check if the process currently working has loaded transactions.
arguments: void
return value: 0 or 1, if 1, then at least one loaded transcation exists, if 0, no one.
note: none
date: 2022/8/5
contact: 18720816902
*/
int CommCheckLtranProcess()
{
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
@ -387,6 +405,21 @@ static T GetCommProxySubParameter(const char* str_attr, const char* key)
return res;
}
/*
function name: ParseCommProxyNumaBind
description: Get the ids of CPU to bind process with specific CPU.
arguments: The first argument is a pointer of type const char* to a string that indicating
the id of CPUs below NUMA, not necessarily all CPUs.
The second argument is an integer telling us we will get CPUs' id from which position
of array str_attr.
The third argument tells us the number of NUMA system framework.
The fourth argument is a pointer to an integer array used to store CPUs' id gotten
from string str_attr, we can use these ids to bind specific CPU.
return value: void
note: none
date: 2022/8/5
contact: 18720816902
*/
static void ParseCommProxyNumaBind(
const char* str_attr, const int pos, const int numa_num, int* numa_bind)
{
@ -487,6 +520,22 @@ bool ParseCommProxyAttr(CommProxyConfig* config)
return true;
}
/*
function name: CommCheckFilterMatch
description: This function compare the ip and port allowed with ip and port gotten from
Filter, if they are correspondingly same, it will return true value.
arguments: The first argument is a pointer of type const char* to a string that indicating
the id and port of the request been sent to Filter, the id and port have been
separated by character ':'.
The second argument is an integer telling us we the length of the string first
argument directs.
The third argument tells us the ip allowed.
The fourth argument tells us the port allowed.
return value: static bool
note: none
date: 2022/8/4
contact: 18720816902
*/
static bool CommCheckFilterMatch(const char *filter, int len, const char *ip, int port)
{
char *str_ip = NULL;

View File

@ -175,6 +175,24 @@ void UpdateTxRxStats(int msg_level)
last_rx_nbytes = current_rx_nbytes;
}
/*
function name: parse_monitor_sock_queue
description: Compare the string recv_buffer with "sockqueue fd:fd", the "fd"
after character ':' is an integer indicating file descriptor. If recv_buffer
accords with the format, the function will takes next action to see if
fd is 0, which represents stdin, so the function ends with returned value 0.
If fd isn't 0, compare the third argument type with ParseMonitorTypeSet,
if equal, then get a structure variable including socket descriptor
destined by the fd gotten from the first argument, if it's NULL, we can
write "fd:[%d], type:[normal fd], no sock queue" into send_buffer.
arguments: The first argument is a pointer to a string indicating request infomation.
The second argument is a pointer to a string to store sent infomation.
The third argument tells the kind of socket request.
return value: 0 or 1.
note: none
date: 2022/8/5
contact tel: 18720816902
*/
int parse_monitor_sock_queue(char* recv_buffer, char* send_buffer, ParseMonitorType type)
{
int length;
@ -205,6 +223,25 @@ int parse_monitor_sock_queue(char* recv_buffer, char* send_buffer, ParseMonitorT
return 0;
}
/*
function name: parse_monitor_fd
description: Compare the string recv_buffer with "query fd:fd", the "fd"
after character ':' is an integer indicating file descriptor. If recv_buffer
accords with the format, the function will takes next action to see if
fd is 0, which represents stdin, so the function ends with returned value 0.
If fd isn't 0, compare the third argument type with ParseMonitorTypeSet,
if equal, then get a structure variable including socket descriptor
destined by the fd gotten from the first argument, if it's NULL, we can
write "fd:[%d], type:[normal fd]"(%d--fd) into send_buffer, else write
"fd:[%d], type:[%d]"(%d--fd,%d--sock_desc->m_fd_type).
arguments: The first argument is a pointer to a string indicating request infomation.
The second argument is a pointer to a string to store sent infomation.
The third argument tells the kind of socket request.
return value: 0 or 1.
note: none
date: 2022/8/5
contact tel: 18720816902
*/
int parse_monitor_fd(char* recv_buffer, char* send_buffer, ParseMonitorType type)
{
int length;

View File

@ -53,6 +53,24 @@ static void comm_wait_broadcast_end(SocketRequest** req_arr, int num);
* export function definition
************************************************************************************
*/
/*
function name: comm_proxy_socket
description: This function creates a socket file descriptor whose protocol family is
domain, protocol type is type, and protocol number is protocol. If the
function call is successful, it will return a file descriptor that identifies
the socket. If it fails, it will return - 1.
arguments: The first argument specifies the protocol family, it's used as domain to
set up network communication.
The second argument is used to set the type of socket communication.
The third argument is used to specify a specific type of a protocol, which
is a type in the second argument types' type.
return value: If the function call is successful, it will return a file descriptor that
identifies the socket. If it fails, it will return - 1.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_socket(int domain, int type, int protocol)
{
SocketRequest req;
@ -154,6 +172,16 @@ ssize_t comm_proxy_addr_recv(int sockfd, void *buf, size_t len, int flags)
return comm_proxy_recv(sockfd, buf, len, flags);
}
/*
function name: comm_proxy_close
description: The function is used to release the resources allocated
to the socket by the system.
arguments: The argument is the socket file descriptor to be closed.
return value: If the call is successful, return 0; otherwise, return - 1 and set errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_close(int fd)
{
SocketRequest req;
@ -205,6 +233,18 @@ int comm_proxy_close(int fd)
return result.s_ret;
}
/*
function name: comm_proxy_shutdown
description: The function is used to release the resources allocated
to the socket by the system.
arguments: The first argument is a descriptor used to identify a socket.
The second argument is used to describe which operations
are prohibited, which determines the behavior of the function.
return value: If the call is successful, return 0; otherwise, return - 1 and set errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_shutdown(int fd, int how)
{
SocketRequest req;
@ -259,6 +299,21 @@ int comm_proxy_shutdown(int fd, int how)
return result.s_ret;
}
/*
function name: comm_proxy_accept
description: This function extracts the first connection from the waiting connection queue of S, creates
a new socket interface similar to s and returns a handle.
arguments: The first argument is a socket descriptor, which listens for connection after comm_proxy_listen().
The second argument is a optional pointer pointing to a buffer where the address of the
connection entity known to the communication layer is received. The actual format of the
addr argument is determined by the address family generated when the socket is created.
The third argument is a optional pointer, used together with addr, pointing to the integer
number with the length of addr address.
return value: The return value is a new socket descriptor, which represents a new connection with the client.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
{
SocketRequest req;
@ -343,6 +398,17 @@ int comm_proxy_accept4(int sockfd, struct sockaddr* addr, socklen_t* addrlen, in
return comm_proxy_accept(sockfd, addr, addrlen);
}
/*
function name: comm_proxy_connect
description: This function is used to establish a connection with a specified socket.
arguments: The first argument is used to identify an unconnected socket.
The second argument is a pointer to the sockaddr structure to socket will be connected.
The third argument is byte length of sockaddr structure.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
SocketRequest req;
@ -394,6 +460,17 @@ int comm_proxy_connect(int sockfd, const struct sockaddr *addr, socklen_t addrle
return result.s_ret;
}
/*
function name: comm_proxy_bind
description: This function binds a local address with a set of interfaces.
arguments: The first argument indicates the socket descriptor that has been established.
The second argument is a pointer to the sockaddr structure to socket.
The third argument is byte length of sockaddr structure.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_bind(int sockfd, const struct sockaddr* ServerAddr, socklen_t addrlen)
{
SocketRequest req;
@ -421,6 +498,16 @@ int comm_proxy_bind(int sockfd, const struct sockaddr* ServerAddr, socklen_t add
return result.s_ret;
}
/*
function name: comm_proxy_listen
description: This function creates a socket interface and listens for the requested connection.
arguments: The first argument is a descriptor used to identify a bundled but unconnected socket.
The second argument indicates the maximum length of waiting for connection queue
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_listen(int sockfd, int backlog)
{
SocketRequest req;
@ -447,6 +534,19 @@ int comm_proxy_listen(int sockfd, int backlog)
return result.s_ret;
}
/*
function name: comm_proxy_setsockopt
description: The function is used to set option values for sockets of any type and any state.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument indicates the level defined by the option.
The third argument specifies the option to be set.
The fourth argument is a pointer to the buffer where the new value of the option to be set is stored.
The fifth argument indicates optval buffer length.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen)
{
SocketRequest req;
@ -476,6 +576,19 @@ int comm_proxy_setsockopt(int sockfd, int level, int optname, const void* optval
return result.s_ret;
}
/*
function name: comm_proxy_getsockopt
description: The function is used to obtain the current value of the option of any type and any state socket, and store the result in optval.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument indicates the level defined by the option.
The third argument specifies the socket options to be obtained.
The fourth argument is a pointer to the buffer where the obtained option value is stored.
The fifth argument is a pointer to the length value of optval buffer.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen)
{
SocketRequest req;
@ -504,6 +617,18 @@ int comm_proxy_getsockopt(int sockfd, int level, int optname, void* optval, sock
return result.s_ret;
}
/*
function name: comm_proxy_getsockname
description: The function is used to get the name of a socket. It is used for a bundled or
connected socket, and the local address will be returned.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument indicates the address of the receiving socket.
The third argument specifies the length of the name buffer.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
{
SocketRequest req;
@ -530,6 +655,17 @@ int comm_proxy_getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen
return result.s_ret;
}
/*
function name: comm_proxy_getpeername
description: The function is used to obtain the foreign protocol address associated with a socket.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument indicates the name structure of the receiver address.
The third argument specifies the length of the name structure.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
{
SocketRequest req;
@ -556,6 +692,19 @@ int comm_proxy_getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen
return result.s_ret;
}
/*
function name: comm_proxy_fcntl
description: The function can change the nature of the opened file, it provides control over descriptors.
The argument sockfd is a descriptor operated by the argument cmd. For the value of cmd,
fcntl can accept the third argument arg, which is a variable argument.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument represents the instruction to be operated.
The third argument is a variable argument
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_fcntl(int sockfd, int cmd, ...)
{
SocketRequest req;
@ -622,6 +771,17 @@ int comm_proxy_fcntl(int sockfd, int cmd, ...)
return result.s_ret;
}
/*
function name: comm_proxy_poll
description: The function is used to hang the current file pointer to the waiting queue.
arguments: The first argument is an array of struct pollfd structure type, used to store the socket descriptor whose state needs to be detected.
The second argument is used to mark the total number of structural elements in the array fdarray;
The third argument is the blocking time of the comm_proxy_poll function call.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_poll(struct pollfd* fdarray, unsigned long nfds, int timeout)
{
CommWaitPollParam param;
@ -658,6 +818,15 @@ int comm_proxy_poll(struct pollfd* fdarray, unsigned long nfds, int timeout)
return param.s_ret;
}
/*
function name: comm_proxy_epoll_create
description: The function is used to create a handle to epoll.
arguments: The only argument size is used to tell the kernel how many listeners there are.
return value: Returns a file descriptor that points to the newly created epoll instance
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_epoll_create(int size)
{
/*
@ -687,6 +856,21 @@ int comm_proxy_epoll_create1(int flag)
return comm_proxy_epoll_create(1);
}
/*
function name: comm_proxy_epoll_ctl
description: This system call performs control operations on the epoll instance referenced
by the file descriptor epfd. It requires the operation op to execute the target
file descriptor fd. It's used as epoll's event registration function, it adds,
modifies, or deletes events of interest to the epoll object.
arguments: The first argument is a specific file descriptor for epoll generated by epoll_ create.
The second argument indicates the actions to be taken, such as registering events.
The third argument is associated file descriptor.
The fourth argument is a pointer of type struct epoll_event, used to tell the kernel what events and actions to listen for.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
{
SocketRequest req;
@ -926,6 +1110,23 @@ int comm_proxy_epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
return result.s_ret;
}
/*
function name: comm_proxy_epoll_wait
description: Wait for IO events on the specified epoll file descriptor.
arguments: The first argument is a specific file descriptor for epoll generated by epoll_ create.
The second argument is a pointer to type epoll_ event structure, but it is now used
as a container to get the collection of events from the kernel.
The third argument is used to tell how large the container is (number of event
array members), that is, the number of events that can be processed each time.
The fourth argument is the timeout value for waiting for IO events.
return value: When successful, comm_proxy_epoll_wait() returns the number of file descriptors
ready for the requested IO. Returns zero if no file descriptor is ready within the
requested timeout milliseconds. When an error occurs, comm_proxy_epoll_wait()
returns - 1 and sets errno correctly.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout)
{
CommWaitEpollWaitParam param;

View File

@ -81,6 +81,27 @@ void mc_tcp_set_keepalive(int fd)
mc_tcp_setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (char*)&count, sizeof(count));
}
/*
function name: mc_tcp_get_peer_name
description: This function is used to obtain the host IP and port number of the host bound to the specific socket.
arguments: The first argument is a descriptor to a specified socket.
The second argument is used to store the host IP address bound to the socket determined by the first parameter, in dotted decimal.
The third parameter is used to store the port number bound to a specific socket, in the order of host bytes.
return value: Return 0 if the function runs successfully.
When the call to the getpeername() function fails
1Return EBADF if the socket argument is not a valid file descriptor.
2Return EINVAL if the socket has been shut down.
3Return ENOTCONN if the socket is not connected or otherwise has not had the peer pre-specified.
4Return ENOTSOCK if the socket argument does not refer to a socket.
5Return EOPNOTSUPP if the operation is not supported for the socket protocol.
6Return ENOBUFS if insufficient resources were available in the system to complete the call.
Return -2 when the host IP address belongs to IPv4 type, it fails to convert it to dotted decimal.
Return -3 when the host IP address belongs to IPv6 type, it fails to convert it to dotted decimal.
Return -4 when the error type is not any of the above.
note: Allocate a certain amount of memory space for the host and port pointers respectively in advance.
date: 2022/8/9
contact tel: 18720816902
*/
int mc_tcp_get_peer_name(int fd, char* host, int* port)
{
struct sockaddr peeraddr = {0};
@ -121,6 +142,17 @@ int mc_tcp_set_cloexec(int fd)
return set_socketopt(fd, 1, FD_CLOEXEC);
}
/*
function name: mc_tcp_accept
description: This function will block the process by default until a client connection is established and returns a new available socket.
arguments: The first argument is a socket descriptor to a specific socket.
The second argument is a result parameter, which is used to accept a return value that specifies the address of the client.
The third argument is also a result argument, which is used to accept the size of the sockaddr structure. It indicates the number of bytes occupied by the sockaddr structure.
return value: Return a value less than 0 if an error occurred when call the function accept4(), else return the new fd of socket.
note: none
date: 2022/8/9
contact tel: 18720816902
*/
int mc_tcp_accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{
int new_fd;
@ -147,6 +179,17 @@ again:
return (new_fd);
}
/*
function name: mc_tcp_bind
description: This function binds the specified socket to a specific IP address and port.
arguments: The first argument indicates the socket descriptor that has been established.
The second argument is a pointer to the sockaddr structure to socket.
The third argument is byte length of sockaddr structure.
return value: Return errno, the return value is 0 if succeed, else one of other error types is returned for failure.
note: none
date: 2022/8/9
contact tel: 18720816902
*/
int mc_tcp_bind(int fd, const struct sockaddr* sa, socklen_t salen)
{
int error = -1;
@ -191,6 +234,20 @@ static void mc_tcp_do_listen(int fd, int backlog)
}
}
/*
function name: mc_tcp_read_block
description: This function receives data from the other end of TCP in a blocking manner, the receiving
process will not end until the data of size byte length is successfully received or a real error occurs in the receiving process
arguments: The first argument indicates the specific socket that has been established.
The second argument is a pointer to memory area, we use it to store received data.
The third argument is byte length of the memory area pointed to by the data pointer.
The fourth argument specifies additional operations in addition to the read operation.
return value: If there is no error, it returns the byte length of the successfully read data. If an error
occurs, return - 1.
note: When the data is read successfully, the byte length of the data is greater than 0.
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_read_block(int fd, void* data, int size, int flags)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -261,6 +318,20 @@ int mc_tcp_read_block(int fd, void* data, int size, int flags)
return (size_t)nbytes;
}
/*
function name: mc_tcp_read_nonblock
description: This function receives data from the other end of TCP in a non blocking manner,
the data receiving process is only performed once.
arguments: The first argument indicates the specific socket that has been established.
The second argument is a pointer to memory area, we use it to store received data.
The third argument is byte length of the memory area pointed to by the data pointer.
The fourth argument specifies additional operations in addition to the read operation.
return value: If the error type is one of the errors represented by EAGAIN, EWOULDBLOCK and EINTR, it returns 0;
other error types return - 1; if there is no error, it returns the byte length of the successfully read data.
note: When the data is read successfully, the byte length of the data is greater than 0.
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_read_nonblock(int fd, void* data, int size, int flags)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -306,6 +377,17 @@ int mc_tcp_read_nonblock(int fd, void* data, int size, int flags)
return (size_t)nbytes;
}
/*
function name: mc_tcp_check_socket
description: This function binds the specified socket to a specific IP address and port.
arguments: The only argument indicates the specific socket that has been established.
return value: Return -1 if when the recv function wait for the protocol to receive data,
the other end of TCP closes the connection or a real error occurred while
reading data. In other cases, 0 is returned.
note: none
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_check_socket(int sock)
{
char temp_buf[IOV_DATA_SIZE] = {0};
@ -368,6 +450,19 @@ int mc_tcp_check_socket(int sock)
return 0;
}
/*
function name: mc_tcp_write_block
description: This function writes data to the specified socket in blocking mode, the sending process
will not end until all the data are successfully sent or a real error occurs during the sending process
arguments: The first argument indicates the specific socket that has been established.
The second argument is a pointer to memory area, we use it to store data to be sent.
The third argument is byte length of data to be sent.
return value: If there is no error, it returns the byte length of the successfully sent data. If an error
occurs, return - 1.
note: none
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_write_block(int fd, const void* data, int size)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -426,6 +521,21 @@ int mc_tcp_write_block(int fd, const void* data, int size)
return (size_t)nSend;
}
/*
function name: mc_tcp_write_noblock
description: This function writes data to the specified socket in non blocking mode,
the data transmission process is only performed once.
arguments: The first argument indicates the specific socket that has been established.
The second argument is a pointer to memory area, we use it to store data to be sent.
The third argument is byte length of data to be sent.
return value: If the sending fails but the failure reason is one of the error types represented by EAGAIN
EWOULDBLOCKEINTR ENOBUFS, then 0 is returned; if the error type is other, then - 1
is returned; If the transmission is successful, the byte length of the successfully transmitted
data is returned
note: none
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_write_noblock(int fd, const void* data, int size)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -519,6 +629,17 @@ int mc_tcp_addr_init(const char* host, int port, struct sockaddr_storage* ss, in
return (error == 1) ? 0 : error;
}
/*
function name: mc_tcp_connect_nonblock
description: This function is used to create a socket and establish a connection with the port of the specified host
in non blocking mode.
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
return value: If the connection is successfully established, the file descriptor of the socket connected to the port of the
specified host is returned; otherwise, - 1 is returned.
note: none
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_connect_nonblock(const char* host, int port)
{
int sockfd, n;
@ -566,6 +687,18 @@ int mc_tcp_connect_nonblock(const char* host, int port)
return sockfd;
}
/*
function name: mc_tcp_connect
description: This function first obtains the ports of other hosts with the same domain name stored through
the ports of specific hosts, and creates a socket to establish a connection with an appropriate
one of these ports.
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
return value: The key is to successfully establish a connection with a port in the linked list. If the connection is successful, the
socket file descriptor connected to it will be returned. Otherwise, it will return - 1.
note: We finally get the infomation of the ports of other hosts through a linked list.
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_connect(const char* host, int port)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -656,6 +789,18 @@ retry:
return (sockfd);
}
/*
function name: mc_tcp_listen
description: This function first obtains the ports of other hosts with the same domain name stored through
the ports of specific hosts, and creates a socket to bind with an appropriate one of these ports.
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
The third is used to store size of protocol address.
return value: The key lies in the successful binding with a port in the linked list. If the binding is successful, the socket file
descriptor connected to it will be returned. Otherwise, it will return - 1.
note: We finally get the infomation of the ports of other hosts through a linked list.
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_listen(const char* host, int port, socklen_t* addrlenp)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE

View File

@ -232,6 +232,21 @@ static int gs_tcp_write_noblock(int node_idx, int sock, const char* msg, int msg
return send_bytes;
}
/*
function name: libcomm_tcp_send
description: This function is used to send the message including message head and message body, to
a specific socket.
arguments: send_ info is a pointer of LibcommRecvInfo* type, pointing to the memory storing the data
waiting to be sent.
return value: Data will be sent twice in total. Before sending data, if it is found that the socket to receive
data is not matched with the specified socket, then - 1 will be returned; If the sending of
message head or message body fails, return - 1; If the function runs successfully, the byte
length of the message body sent successfully is returned.
note: none
date: 2022/8/11
contact tel: 18720816902
*/
static int libcomm_tcp_send(LibcommSendInfo* send_info)
{
int sock = send_info->socket;
@ -322,6 +337,19 @@ static int libcomm_tcp_send(LibcommSendInfo* send_info)
return send_bytes;
}
/*
function name: libcomm_tcp_recv_noidx
description: This function is used to store the message transmitted from the sender, specifically to obtain
the message from a specific socket.
arguments: recv_ info is a pointer of LibcommRecvInfo* type, pointing to the memory to store the data
received.
return value: If it fails to allocate memory for iov_ Item, return RECV_MEM_ERROR;
If it fails to obtain data, no matter it is a message header or a message body, from the specified socket in blocking mode, return RECV_NET_ERROR;
If the function runs successfully, the byte length of the read message body is returned.
note: none
date: 2022/8/11
contact tel: 18720816902
*/
static int libcomm_tcp_recv_noidx(LibcommRecvInfo* recv_info)
{
int sock = recv_info->socket;
@ -371,6 +399,23 @@ static int libcomm_tcp_recv_noidx(LibcommRecvInfo* recv_info)
return error;
}
/*
function name: libcomm_tcp_recv
description: This function is used to store the message transmitted from the sender, specifically to obtain
the message from a specific socket.
arguments: recv_ info is a pointer of LibcommRecvInfo* type, pointing to the memory to store the data
received.
return value: If the receiver has not been determined, call libcomm_tcp_recv_noidx() and take the return value
of (libcomm_tcp_recv_noidx (recv_info)); Return RECV_NET_ERROR if there is an error in the
process of reading the message heade or message body; If there is no data readable in the
receiving buffer of the specified socket at this time or the number of bytes of the data that
has been read is not enough, it returns RECV_NEED_RETRY; If iov_item is NULL, it returns
RECV_MEM_ERROR if it fails to allocate space for it; If the function runs successfully, then
the byte length of the read message head and message body is returned.
note: none
date: 2022/8/11
contact tel: 18720816902
*/
int libcomm_tcp_recv(LibcommRecvInfo* recv_info)
{
MsgHead* msg_head = NULL;

View File

@ -137,6 +137,16 @@ static int LibCommClientSSLDHVerifyCb(const SSL* s, const SSL_CTX* ctx,
return 1;
}
/*
function name: ssl_cipher_list2string
description: This function converts the two-dimensional character array storing the key into a one-dimensional character array.
arguments: The first argument represents the two-dimensional character array to be converted.
The second argument indicates the number of one-dimensional arrays contained in this two-dimensional array.
return value: Returns a pointer to the one-dimensional character array that has been successfully converted. If the conversion fails, NULL is returned.
note: none
date: 2022/8/12
contact tel: 18720816902
*/
static char* ssl_cipher_list2string(const char* ciphers[], const int num) {
int i;
int catlen = 0;
@ -227,7 +237,20 @@ char* LibCommErrMessage(void) {
return errBuf;
}
/*
function name: LibCommClientSSLPasswd
description: As a client, this function is used to detect whether there is a file with a valid key in the specified
directory and whether there is permission to operate it. If so, the password will be decrypted by
using the file.
arguments: The first parameter is a pointer of type (SSL *).
The second parameter is used to obtain the absolute path of the certificate file.
The third parameter represents the user name.
The fourth parameter is a pointer of type (libcommconn *), whose member variable contains the ciphertext to be decrypted.
return value: If the path is empty or does not have operation permission to the directory where the certificate file is located, a non-1 value is returned; otherwise, 0 is returned.
note: none
date: 2022/8/12
contact tel: 18720816902
*/
int LibCommClientSSLPasswd(SSL* pstContext, const char * path, const char * userName, LibCommConn * conn) {
char* CertFilesDir = NULL;
char CertFilesPath[MAXPATH] = {0};
@ -251,8 +274,8 @@ int LibCommClientSSLPasswd(SSL* pstContext, const char * path, const char * user
/*check whether the cipher and rand files begins with userName exist.
if exist, decrypt it.
if not,decrypt the default cipher and rand files begins with client%.
Because,for every client user mayown certification and private key*/
if not,decrypt the default cipher and rand files begins with client.
Because,for every client user may own certification and private key*/
if (NULL == userName) {
retval = LibCommClientCheckPermissionCipherFile(CertFilesDir, conn, NULL);
if (retval != 1)

View File

@ -33,6 +33,16 @@ inline int mc_lqueue_item_size(struct mc_lqueue_item* q_item)
return q_item->element.data->iov_len;
}
/*
function name: mc_lqueue_add
description: Add an element to a specific queue.
arguments: The first parameter is a pointer of type (mc_lqueue *), whose member variable list points to the target queue.
The second parameter points to the element to be added to the queue.
return value: Returns 1 if the element is successfully added to the queue, otherwise returns - 1.
note: none
date: 2022/8/13
contact tel: 18720816902
*/
int mc_lqueue_add(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
{
if (q == NULL || q_item == NULL) {
@ -60,6 +70,17 @@ int mc_lqueue_add(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
return 1;
}
/*
function name: mc_lqueue_remove
description: Remove the head element in a specific queue.
arguments: The first parameter is a pointer of type (mc_lqueue *), whose member variable list points to the target queue.
The second parameter points to the queue head element used to store the removal from the queue.
return value: Return NULL if an error occurs during the removal of the queue head element, otherwise a pointer to
the successfully removed queue head element is returned.
note: none
date: 2022/8/13
contact tel: 18720816902
*/
struct mc_lqueue_item* mc_lqueue_remove(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
{
if (q == NULL) {
@ -89,6 +110,18 @@ struct mc_lqueue_item* mc_lqueue_remove(struct mc_lqueue* q, struct mc_lqueue_it
return q_item;
}
/*
function name: mc_lqueue_init
description: This function is used to open an area in the memory area. One part of the area is used to store a queue with
a certain specification, and the other part is used to store the information of the queue, such as the specification
and the number of elements. Finally, a pointer to the area is returned.
arguments: This parameter specifies that the maximum number of elements that the queue can hold is size, but this does
not mean that the size of the queue is so large at the beginning.
return value: If the function runs successfully, it returns a pointer to the opened memory area; otherwise, it returns NULL.
note: none
date: 2022/8/13
contact tel: 18720816902
*/
struct mc_lqueue* mc_lqueue_init(unsigned long size)
{
if (size == 0) {

View File

@ -222,6 +222,7 @@ NON_EXEC_STATIC void PercentileMain()
g_instance.stat_cxt.force_process = false;
sleep(SLEEP_INTERVAL);
}
elog(LOG, "instrumention percentile ended");
gs_thread_exit(0);
}

View File

@ -14,11 +14,23 @@ import os
from . import feature_mapping
from . import features
# To import file feature_mapping and features from parent folder
#function name: load_feature_lib
#description: Print the variable FEATURE_LIB in the file-- features
#return value: The value of FEATURE_LIB
#date: 2022/8/2
#contact: 1865997821
def load_feature_lib():
return features.FEATURE_LIB
#function name: get_feature_mapper
#description: Get the item and value of a dictionary type in the file-- feature_mapping and output it as a generator.
#return value: The item and value in _dict_ variable
#noteDictionary key-value pairs must start with C then the item and value will be return.
#date: 2022/8/2
#contact: 1865997821
def get_feature_mapper():
return {

View File

@ -11,22 +11,27 @@
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
import csv
#import csv packet
from collections import defaultdict
from typing import List
# To import defaultdict in the parent floder collections and List in the parent floder typing
import numpy as np
# import numpy packet as the name np
from ..analyzer import _euclid_distance as euclid_distance
from dbmind.common.utils import ExceptionCatch
#To import private function-- _euclid_distance as euclid_distance
#function name: calculate_weight
#description: This function will output feature_weight (= residual_vector / the sum of residual_vector)
#The data used for the calculation is from the features_labels_dict, and the key value pairs of the features_labels_dict are filtered
#arguments: np.ndarray and np.ndarray
#return value: weight_matrix
#date: 2022/8/2
#contact: 1865997821
def calculate_weight(features: np.ndarray, labels: np.ndarray) -> List:
"""
Calculate weight matrix based on feature set
:param features: feature set
:param labels: label set
:return: weight_matrix
"""
normalize_features, normalize_labels = [], []
features_labels_dict = defaultdict(list)
for i in range(len(labels)):
@ -56,6 +61,16 @@ def calculate_weight(features: np.ndarray, labels: np.ndarray) -> List:
return weight_matrix
# function name: build_model
# description: Create two variables-- features and labels.There are refer to two numpy array(all elements are zero)
# The features array's size is feature_number and dimension is feature_dimension
# This function will read the two arrays and write it as a matrix in a csv file(the save path is './features_new.npz')
# And then it will call the function calculate_weight to calculate the matrix
# arguments: feature_path, feature_number, feature_dimension
# return value: None
# noteA ExceptionCatch function modifier is used
# date: 2022/8/2
#contact: 1865997821
@ExceptionCatch(strategy='exit', name='FEATURE')
def build_model(feature_path: str, feature_number: int, feature_dimension: int,
save_path: str = './features_new.npz') -> None:

View File

@ -11,6 +11,13 @@
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
#function name: detect
#description: if the method is "bool" type, then call the functions sum_detect、avg_detect、ks_detect to diagnose errors
#These functions are in the parent slow_sql/significance_detection
#arguments: data1(array), data2(array), method
#return value: bool type
#date: 2022/8/2
#contact: 1865997821
def detect(data1, data2, method='bool', threshold=0.01, p_value=0.5):
if method == 'bool':

View File

@ -12,17 +12,16 @@
# See the Mulan PSL v2 for more details.
alpha = 1e-10
#Define a minimum number of errors
#function name: detect
#description: Calculate whether the data has abrupt changes based on the average value
#arguments: data1, data2, threshold,method
#return value: bool
#date: 2022/8/
#contact: 1865997821
def detect(data1, data2, threshold=0.5, method='bool'):
"""
Calculate whether the data has abrupt changes based on the average value
:param data1: input data array
:param data2: input data array
:param threshold: Mutation rate
:param method: The way to calculate the mutation
:return: bool
"""
if not isinstance(data1, list) or not isinstance(data2, list):
raise TypeError("The format of the input data is wrong.")
avg1 = sum(data1) / len(data1) if data1 else 0

View File

@ -13,8 +13,14 @@
import sys
from .cli import DBMindRun
#To import DBMindRun method from the parent file cli
#function name: main
#description: Get the system command parameters, pass to the DBMindRun and call this function,if an InterruptedError is reported, the program will exit( sys.exit(1)).
#arguments: None
#return value: None
#date: 2022/8/3
#contact: 1865997821
def main() -> None:
try:
DBMindRun(sys.argv[1:])

View File

@ -55,7 +55,12 @@ CONFIG_OPTIONS = {
'LOG-level': ['DEBUG', 'INFO', 'WARNING', 'ERROR']
}
#function name: check_config_validity
#description: Checks the validity of the passed parameter
#arguments: section, option, value
#return value: bool and string
#date: 2022/8/
#contact: 1865997821
def check_config_validity(section, option, value):
config_item = '%s-%s' % (section, option)
# exceptional cases:
@ -87,6 +92,16 @@ def check_config_validity(section, option, value):
return True, None
#function name: load_sys_configs
#description: Create and load the modification file
#arguments: The configuration to modify
#return value: a new configuration file
#noteTo facilitate the user to modify the configuration items through the
#configuration file easily, we add inline comments to the file, but we need to remove the inline comments while parsing.
#Otherwise, it will cause the read configuration items to be wrong.
#date: 2022/8/
#contact: 1865997821
def load_sys_configs(confile):
# Note: To facilitate the user to modify the configuration items through the
# configuration file easily, we add inline comments to the file, but we need
@ -96,6 +111,8 @@ def load_sys_configs(confile):
with open(file=confile, mode='r') as fp:
configs.read_file(fp)
# Define a class that encapsulates the modification item
class ConfigWrapper(object):
def __getattribute__(self, name):
try:
@ -122,7 +139,7 @@ def load_sys_configs(confile):
return ConfigWrapper()
# Defines a class that updates the encapsulated modification file
class ConfigUpdater:
def __init__(self, filepath):
self.config = ConfigParser(inline_comment_prefixes=None)
@ -170,7 +187,7 @@ class ConfigUpdater:
self.fp.flush()
self.fp.close()
# Defines a class that dynamically displays a modified item
class DynamicConfig:
@staticmethod
def get(*args, **kwargs):

View File

@ -43,6 +43,7 @@ except ImportError:
SKIP_LIST = ('COMMENT', 'LOG')
# The global variable acts as a switch that controls whether the program runs
dbmind_master_should_exit = False
@ -57,8 +58,16 @@ def _process_clean(force=False):
global_vars.worker.terminate(cancel_futures=force)
TimedTaskManager.stop()
#function name: signal_handler
#description: The function processes the received signal parameters, reassigns variable x according to different signals
#or calls other functions to complete the content indicated by signals
#arguments: signum, frame
#return value: bool (dbmind_master_should_exit)
#date: 2022/8/3
#contact: 1865997821
def signal_handler(signum, frame):
# The global variable dbmind_master_should_exit can be modified in this function to continue to play a control role
global dbmind_master_should_exit
if signum == signal.SIGINT or signum == signal.SIGHUP:
@ -148,10 +157,12 @@ class DBMindMain(Daemon):
time.sleep(1)
logging.info('DBMind will close.')
# Emptying the execution pool
def clean(self):
if os.path.exists(self.pid_file):
os.unlink(self.pid_file)
# Reload the execution pool and solve the error
def reload(self):
pid = read_dbmind_pid_file(self.pid_file)
if pid > 0:

View File

@ -27,6 +27,17 @@ def do_after(rt_result):
def do_exception(exception):
"""Nothing"""
#function name: around
#description: Preserve the function properties and prevent an error from terminating the program
#arguments: One or more functions
#return value: none
#note Decorators are implemented in such a way that the function being decorated is actually another function (the function name and other properties change).
#To avoid this, Python's FuncTools package provides a decorator called wraps to remove such side effects.
#When writing a decorator, it is a good idea to wrap FuncTools before implementing it.
#It preserves the name and properties of the original function
#date: 2022/8/4
#contact: 1865997821
def around(func, *args, **kw):
@wraps(func)
def wrapper():

View File

@ -15,7 +15,11 @@ from typing import Optional, Iterable, Union
from .root_cause import RootCause
from .enumerations import ALARM_TYPES, ALARM_LEVEL
#Define an Alarm class that takes the error parameters entered by the user and displays the error content and cause
#methodDisplay the error content and suggestions, and retrieve suggestions provided by the system. If there are no suggestions, return “ no suggestions”
#noteThe property decorator turns a method into a property call.(root_causes、suggestions)
#date2022/8/4
#contact18365997821
class Alarm:
def __init__(self,
host: Union[str],

View File

@ -12,7 +12,11 @@
# See the Mulan PSL v2 for more details.
from .root_cause import RootCause
#Define anSlowQuery class thatSlow query accepts user input commands and performs operations on the database
#methodDisplay the error content and suggestions, and retrieve suggestions provided by the system. If there are no suggestions, return “ no suggestions”
#noteThe property decorator turns a method into a property call.(root_causes、suggestions)
#date2022/8/4
#contact18365997821
class SlowQuery:
def __init__(self, db_host, db_port, db_name, schema_name, query, start_timestamp, duration_time,
hit_rate=None, fetch_rate=None, cpu_time=None, data_io_time=None, template_id=None, sort_count=None,

View File

@ -18,13 +18,19 @@ import psycopg2
from .execute_factory import ExecuteFactory
from .execute_factory import IndexInfo
#class name: DriverExecute Inherits from the parent class ExecuteFactory
#description: The SQL statement performs the operations associated with the call
#date: 2022/8/10
#contact: 1865997821
class DriverExecute(ExecuteFactory):
def __init__(self, *arg):
#Call the arguments of the parent class __init__ method
super(DriverExecute, self).__init__(*arg)
self.conn = None
self.cur = None
#Connecting to the database
def init_conn_handle(self):
self.conn = psycopg2.connect(dbname=self.dbname,
user=self.user,
@ -33,6 +39,7 @@ class DriverExecute(ExecuteFactory):
port=self.port)
self.cur = self.conn.cursor()
#If an error occurs after the SQL statement is executed, the error information is reported to the user
def execute(self, sql):
try:
self.cur.execute(sql)
@ -41,11 +48,13 @@ class DriverExecute(ExecuteFactory):
except Exception:
self.conn.commit()
#Disconnecting from the database
def close_conn(self):
if self.conn and self.cur:
self.cur.close()
self.conn.close()
#Check whether multiple nodes exist
def is_multi_node(self):
self.init_conn_handle()
try:

View File

@ -13,6 +13,11 @@
import re
#class name: IndexInfo
#description: Define information about table indexes
#methods: __init__
#date: 2022/8/10
#contact: 1865997821
class IndexInfo:
def __init__(self, schema, table, indexname, columns, indexdef):
@ -24,7 +29,9 @@ class IndexInfo:
self.primary_key = False
self.redundant_obj = []
#class name: ExecuteFactory
#date: 2022/8/10
#contact: 1865997821
class ExecuteFactory:
def __init__(self, dbname, user, password, host, port, schema, multi_node, max_index_storage):
self.dbname = dbname
@ -36,11 +43,11 @@ class ExecuteFactory:
self.max_index_storage = max_index_storage
self.multi_node = multi_node
# Record redundant indexes
@staticmethod
def record_redundant_indexes(cur_table_indexes, redundant_indexes):
cur_table_indexes = sorted(cur_table_indexes,
key=lambda index_obj: len(index_obj.columns.split(',')))
# record redundant indexes
for pos, index in enumerate(cur_table_indexes[:-1]):
is_redundant = False
for candidate_index in cur_table_indexes[pos + 1:]:
@ -52,6 +59,7 @@ class ExecuteFactory:
if is_redundant:
redundant_indexes.append(index)
#Match the name of the table against the index of the query
@staticmethod
def match_table_name(table_name, query_index_dict):
for elem in query_index_dict.keys():
@ -66,6 +74,7 @@ class ExecuteFactory:
return False, table_name
return True, table_name
#Retrieves a valid index based on the regular expression, adding the corresponding index and empty element if none exists
@staticmethod
def get_valid_indexes(record, hypoid_table_column, valid_indexes):
tokens = record.split(' ')
@ -88,6 +97,7 @@ class ExecuteFactory:
if columns not in valid_indexes[table_name]:
valid_indexes[table_name].append((columns, index_type))
#Record invalid SQL statements and returns the corresponding help information that matches the corresponding SQL statement
@staticmethod
def record_ineffective_negative_sql(candidate_index, obj, ind):
cur_table = candidate_index.table
@ -125,6 +135,7 @@ class ExecuteFactory:
candidate_index.ineffective_pos.append(ind)
candidate_index.total_sql_num += obj.frequency
#Returns the last input and the corresponding result
@staticmethod
def match_last_result(table_name, index_column, history_indexes, history_invalid_indexes):
for column in history_indexes.get(table_name, dict()):
@ -142,6 +153,7 @@ class ExecuteFactory:
if not history_indexes[table_name]:
del history_indexes[table_name]
#Correcting SQL statements
@staticmethod
def make_single_advisor_sql(ori_sql):
sql = 'select gs_index_advise(\''

View File

@ -23,12 +23,16 @@ from .execute_factory import IndexInfo
BASE_CMD = None
#class name: GSqlExecute
#description: Solve the optimization problem of GSQL statement execution
#date: 2022/8/11
#contact: 1865997821
class GSqlExecute(ExecuteFactory):
def __init__(self, *args):
super(GSqlExecute, self).__init__(*args)
def init_conn_handle(self):
#define a global variable BASE_CMD,it is a connection command statement
global BASE_CMD
BASE_CMD = 'gsql -p ' + str(self.port) + ' -d ' + self.dbname
if self.host:
@ -38,6 +42,7 @@ class GSqlExecute(ExecuteFactory):
if self.password:
BASE_CMD += ' -W ' + self.password
#Run the shell command in BASE_CMD
def run_shell_cmd(self, target_sql_list):
cmd = BASE_CMD + ' -c \"'
if self.schema:
@ -47,6 +52,7 @@ class GSqlExecute(ExecuteFactory):
cmd += '\"'
proc = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
#Read data from stdout and stderr,If an error message is displayed, an error message is displayed
(stdout, stderr) = proc.communicate()
stdout, stderr = stdout.decode(), stderr.decode()
if 'gsql: FATAL:' in stderr or 'failed to connect' in stderr:
@ -74,6 +80,7 @@ class GSqlExecute(ExecuteFactory):
print(e.output.decode(), file=sys.stderr)
return int(ret.decode().strip().split()[2]) > 0
#Parse the recommended result returned
@staticmethod
def parse_single_advisor_result(res, table_index_dict):
if len(res) > 2 and res[0:2] == ' (':
@ -183,6 +190,7 @@ class GSqlExecute(ExecuteFactory):
total_cost = 0
found_plan = False
hypo_index = False
# create hypo-indexes
for line in res:
if 'QUERY PLAN' in line:
found_plan = True
@ -222,6 +230,7 @@ class GSqlExecute(ExecuteFactory):
i += 1
return total_cost
#Production workflows consume report files
def estimate_workload_cost_file(self, workload, index_config=None, ori_indexes_name=None):
sql_file = str(time.time()) + '.sql'
is_computed = False
@ -264,6 +273,7 @@ class GSqlExecute(ExecuteFactory):
return total_cost
#Check for empty indexes and note them to optimize the table structure
def check_useless_index(self, history_indexes, history_invalid_indexes):
schemas = [elem.lower()
for elem in filter(None, self.schema.split(','))]

View File

@ -26,9 +26,11 @@ import logging
try:
from .dao.gsql_execute import GSqlExecute
from .dao.execute_factory import ExecuteFactory
from .mcts import MCTS
except ImportError:
from dao.gsql_execute import GSqlExecute
from dao.execute_factory import ExecuteFactory
from mcts import MCTS
ENABLE_MULTI_NODE = False
SAMPLE_NUM = 5
@ -192,9 +194,12 @@ class IndexAdvisor:
self.workload_used_index))
if DRIVER:
self.db.close_conn()
opt_config = greedy_determine_opt_config(self.workload_info[0], atomic_config_total,
candidate_indexes, self.index_cost_total[0])
if MAX_INDEX_STORAGE:
opt_config = MCTS(self.workload_info[0], atomic_config_total, candidate_indexes,
MAX_INDEX_STORAGE, MAX_INDEX_NUM)
else:
opt_config = greedy_determine_opt_config(self.workload_info[0], atomic_config_total,
candidate_indexes, self.index_cost_total[0])
self.retain_lower_cost_index(candidate_indexes)
if len(opt_config) == 0:
print("No optimal indexes generated!")
@ -943,7 +948,7 @@ def check_parameter(args):
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
args.max_index_num)
if args.max_index_storage is not None and args.max_index_storage <= 0:
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
raise argparse.ArgumentTypeError("%s is an invalid positive float value" %
args.max_index_storage)
JSON_TYPE = args.json
MAX_INDEX_NUM = args.max_index_num
@ -971,7 +976,7 @@ def main(argv):
arg_parser.add_argument(
"--max_index_num", help="Maximum number of suggested indexes", type=int)
arg_parser.add_argument("--max_index_storage",
help="Maximum storage of suggested indexes/MB", type=int)
help="Maximum storage of suggested indexes/MB", type=float)
arg_parser.add_argument("--multi_iter_mode", action='store_true',
help="Whether to use multi-iteration algorithm", default=False)
arg_parser.add_argument("--multi_node", action='store_true',

View File

@ -0,0 +1,397 @@
import sys
import math
import random
import copy
STORAGE_THRESHOLD = 0
AVAILABLE_CHOICES = None
ATOMIC_CHOICES = None
WORKLOAD_INFO = None
MAX_INDEX_NUM = 0
def is_same_index(index, compared_index):
return index.table == compared_index.table and \
index.columns == compared_index.columns and \
index.index_type == compared_index.index_type
def atomic_config_is_valid(atomic_config, config):
# if candidate indexes contains all atomic index of current config1, then record it
for atomic_index in atomic_config:
is_exist = False
for index in config:
if is_same_index(index, atomic_index):
index.storage = atomic_index.storage
is_exist = True
break
if not is_exist:
return False
return True
def find_subsets_num(choice):
atomic_subsets_num = []
for pos, atomic in enumerate(ATOMIC_CHOICES):
if not atomic or len(atomic) > len(choice):
continue
# find valid atomic index
if atomic_config_is_valid(atomic, choice):
atomic_subsets_num.append(pos)
# find the same atomic index as the candidate index
if len(atomic) == 1 and (is_same_index(choice[-1], atomic[0])):
choice[-1].atomic_pos = pos
return atomic_subsets_num
def find_best_benefit(choice):
atomic_subsets_num = find_subsets_num(choice)
total_benefit = 0
for ind, obj in enumerate(WORKLOAD_INFO):
# calculate the best benefit for the current sql
max_benefit = 0
for pos in atomic_subsets_num:
if (obj.cost_list[0] - obj.cost_list[pos]) > max_benefit:
max_benefit = obj.cost_list[0] - obj.cost_list[pos]
total_benefit += max_benefit
return total_benefit
def get_diff(available_choices, choices):
except_choices = copy.copy(available_choices)
for i in available_choices:
for j in choices:
if is_same_index(i, j):
except_choices.remove(i)
return except_choices
class State(object):
"""
The game state of the Monte Carlo tree search,
the state data recorded under a certain Node node,
including the current game score, the current number of game rounds,
and the execution record from the beginning to the current.
It is necessary to realize whether the current state has reached the end of the game state,
and support the operation of randomly fetching from the Action collection.
"""
def __init__(self):
self.current_storage = 0.0
self.current_benefit = 0.0
# record the sum of choices up to the current state
self.accumulation_choices = []
# record available choices of current state
self.available_choices = []
self.displayable_choices = []
def get_available_choices(self):
return self.available_choices
def set_available_choices(self, choices):
self.available_choices = choices
def get_current_storage(self):
return self.current_storage
def set_current_storage(self, value):
self.current_storage = value
def get_current_benefit(self):
return self.current_benefit
def set_current_benefit(self, value):
self.current_benefit = value
def get_accumulation_choices(self):
return self.accumulation_choices
def set_accumulation_choices(self, choices):
self.accumulation_choices = choices
def is_terminal(self):
# the current node is a leaf node
return len(self.accumulation_choices) == MAX_INDEX_NUM
def compute_benefit(self):
return self.current_benefit
def get_next_state_with_random_choice(self):
# ensure that the choices taken are not repeated
if not self.available_choices:
return None
random_choice = random.choice([choice for choice in self.available_choices])
self.available_choices.remove(random_choice)
choice = copy.copy(self.accumulation_choices)
choice.append(random_choice)
benefit = find_best_benefit(choice)
# if current choice not satisfy restrictions, then continue get next choice
if benefit <= self.current_benefit or \
self.current_storage + random_choice.storage > STORAGE_THRESHOLD:
return self.get_next_state_with_random_choice()
next_state = State()
# initialize the properties of the new state
next_state.set_accumulation_choices(choice)
next_state.set_current_benefit(benefit)
next_state.set_current_storage(self.current_storage + random_choice.storage)
next_state.set_available_choices(get_diff(AVAILABLE_CHOICES, choice))
return next_state
def __repr__(self):
self.displayable_choices = ['{}: {}'.format(choice.table, choice.columns)
for choice in self.accumulation_choices]
return "reward: {}, storage :{}, choices: {}".format(
self.current_benefit, self.current_storage, self.displayable_choices)
class Node(object):
"""
The Node of the Monte Carlo tree search tree contains the parent node and
current point information,
which is used to calculate the traversal times and quality value of the UCB,
and the State of the Node selected by the game.
"""
def __init__(self):
self.visit_number = 0
self.quality = 0.0
self.parent = None
self.children = []
self.state = None
def get_parent(self):
return self.parent
def set_parent(self, parent):
self.parent = parent
def get_children(self):
return self.children
def expand_child(self, node):
node.set_parent(self)
self.children.append(node)
def set_state(self, state):
self.state = state
def get_state(self):
return self.state
def get_visit_number(self):
return self.visit_number
def set_visit_number(self, number):
self.visit_number = number
def update_visit_number(self):
self.visit_number += 1
def get_quality_value(self):
return self.quality
def set_quality_value(self, value):
self.quality = value
def update_quality_value(self, reward):
self.quality += reward
def is_all_expand(self):
return len(self.children) == \
len(AVAILABLE_CHOICES) - len(self.get_state().get_accumulation_choices())
def __repr__(self):
return "Node: {}, Q/N: {}/{}, State: {}".format(
hash(self), self.quality, self.visit_number, self.state)
def tree_policy(node):
"""
In the Selection and Expansion stages of Monte Carlo tree search,
the node that needs to be searched (such as the root node) is passed in,
and the best node that needs to be expanded is returned
according to the exploration/exploitation algorithm.
Note that if the node is a leaf node, it will be returned directly.
The basic strategy is to first find the child nodes that have not been selected at present,
and select them randomly if there are more than one. If both are selected,
find the one with the largest UCB value that has weighed exploration/exploitation,
and randomly select if the UCB values are equal.
"""
# check if the current node is leaf node
while node and not node.get_state().is_terminal():
if node.is_all_expand():
node = best_child(node, True)
else:
# return the new sub node
sub_node = expand(node)
# when there is no node that satisfies the condition in the remaining nodes,
# this state is empty
if sub_node.get_state():
return sub_node
# return the leaf node
return node
def default_policy(node):
"""
In the Simulation stage of Monte Carlo tree search, input a node that needs to be expanded,
create a new node after random operation, and return the reward of the new node.
Note that the input node should not be a child node,
and there are unexecuted Actions that can be expendable.
The basic strategy is to choose the Action at random.
"""
# get the state of the game
current_state = copy.deepcopy(node.get_state())
# run until the game over
while not current_state.is_terminal():
# pick one random action to play and get next state
next_state = current_state.get_next_state_with_random_choice()
if not next_state:
break
current_state = next_state
final_state_reward = current_state.compute_benefit()
return final_state_reward
def expand(node):
"""
Enter a node, expand a new node on the node, use the random method to execute the Action,
and return the new node. Note that it is necessary to ensure that the newly
added nodes are different from other node Action
"""
new_state = node.get_state().get_next_state_with_random_choice()
sub_node = Node()
sub_node.set_state(new_state)
node.expand_child(sub_node)
return sub_node
def best_child(node, is_exploration):
"""
Using the UCB algorithm,
select the child node with the highest score after weighing the exploration and exploitation.
Note that if it is the prediction stage,
the current Q-value score with the highest score is directly selected.
"""
best_score = -sys.maxsize
best_sub_node = None
# travel all sub nodes to find the best one
for sub_node in node.get_children():
# The children nodes of the node contains the children node whose state is empty,
# this kind of node comes from the node that does not meet the conditions.
if not sub_node.get_state():
continue
# ignore exploration for inference
if is_exploration:
C = 1 / math.sqrt(2.0)
else:
C = 0.0
# UCB = quality / times + C * sqrt(2 * ln(total_times) / times)
left = sub_node.get_quality_value() / sub_node.get_visit_number()
right = 2.0 * math.log(node.get_visit_number()) / sub_node.get_visit_number()
score = left + C * math.sqrt(right)
# get the maximum score, while filtering nodes that do not meet the space constraints and
# nodes that have no revenue
if score > best_score \
and sub_node.get_state().get_current_storage() <= STORAGE_THRESHOLD \
and sub_node.get_state().get_current_benefit() > 0:
best_sub_node = sub_node
best_score = score
return best_sub_node
def backpropagate(node, reward):
"""
In the Backpropagation stage of Monte Carlo tree search,
input the node that needs to be expended and the reward of the newly executed Action,
feed it back to the expend node and all upstream nodes,
and update the corresponding data.
"""
# update util the root node
while node is not None:
# update the visit number
node.update_visit_number()
# update the quality value
node.update_quality_value(reward)
# change the node to the parent node
node = node.parent
def monte_carlo_tree_search(node):
"""
Implement the Monte Carlo tree search algorithm, pass in a root node,
expand new nodes and update data according to the
tree structure that has been explored before in a limited time,
and then return as long as the child node with the highest exploitation.
When making predictions,
you only need to select the node with the largest exploitation according to the Q value,
and find the next optimal node.
"""
computation_budget = len(AVAILABLE_CHOICES) * 3
# run as much as possible under the computation budget
for i in range(computation_budget):
# 1. find the best node to expand
expand_node = tree_policy(node)
if not expand_node:
# when it is None, it means that all nodes are added but no nodes meet the space limit
break
# 2. random get next action and get reward
reward = default_policy(expand_node)
# 3. update all passing nodes with reward
backpropagate(expand_node, reward)
# get the best next node
best_next_node = best_child(node, False)
return best_next_node
def MCTS(workload_info, atomic_choices, available_choices, storage_threshold, max_index_num):
global ATOMIC_CHOICES, STORAGE_THRESHOLD, WORKLOAD_INFO, AVAILABLE_CHOICES, MAX_INDEX_NUM
WORKLOAD_INFO = workload_info
AVAILABLE_CHOICES = available_choices
ATOMIC_CHOICES = atomic_choices
STORAGE_THRESHOLD = storage_threshold
MAX_INDEX_NUM = max_index_num if max_index_num else len(available_choices)
# create the initialized state and initialized node
init_state = State()
choices = copy.copy(available_choices)
init_state.set_available_choices(choices)
init_node = Node()
init_node.set_state(init_state)
current_node = init_node
opt_config = []
# set the rounds to play
for i in range(len(AVAILABLE_CHOICES)):
if current_node:
current_node = monte_carlo_tree_search(current_node)
if current_node:
opt_config = current_node.state.accumulation_choices
else:
break
return opt_config

View File

@ -539,13 +539,14 @@ class RnnModel():
keras.backend.clear_session()
set_session(self.session)
with self.graph.as_default():
# Judge whether the model needs to be initialized according to the changes of the model input and output dimensions.
feature, label, need_init = self.parse(filename)
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
epsilon = self.model_info.make_epsilon()
if need_init:
if need_init:# Cold start training
epoch_start = 0
self.model = self._build_model(epsilon)
else:
else:# Incremental training
epoch_start = int(self.model_info.last_epoch)
ratio_error = ratio_error_loss_wrapper(epsilon)
ratio_acc_2 = ratio_error_acc_wrapper(epsilon, 2)
@ -556,12 +557,16 @@ class RnnModel():
log_path = os.path.realpath(os.path.join(settings.PATH_LOG, self.model_info.model_name + '_log.json'))
if not os.path.exists(log_path):
os.mknod(log_path, mode=0o600)
# Training logging callback function
json_logging_callback = LossHistory(log_path, self.model_info.model_name, self.model_info.last_epoch)
# Data segmentation
X_train, X_val, y_train, y_val = \
train_test_split(feature, label, test_size=0.1)
# model training
self.model.fit(X_train, y_train, epochs=self.model_info.last_epoch,
batch_size=int(self.model_info.batch_size), validation_data=(X_val, y_val),
verbose=0, initial_epoch=epoch_start, callbacks=[json_logging_callback])
# save model
self.model.save(self.model_info.model_path)
val_pred = self.model.predict(X_val)
val_re = get_ratio_errors_general(val_pred, y_val, epsilon)

View File

@ -27,6 +27,7 @@ from . import AbstractModel
class TemplateModel(AbstractModel):
# Initialize algorithm parameters
def __init__(self, params):
super().__init__(params)
self.bias = 1e-5

View File

@ -173,11 +173,16 @@ def procedure_main(mode, db_info, config):
def rl_model(mode, env, config):
# Lazy loading. Because loading Tensorflow takes a long time.
from tuner.algorithms.rl_agent import RLAgent
# Start reinforcement learning agent class.
rl = RLAgent(env, alg=config['rl_algorithm'])
# The two modes of training and tuning correspond to different execution processes.
# The model needs to be trained before it can be used for tuning. The output of the training and tuning process is the list of parameters to be tuned. Because they share a set of models, it is required that the list of parameters to be tuned must be consistent in the two modes, otherwise exceptions with different output dimensions will be thrown.
if mode == 'train':
logging.warning('The list of tuned knobs in the training mode '
'based on the reinforcement learning algorithm must be the same as '
'that in the tuning mode. ')
# The key parameter is the maximum iteration round rl_ steps, theoretically, the longer the more accurate, but also more time-consuming.
# max_episode_steps is the maximum number of rounds in each round of reinforcement learning algorithm. In the implementation of x-tuner, this parameter is weakened, and it is generally default.
rl.fit(config['rl_steps'], nb_max_episode_steps=config['max_episode_steps'])
rl.save(config['rl_model_path'])
logging.info('Saved reinforcement learning model at %s.', config['rl_model_path'])
@ -200,6 +205,7 @@ def rl_model(mode, env, config):
def global_search(env, config):
method = config['gop_algorithm']
# Determine which algorithm to use.
if method == 'bayes':
from bayes_opt import BayesianOptimization
@ -207,6 +213,13 @@ def global_search(env, config):
pbound = {name: (0, 1) for name in env.db.ordered_knob_list}
def performance_function(**params):
"""
function name: performance_function
description: Define a black box function to adapt to the interface of the third-party library.
author: Li Xinran
date: 2022/8/4
contact: 19154068808
"""
if not len(params) == env.nb_actions:
raise AssertionError('Failed to check the input feature dimension.')
@ -222,12 +235,21 @@ def global_search(env, config):
pbounds=pbound
)
optimizer.maximize(
# The larger the maximum iteration round, the more accurate the result is, but it is also more time-consuming.
n_iter=config['max_iterations']
)
elif method == 'pso':
from tuner.algorithms.pso import Pso
def performance_function(v):
"""
function name: performance_function
description: Find the global minimum value.
note: Because the implementation of PSO algorithm is to find the global minimum value, take the opposite number here, so we need to change to take the global maximum value.
author: Li Xinran
date: 2022/8/4
contact: 19154068808
"""
s, r, d, _ = env.step(v)
return -r # Use -reward because PSO wishes to minimize.
@ -237,6 +259,7 @@ def global_search(env, config):
particle_nums=config['particle_nums'],
# max_iterations on the PSO indicates the maximum number of iterations per particle,
# so it must be divided by the number of particles to be consistent with Bayes.
# The larger the maximum iteration round is, the more accurate the result is, but also the more time-consuming.
max_iteration=config['max_iterations'] // config['particle_nums'],
x_min=0, x_max=1, max_vel=0.5
)

View File

@ -27,6 +27,7 @@ from collections.abc import Iterable
from collections import defaultdict
import index_advisor_workload as iaw
import mcts
def hash_any(obj):
@ -227,6 +228,32 @@ select * from student_range_part1 where credit=1;
class IndexAdvisorTester(unittest.TestCase):
def test_mcts(self):
storage_threshold = 12
index1 = iaw.IndexItem('public.a', 'col1', index_type='global')
index2 = iaw.IndexItem('public.b', 'col1', index_type='global')
index3 = iaw.IndexItem('public.c', 'col1', index_type='global')
index4 = iaw.IndexItem('public.d', 'col1', index_type='global')
atomic_index1 = iaw.IndexItem('public.a', 'col1', index_type='global')
atomic_index2 = iaw.IndexItem('public.b', 'col1', index_type='global')
atomic_index3 = iaw.IndexItem('public.c', 'col1', index_type='global')
atomic_index4 = iaw.IndexItem('public.d', 'col1', index_type='global')
atomic_index1.storage = 10
atomic_index2.storage = 4
atomic_index3.storage = 7
available_choices = [index1, index2, index3, index4]
atomic_choices = [[], [atomic_index2], [atomic_index1], [atomic_index3],
[atomic_index2, atomic_index3], [atomic_index4]]
query = iaw.QueryItem('select * from gia_01', 1)
query.cost_list = [10, 7, 5, 9, 4, 11]
workload_info = [query]
results = mcts.MCTS(workload_info, atomic_choices, available_choices, storage_threshold, 2)
self.assertLessEqual([index1.atomic_pos, index2.atomic_pos, index3.atomic_pos], [2, 1, 3])
self.assertSetEqual({results[0].table, results[1].table}, {'public.b', 'public.c'})
def test_get_indexable_columns(self):
tables = 'table1 table2 table2 table3 table3 table3'.split()
columns = 'col1,col2 col2 col3 col1,col2 col2,col3 col2,col5'.split()

View File

@ -43,47 +43,47 @@
#include "catalog/pg_proc_fn.h"
/*
* DefineAggregate
*
* "oldstyle" signals the old (pre-8.2) style where the aggregate input type
* is specified by a BASETYPE element in the parameters. Otherwise,
* "args" defines the input type(s).
DefineAggregate
"oldstyle"
BASETYPE
"args"
*/
void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
{
char* aggName = NULL;
Oid aggNamespace;
AclResult aclresult;
List* transfuncName = NIL;
List* finalfuncName = NIL;
List* sortoperatorName = NIL;
TypeName* baseType = NULL;
TypeName* transType = NULL;
char* initval = NULL;
char* aggName = NULL;//字符型指针,用于存储聚合函数的名称
Oid aggNamespace;//对象标识符类型,表示聚合函数所属的命名空间
AclResult aclresult;//aclresult 是一个枚举值,表示访问控制的结果
List* transfuncName = NIL;//链表类型,用于存储聚合函数的过渡函数的名称
List* finalfuncName = NIL;//用于存储聚合函数的最终函数的名称
List* sortoperatorName = NIL;//用于存储排序操作符的名称
TypeName* baseType = NULL;//一个指向 TypeName 结构的指针,表示聚合函数的基础类型
TypeName* transType = NULL;//也是TypeName结构的指针表示聚合函数的过渡类型
char* initval = NULL;//用于存储聚合函数的初始值
#ifdef PGXC
List* collectfuncName = NIL;
char* initcollect = NULL;
#endif
Oid* aggArgTypes = NULL;
int numArgs;
Oid transTypeId;
ListCell* pl = NULL;
//定义了用于收集数据的函数名称和初始值
Oid* aggArgTypes = NULL;//表示聚合函数的参数类型
int numArgs;//表示聚合函数的参数数量
Oid transTypeId;//表示聚合函数的过渡类型的标识符
ListCell* pl = NULL;//循环中间变量
/* attribute for ordered set aggregate */
//有序集合聚合函数的属性或特征
char aggKind = AGGKIND_NORMAL;
/* Convert list of names to a name and namespace */
//将一组名称列表转换为名称和命名空间
aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
/* Check we have creation rights in target namespace */
//检查是否具有在目标命名空间中创建的权限
aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace));
if (u_sess->attr.attr_sql.enforce_a_behavior) {
Oid proowner = InvalidOid;
/*
* isalter is true, change the owner of the objects as the owner of the
* namespace, if the owner of the namespce has the same name as the namescpe
isalter
*/
bool isalter = false;
proowner = GetUserIdFromNspId(aggNamespace);
@ -102,10 +102,8 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
foreach (pl, parameters) {
DefElem* defel = (DefElem*)lfirst(pl);
/*
* sfunc1, stype1, and initcond1 are accepted as obsolete spellings
* for sfunc, stype, initcond.
*/
//sfunc1、stype1 和 initcond1 被认为 是sfunc、stype 和 initcond 的过时拼写方式
if (pg_strcasecmp(defel->defname, "sfunc") == 0)
transfuncName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "sfunc1") == 0)
@ -124,6 +122,10 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
initval = defGetString(defel);
else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
initval = defGetString(defel);
/*
使 parameters DefElem
defname
*/
#ifdef PGXC
else if (pg_strcasecmp(defel->defname, "cfunc") == 0)
collectfuncName = defGetQualifiedName(defel);
@ -135,29 +137,23 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("aggregate attribute \"%s\" not recognized", defel->defname)));
}
/*
* make sure we have our required definitions
*/
//确保我们有所需的定义变量
if (transType == NULL)
ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate stype must be specified")));
if (transfuncName == NIL)
ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate sfunc must be specified")));
/*
* look up the aggregate's input datatype(s).
*/
if (oldstyle) {
/*
* Old style: use basetype parameter. This supports aggregates of
* zero or one input, with input type ANY meaning zero inputs.
*
* Historically we allowed the command to look like basetype = 'ANY'
* so we must do a case-insensitive comparison for the name ANY. Ugh.
Old style支持零个或一个输入的聚合函数 ANY
basetype = 'ANY' ANY
*/
if (baseType == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate input type must be specified")));
//baseType参数的值不能为空聚合函数的输入类型必须指定
if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0) {
numArgs = 0;
aggArgTypes = NULL;
@ -167,9 +163,6 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
aggArgTypes[0] = typenameTypeId(NULL, baseType);
}
} else {
/*
* New style: args is a list of TypeNames (possibly zero of 'em).
*/
ListCell* lc = NULL;
int i = 0;
@ -178,32 +171,31 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("basetype is redundant with aggregate input type specification")));
/* Given ordered set aggregate with no direct args, aggr_args variable is modified in gram.y.
So the parse of aggr_args should be changed. See gram.y for detail. */
/* 在使用没有直接参数的有序集合聚合函数时aggr_args 变量会在 gram.y 文件中进行修改。
aggr_args
*/
numArgs = list_length((List*)linitial(args));
// 获取传递给聚合函数的参数数量
aggArgTypes = (Oid*)palloc(sizeof(Oid) * numArgs);
foreach (lc, (List*)linitial(args)) {
// 为聚合函数的参数类型标识符分配内存
foreach (lc, (List*)linitial(args))
{//// 遍历传递给聚合函数的参数列表
TypeName* curTypeName = (TypeName*)lfirst(lc);
aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
// 获取当前参数的类型标识符并存储到数组中
}
/* Set aggKind to AGGKIND_ORDERED_SET if second arg of aggr_args is 0. */
if (intVal(lsecond(args)) == 0) {
aggKind = AGGKIND_ORDERED_SET;
}
}
/*
* look up the aggregate's transtype.
*
* transtype can't be a pseudo-type, since we need to be able to store
* values of the transtype. However, we can allow polymorphic transtype
* in some cases (AggregateCreate will check). Also, we allow "internal"
* for functions that want to pass pointers to private data structures;
* but allow that only to superusers, since you could crash the system (or
* worse) by connecting up incompatible internal-using functions in an
* aggregate.
transtype
transtype transtype
transtypeAggregateCreate
使 "internal"
*/
transTypeId = typenameTypeId(NULL, transType);
if (get_typtype(transTypeId) == TYPTYPE_PSEUDO && !IsPolymorphicType(transTypeId)
@ -212,45 +204,44 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("aggregate transition data type cannot be %s", format_type_be(transTypeId))));
}
/*
* Most of the argument-checking is done inside of AggregateCreate
*/
AggregateCreate(aggName, /* aggregate name */
aggNamespace, /* namespace */
aggKind, /* agg kind */
aggArgTypes, /* input data type(s) */
numArgs,
transfuncName, /* step function name */
//大部分的参数检查都在 AggregateCreate 函数内部完成
AggregateCreate(aggName, /* 聚合函数的名称 */
aggNamespace, /* 命名空间 */
aggKind, /* 聚合种类 */
aggArgTypes, /* 输入类型种类 */
numArgs, /* 输入参数数量 */
transfuncName, /* 过渡函数名称 */
#ifdef PGXC
collectfuncName, /* collect function name */
collectfuncName, /* 收集函数名称 */
#endif
finalfuncName, /* final function name */
sortoperatorName, /* sort operator name */
transTypeId, /* transition data type */
finalfuncName, /* 最终函数名称 */
sortoperatorName, /* 排序操作符名称 */
transTypeId, /* 过渡数据类型 */
#ifdef PGXC
initval, /* initial condition */
initcollect); /* initial condition for collection function */
initval, /* 初始条件 */
initcollect); /* 收集函数的初始条件 */
#else
initval); /* initial condition */
initval); /* 初始条件 */
#endif
}
void RenameAggregate(List* name, List* args, const char* newname)
{
Oid procOid;
Oid namespaceOid;
HeapTuple tup;
Form_pg_proc procForm;
Relation rel;
AclResult aclresult;
bool isNull = false;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
Oid procOid; /* 聚合函数的 Oid */
Oid namespaceOid; /* 命名空间的 Oid */
HeapTuple tup; /* HeapTuple 结构,用于存储元组 */
Form_pg_proc procForm; /* pg_proc 表中的元组结构 */
Relation rel; /* pg_proc 表的 Relation 对象 */
AclResult aclresult; /* AclResult 枚举,用于存储访问控制的结果 */
bool isNull = false; /* 布尔变量,表示是否为 NULL */
rel = heap_open(ProcedureRelationId, RowExclusiveLock); /* 打开 pg_proc 表 */
/* Look up function and make sure it's an aggregate */
/* 查询函数并确保他是聚合的*/
procOid = LookupAggNameTypeNames(name, args, false);
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
if (!HeapTupleIsValid(tup)) /* 如果运行正常这是不会出现的 */
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", procOid)));
procForm = (Form_pg_proc)GETSTRUCT(tup);
@ -265,13 +256,14 @@ void RenameAggregate(List* name, List* args, const char* newname)
#ifndef ENABLE_MULTIPLE_NODES
Datum allargtypes = ProcedureGetAllArgTypes(tup, &isNull);
Datum argmodes = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proargmodes, &isNull);
/* make sure the new name doesn't exist */
// 在系统缓存中搜索具有相同参数的函数
if (SearchSysCacheForProcAllArgs(
CStringGetDatum(newname),
allargtypes,
ObjectIdGetDatum(namespaceOid),
ObjectIdGetDatum(packageoid),
argmodes))
// 如果找到相同参数的函数,报告错误
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function %s already exists in schema \"%s\"",
@ -288,16 +280,16 @@ void RenameAggregate(List* name, List* args, const char* newname)
funcname_signature_string(newname, procForm->pronargs, NIL, proargs->values),
get_namespace_name(namespaceOid))));
#endif
/* must be owner */
// 检查当前用户是否是聚合函数的所有者,如果不是,则报告错误
if (!pg_proc_ownercheck(procOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(name));
/* must have CREATE privilege on namespace */
// 检查当前用户是否有在命名空间中创建对象的权限
aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid));
/* rename */
//重命名
(void)namestrcpy(&(((Form_pg_proc)GETSTRUCT(tup))->proname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
@ -307,15 +299,15 @@ void RenameAggregate(List* name, List* args, const char* newname)
}
/*
* Change aggregate owner
*/
void AlterAggregateOwner(List* name, List* args, Oid newOwnerId)
{
Oid procOid;
/* Look up function and make sure it's an aggregate */
/* 查找函数并确保它是一个聚合函数。 */
procOid = LookupAggNameTypeNames(name, args, false);
/* The rest is just like a function */
/* 其余部分与普通函数类似 */
AlterFunctionOwner_oid(procOid, newOwnerId);
}
}

View File

@ -90,7 +90,10 @@ static void DropExtensionInListIsSupported(List* objname)
}
}
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXTENSION is not yet supported.")));
/* Enable DROP operation of the above objects during inplace upgrade or support_extended_features is true */
if (!u_sess->attr.attr_common.IsInplaceUpgrade && !g_instance.attr.attr_common.support_extended_features) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXTENSION is not yet supported.")));
}
}
/*

View File

@ -1175,7 +1175,7 @@ void CreateExtension(CreateExtensionStmt* stmt)
FEATURE_NOT_PUBLIC_ERROR("EXTENSION is not yet supported.");
}
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
if (pg_strcasecmp(stmt->extname, "dolphin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
ereport(ERROR,
(errmsg("please create extension \"%s\" with B type DBCOMPATIBILITY", stmt->extname)));
}
@ -1418,8 +1418,8 @@ void CreateExtension(CreateExtensionStmt* stmt)
u_sess->exec_cxt.extension_is_valid = true;
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0) {
u_sess->attr.attr_sql.b_sql_plugin = true;
if (pg_strcasecmp(stmt->extname, "dolphin") == 0) {
u_sess->attr.attr_sql.dolphin = true;
}
/*

View File

@ -23175,7 +23175,7 @@ static void checkValidationForExchangeTable(Relation partTableRel, Relation ordT
int2 bucketId = InvalidBktId;
// get right partition oid for the tuple
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple) tuple);
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple)tuple, true);
searchFakeReationForPartitionOid(
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
@ -24797,7 +24797,8 @@ static Oid AddTemporaryPartitionForAlterPartitions(const AlterTableCmd* cmd, Rel
destPartOid = AddTemporaryHashPartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
break;
}
case PART_TYPE_RANGE: {
case PART_TYPE_RANGE:
case PART_TYPE_INTERVAL: {
destPartOid = AddTemporaryRangePartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
break;
}
@ -25098,11 +25099,11 @@ static void readTuplesAndInsertInternal(Relation tempTableRel, Relation partTabl
/* tableam_tops_copy_tuple is not ready so we add UStore hack path */
copyTuple = tableam_tops_copy_tuple(tuple);
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple);
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple, true);
searchFakeReationForPartitionOid(
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
if (RelationIsSubPartitioned(partTableRel)) {
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple);
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple, true);
searchFakeReationForPartitionOid(partRelHTAB, CurrentMemoryContext, partRel, targetPartOid, subPartRel,
subPart, RowExclusiveLock);
partRel = subPartRel;

6
src/gausskernel/optimizer/commands/user.cpp Executable file → Normal file
View File

@ -5911,6 +5911,7 @@ Datum calculate_encrypted_combined_password(const char* password, const char* ro
errno_t rc = EOK;
/* For PG ecological compatibility, we stored both sha256 and md5 password. */
/* the encrypted method of sha256 */
if (!pg_sha256_encrypt(password,
salt_string,
strlen(salt_string),
@ -5921,7 +5922,7 @@ Datum calculate_encrypted_combined_password(const char* password, const char* ro
securec_check(rc, "\0", "\0");
ereport(ERROR, (errcode(ERRCODE_INVALID_PASSWORD), errmsg("first stage encryption password failed")));
}
/* the encrypted method of md5 */
if (!pg_md5_encrypt(password, rolname, strlen(rolname), encrypted_md5_password)) {
rc = memset_s(encrypted_md5_password, MD5_PASSWD_LEN + 1, 0, MD5_PASSWD_LEN + 1);
securec_check(rc, "\0", "\0");
@ -6052,6 +6053,7 @@ static Datum gs_calculate_encrypted_sm3_password(const char* password, const cha
Datum calculate_encrypted_password(bool is_encrypted, const char* password, const char* rolname,
const char* salt_string)
{
/* If the password is '\0' or not exist */
if (password == NULL || password[0] == '\0') {
ereport(ERROR, (errcode(ERRCODE_INVALID_PASSWORD), errmsg("The password could not be NULL.")));
}
@ -6059,6 +6061,7 @@ Datum calculate_encrypted_password(bool is_encrypted, const char* password, cons
char encrypted_md5_password[MD5_PASSWD_LEN + 1] = {0};
Datum datum_value;
/* If the password has encrypted */
if (!is_encrypted || isPWDENCRYPTED(password)) {
return CStringGetTextDatum(password);
}
@ -6068,6 +6071,7 @@ Datum calculate_encrypted_password(bool is_encrypted, const char* password, cons
* if Password_encryption_type is 0, the encrypted password is md5.
* if Password_encryption_type is 1, the encrypted password is sha256 + md5.
* if Password_encryption_type is 2, the encrypted password is sha256.
* if Password_encryption_type is 3, the encrypted password is SM3.
*/
if (u_sess->attr.attr_security.Password_encryption_type == 0) {
if (!pg_md5_encrypt(password, rolname, strlen(rolname), encrypted_md5_password)) {

View File

@ -1181,6 +1181,17 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn
return jtnode;
}
/*
* We must flatten any join alias Vars in the subquery's targetlist,
* because pulling up the subquery's subqueries might have changed their
* expansions into arbitrary expressions, which could affect
* pullup_replace_vars' decisions about whether PlaceHolderVar wrappers
* are needed for tlist entries. (Likely it'd be better to do
* flatten_join_alias_vars on the whole query tree at some earlier stage,
* maybe even in the rewriter; but for now let's just fix this case here.)
*/
subquery->targetList = (List *) flatten_join_alias_vars(subroot, (Node *) subquery->targetList);
/*
* Adjust level-0 varnos in subquery so that we can append its rangetable
* to upper query's. We have to fix the subquery's append_rel_list as

File diff suppressed because it is too large Load Diff

View File

@ -38,63 +38,102 @@
#include "utils/plancache.h"
#include "utils/syscache.h"
/*
*
*
*
* msg SharedInvalidationMessage
*
*
* true false
*/
bool GlobalPlanCache::MsgCheck(const SharedInvalidationMessage *msg)
{
if (msg->id >= 0) {
// 如果消息的 id 大于等于 0
if (msg->cc.id == PROCOID || msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) {
// 如果消息的 cc.id 是 PROCOID、NAMESPACEOID、OPOID 或 AMOPOPID 中的任何一个,返回 true
return true;
}
} else if (msg->id == SHAREDINVALRELCACHE_ID || msg->id == SHAREDINVALPARTCACHE_ID) {
// 如果消息的 id 是 SHAREDINVALRELCACHE_ID 或 SHAREDINVALPARTCACHE_ID返回 true
return true;
}
// 如果以上条件都不满足,返回 false
return false;
}
bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource* plansource, int tot, const int *idx, const SharedInvalidationMessage *msgs)
/*
*
*
*
* plansource CachedPlanSource
* tot
* idx
* msgs SharedInvalidationMessage
*
*
* true false
*/
bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource *plansource, int tot, const int *idx,
const SharedInvalidationMessage *msgs)
{
// 获取计划源对象所属的数据库 ID
Oid database_id = plansource->gpc.key->env.plainenv.database_id;
for (int j = 0; j < tot; j++) {
const SharedInvalidationMessage *msg = &msgs[idx[j]];
// 如果计划源对象具有原始解析树,并且原始解析树的类型是 TransactionStmt则跳过该消息的处理
if ((plansource)->raw_parse_tree && IsA((plansource)->raw_parse_tree, TransactionStmt))
continue;
if (msg->id >= 0) {
// 如果消息的 id 大于等于 0
if (msg->cc.dbId == database_id || msg->cc.dbId == InvalidOid) {
if (msg->cc.id == PROCOID) {
// 检查计划缓存项的失效项依赖性,并更新
CheckInvalItemDependency(plansource, msg->cc.id, msg->cc.hashValue);
} else if (msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) {
// 重置计划缓存项
ResetPlanCache(plansource);
}
}
} else if (msg->id == SHAREDINVALRELCACHE_ID) {
if (msg->rc.dbId == database_id || msg->rc.dbId == InvalidOid)
{
// 如果消息的 id 是 SHAREDINVALRELCACHE_ID
if (msg->rc.dbId == database_id || msg->rc.dbId == InvalidOid) {
// 检查计划缓存项与关系依赖性,并更新
CheckRelDependency(plansource, msg->rc.relId);
}
} else if (msg->id == SHAREDINVALPARTCACHE_ID) {
// 如果消息的 id 是 SHAREDINVALPARTCACHE_ID
if (msg->pc.dbId == database_id || msg->pc.dbId == InvalidOid) {
// 检查计划缓存项与分区依赖性,并更新
CheckRelDependency(plansource, msg->pc.partId);
}
}
// 如果计划源对象需要丢弃共享 GPC则返回 true
if (plansource->gpc.status.NeedDropSharedGPC()) {
return true;
}
}
// 如果不需要丢弃计划缓存项,返回 false
return false;
}
/*
*
*
*
* msgs SharedInvalidationMessage
* n
*/
void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
{
int *idx = (int *)palloc0(n * sizeof(int));
int tot = 0;
// 分配并初始化一个索引数组
int *idx = (int *)palloc0(n * sizeof(int));
int tot = 0;
// 遍历失效消息数组,筛选出需要处理的消息
for (int i = 0; i < n; i++) {
const SharedInvalidationMessage *msg = &msgs[i];
@ -103,20 +142,21 @@ void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
}
}
// 如果没有需要处理的消息,释放索引数组并返回
if (tot == 0) {
pfree_ext(idx);
return ;
return;
}
/* Go through each bucket in the GPC HTAB and do some invalidation depending on the GPCInvalInfo we got.*/
for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id ++) {
for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id++) {
/* Ok so bucket is not empty. Get the bucket S-lock so we can iterate through it. */
int lock_id = m_array[bucket_id].lockId;
LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE);
MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context);
/* Check the number of entries in the bucket again.
* GPC Eviction might have removed the last entry while we were waiting for the shared lock. */
/* Check the number of entries in the bucket again.
* GPC Eviction might have removed the last entry while we were waiting for the shared lock. */
int bucketEntriesCount = m_array[bucket_id].count;
if (0 == bucketEntriesCount) {
MemoryContextSwitchTo(oldcontext);
@ -129,7 +169,7 @@ void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
GPCEntry *entry = NULL;
while ((entry = (GPCEntry *)hash_seq_search(&hash_seq)) != NULL) {
Assert (entry->val.plansource != NULL);
Assert(entry->val.plansource != NULL);
/* for standby mode, Invalid Msg send by xlog thread, but xlog thread didn't set db id into MyDatabaseId.
So we need check each plan's db id by gpc'key in NeedDropEntryByLocalMsg latter */
if (pmState == PM_RUN &&
@ -138,7 +178,7 @@ void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
}
/* Atomic read the number of CachedEnvironment in this entry */
if(NeedDropEntryByLocalMsg(entry->val.plansource, tot, idx, msgs)) {
if (NeedDropEntryByLocalMsg(entry->val.plansource, tot, idx, msgs)) {
RemoveEntry(bucket_id, entry);
}
}
@ -147,5 +187,6 @@ void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
LWLockRelease(GetMainLWLockByIndex(lock_id));
}
// 释放索引数组
pfree_ext(idx);
}

View File

@ -23,10 +23,10 @@
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/dbe_scheduler.h"
#include "postgres.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/dbe_scheduler.h"
/*
* repeat_interval = frequency_clause
@ -53,7 +53,7 @@
*/
/* Initialize calendaring fields */
static bool IsLegalIntervalStr(const char* str, bool numeric_only = false);
static bool IsLegalIntervalStr(const char *str, bool numeric_only = false);
static char *get_calendar_clause_val(char **tokens, const char *clause, bool numeric_only = false);
static char **tokenize_str(char *src, const char *delims, int fields);
static int validate_field_names(char **toks);
@ -66,9 +66,9 @@ static bool get_calendar_freqency(Calendar calendar, char **tokens);
static void get_calendar_n_interval(Calendar calendar, char **tokens);
static char *get_calendar_bymonth_val(Calendar calendar, char **tokens);
static void get_calendar_bymonth(Calendar calendar, char **tokens);
static void get_calendar_byweekno(Calendar calendar, char **tokens); /* unsupported */
static void get_calendar_byweekno(Calendar calendar, char **tokens); /* unsupported */
static void get_calendar_byyearday(Calendar calendar, char **tokens); /* unsupported */
static void get_calendar_bydate(Calendar calendar, char **tokens); /* unsupported */
static void get_calendar_bydate(Calendar calendar, char **tokens); /* unsupported */
static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens);
static void get_calendar_bymonthday(Calendar calendar, char **tokens);
static void get_calendar_byday(Calendar calendar, char **tokens); /* unsupported */
@ -97,10 +97,10 @@ static bool find_nearest_calendar_time(Calendar calendar, TimestampTz *timeline,
/* Calendaring Interval Calculator */
static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, TimestampTz *timeline);
static void evaluate_calendar_bymonth(Calendar calendar, TimestampTz *timeline, int *cnt);
static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */
static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */
static void evaluate_calendar_byyearday(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */
static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timeline, int *cnt);
static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */
static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */
static void evaluate_calendar_byminute(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */
static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */
static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, TimestampTz *sub_timeline,
@ -115,7 +115,7 @@ static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz sta
* @return true legal
* @return false illegal
*/
static bool IsLegalIntervalStr(const char* str, bool numeric_only)
static bool IsLegalIntervalStr(const char *str, bool numeric_only)
{
size_t NBytes = (unsigned int)strlen(str);
if (NBytes > (MAX_CALENDAR_FIELD_LEN)) {
@ -141,7 +141,6 @@ static bool IsLegalIntervalStr(const char* str, bool numeric_only)
return true;
}
/*
* @brief get_calendar_clause
* Get calendar clause and return its value;
@ -154,7 +153,7 @@ static char *get_calendar_clause_val(char **tokens, const char *clause, bool num
char *val = NULL;
for (int i = 0; i < MAX_CALENDAR_FIELDS; i += 2) {
if (tokens[i] != NULL && pg_strcasecmp(tokens[i], clause) == 0) {
val = tokens[i + 1]; /* get clause's value */
val = tokens[i + 1]; /* get clause's value */
break;
}
}
@ -163,10 +162,10 @@ static char *get_calendar_clause_val(char **tokens, const char *clause, bool num
}
if (!IsLegalIntervalStr(val, numeric_only)) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("Invalid value string for clause \'%s\'", clause), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("Invalid value string for clause \'%s\'", clause), errcause("N/A"),
erraction("Please modify the calendaring string.")));
}
return val;
}
@ -205,10 +204,10 @@ static bool get_calendar_freqency(Calendar calendar, char **tokens)
calendar->frequency = SECONDLY;
} else {
pfree_ext(tokens);
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("Invalid frequency value \'%s\'.", val), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("Invalid frequency value \'%s\'.", val), errcause("N/A"),
erraction("Please modify the calendaring string.")));
}
return true;
}
@ -223,7 +222,7 @@ static bool get_calendar_freqency(Calendar calendar, char **tokens)
*/
static void get_calendar_n_interval(Calendar calendar, char **tokens)
{
calendar->interval = 1; /* we ALWAYS set interval to 1 */
calendar->interval = 1; /* we ALWAYS set interval to 1 */
char *val = get_calendar_clause_val(tokens, "interval", true);
if (val == NULL) {
return;
@ -232,10 +231,10 @@ static void get_calendar_n_interval(Calendar calendar, char **tokens)
int num = atoi(val);
if (num < 1 || num > MAX_CALENDAR_INTERVAL_NUM) {
pfree_ext(tokens);
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("Interval \'%d\' not in range [1, 99].", num), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("Interval \'%d\' not in range [1, 99].", num), errcause("N/A"),
erraction("Please modify the calendaring string.")));
}
calendar->interval = num;
}
@ -306,10 +305,10 @@ static void get_calendar_bymonth(Calendar calendar, char **tokens)
}
}
if (month == 0) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("Invalid month token \'%s\'.", val), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."), errdetail("Invalid month token \'%s\'.", val),
errcause("N/A"), erraction("Please modify the calendaring string.")));
}
} else {
/* numeric in */
@ -352,10 +351,10 @@ static void get_calendar_byweekno(Calendar calendar, char **tokens)
{
char *val = get_calendar_clause_val(tokens, "byweekno", true);
if (val != NULL) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("BYWEEKNO clause is currently unsupported."), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("BYWEEKNO clause is currently unsupported."), errcause("N/A"),
erraction("Please modify the calendaring string.")));
}
}
@ -373,10 +372,10 @@ static void get_calendar_byyearday(Calendar calendar, char **tokens)
{
char *val = get_calendar_clause_val(tokens, "byyearday", true);
if (val != NULL) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("BYYEARDAY clause is currently unsupported."), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("BYYEARDAY clause is currently unsupported."), errcause("N/A"),
erraction("Please modify the calendaring string.")));
}
}
@ -393,16 +392,26 @@ static void get_calendar_bydate(Calendar calendar, char **tokens)
{
char *val = get_calendar_clause_val(tokens, "byweekno", true);
if (val != NULL) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("BYDATE clause is currently unsupported."), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("BYDATE clause is currently unsupported."), errcause("N/A"),
erraction("Please modify the calendaring string.")));
}
}
/*
* "byhour"
*
*
* calendar
* tokens
*
*
* "byhour" NULL
*/
static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens)
{
// 获取 "byhour" 子句的值
char *val = get_calendar_clause_val(tokens, "bymonthday", true);
if (val != NULL) {
/* apply bymonthday rule if bymonthday is specified */
@ -421,10 +430,10 @@ static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens)
calendar->monthday_len = 0;
}
/* We cannot optimize any further since monthday/yearday are not perfectly periodic */
// 没有指定 "byhour" 子句,返回 NULL
return NULL;
}
/*
* @brief get_calendar_bymonth
* Get bymonthday_clause.
@ -449,10 +458,10 @@ static void get_calendar_bymonthday(Calendar calendar, char **tokens)
while (tok != NULL) {
int monthday = atoi(tok);
if (monthday < -(DAYS_PER_MONTH + 1) || monthday > (DAYS_PER_MONTH + 1) || monthday == 0) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("Invalid monthday \'%d\'.", monthday), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."), errdetail("Invalid monthday \'%d\'.", monthday),
errcause("N/A"), erraction("Please modify the calendaring string.")));
}
tok = strtok_s(NULL, ",", &context);
@ -505,14 +514,13 @@ static void get_calendar_byday(Calendar calendar, char **tokens)
{
char *val = get_calendar_clause_val(tokens, "byday", true);
if (val != NULL) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("BYDAY clause is currently unsupported."), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("BYDAY clause is currently unsupported."), errcause("N/A"),
erraction("Please modify the calendaring string.")));
}
}
static char *get_calendar_byhour_val(Calendar calendar, char **tokens)
{
char *val = get_calendar_clause_val(tokens, "byhour", true);
@ -568,10 +576,10 @@ static void get_calendar_byhour(Calendar calendar, char **tokens)
while (tok != NULL) {
int hour = atoi(tok);
if (hour < 0 || hour >= HOURS_PER_DAY) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("Invalid time \'%d\' o\' clock.", hour), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."), errdetail("Invalid time \'%d\' o\' clock.", hour),
errcause("N/A"), erraction("Please modify the calendaring string.")));
}
tok = strtok_s(NULL, ",", &context);
@ -590,9 +598,19 @@ static void get_calendar_byhour(Calendar calendar, char **tokens)
calendar->byfields |= INTERVAL_BYHOUR;
}
/*
* "byminute"
*
*
* calendar
* tokens
*
*
* "byminute" NULL
*/
static char *get_calendar_byminute_val(Calendar calendar, char **tokens)
{
// 获取 "byminute" 子句的值
char *val = get_calendar_clause_val(tokens, "byminute", true);
if (val != NULL) {
/* apply byminute rule if byminute is specified */
@ -621,6 +639,7 @@ static char *get_calendar_byminute_val(Calendar calendar, char **tokens)
calendar->minute_len *= -1;
calendar->byminute[0] = mod;
}
// 没有指定 "byminute" 子句,返回 NULL
return NULL;
}
@ -667,15 +686,26 @@ static void get_calendar_byminute(Calendar calendar, char **tokens)
calendar->time_depth *= (calendar->minute_len == 0) ? 1 : calendar->minute_len;
calendar->byfields |= INTERVAL_BYMINUTE;
}
/*
* "bysecond"
*
*
* calendar
* tokens
*
*
* "bysecond" NULL
*/
static char *get_calendar_bysecond_val(Calendar calendar, char **tokens)
{
// 获取 "bysecond" 子句的值
char *val = get_calendar_clause_val(tokens, "bysecond", true);
if (val != NULL) {
/* apply bysecond rule if bysecond is specified */
return val;
}
// 断言,确保频率不高于 SECONDLY
Assert(calendar->frequency <= SECONDLY);
/* Even higher frequency is unavailable */
if (calendar->frequency < SECONDLY) {
@ -693,6 +723,7 @@ static char *get_calendar_bysecond_val(Calendar calendar, char **tokens)
calendar->second_len *= -1;
calendar->bysecond[0] = mod;
}
// 没有指定 "bysecond" 子句,返回 NULL
return NULL;
}
@ -740,7 +771,6 @@ static void get_calendar_bysecond(Calendar calendar, char **tokens)
calendar->byfields |= INTERVAL_BYSECOND;
}
/*
* @brief tokenize_str
* Tokenize string with given delimiters.
@ -766,13 +796,21 @@ static char **tokenize_str(char *src, const char *delims, int fields)
}
return tokens;
}
/*
*
*
*
* toks
*
*
* -1
*/
static int validate_field_names(char **toks)
{
bool valid = false;
const int name_pos_step = 2;
const char *supported_fields[SUPPORTED_FIELDS] = {"freq", "interval", "bymonth", "bymonthday", "byhour",
"byminute", "bysecond"};
const char *supported_fields[SUPPORTED_FIELDS] = {"freq", "interval", "bymonth", "bymonthday",
"byhour", "byminute", "bysecond"};
bool fields_used[SUPPORTED_FIELDS] = {0};
for (int i = 0; i < MAX_CALENDAR_FIELDS; i += name_pos_step) {
for (int j = 0; j < SUPPORTED_FIELDS; j++) {
@ -791,7 +829,7 @@ static int validate_field_names(char **toks)
fields_used[j] = true;
valid = true;
}
break; /* here is way pass guarding condition, break it */
break; /* here is way pass guarding condition, break it */
}
if (!valid) {
return i;
@ -815,19 +853,18 @@ Calendar interpret_calendar_interval(char *calendar_str)
/* Make token lists */
char **str_toks = tokenize_str(calendar_str, " =;", MAX_CALENDAR_FIELDS);
if (str_toks == NULL) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("Unable to parse calendaring string."),
errcause("Calendaring string is too long/invalid"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("Unable to parse calendaring string."), errcause("Calendaring string is too long/invalid"),
erraction("Please modify the calendaring string.")));
}
int pos = validate_field_names(str_toks);
if (pos >= 0) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
errmsg("Fail to evaluate calendaring string."),
errdetail("Incorrect/duplicate clause name '%s'.", str_toks[pos]), errcause("N/A"),
erraction("Please modify the calendaring string.")));
ereport(ERROR,
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
errdetail("Incorrect/duplicate clause name '%s'.", str_toks[pos]), errcause("N/A"),
erraction("Please modify the calendaring string.")));
}
/* Make Calendar */
@ -1028,7 +1065,7 @@ static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timelin
*cnt = 0;
int tz = 0;
fsec_t fsec;
struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */
struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */
copy_calendar_dates(timeline, calendar->monthday_len, chunk);
for (int i = 0; i < calendar->monthday_len; i++) {
if (calendar->bymonthday[i] < 0) {
@ -1222,8 +1259,7 @@ static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_da
errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."),
errcause("N/A"), erraction("Please modify the calendaring string.")));
}
Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"),
PointerGetDatum(period));
Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"), PointerGetDatum(period));
int pace = (int)DatumGetFloat8(pace_datum);
pfree_ext(period);
@ -1237,8 +1273,8 @@ static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_da
Interval *ff_span = get_calendar_period(calendar, num_of_periods);
if (ff_span == NULL) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."),
errcause("N/A"), erraction("Please modify the calendaring string.")));
errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."),
errcause("N/A"), erraction("Please modify the calendaring string.")));
}
new_start_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, ff_span));
pfree_ext(ff_span);
@ -1397,7 +1433,7 @@ static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, Ti
}
fsec_t fsec;
struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */
struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */
int tz;
if (timestamp2tm(base_date, &tz, tm, &fsec, NULL, NULL) != 0) {
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@ -1431,7 +1467,6 @@ static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_
return base_date;
}
/*
* @brief evaluate_calendar_interval
* Calculate next date base on start date.
@ -1516,9 +1551,9 @@ Datum evaluate_repeat_interval(Datum calendar_in, Datum start_date, Datum date_a
*/
Datum evaluate_calendar_string_internal(PG_FUNCTION_ARGS)
{
Datum string = PG_GETARG_DATUM(0); /* calendar string */
Datum start_date = PG_GETARG_DATUM(1); /* start date */
Datum date_after = PG_GETARG_DATUM(2); /* return date after */
Datum string = PG_GETARG_DATUM(0); /* calendar string */
Datum start_date = PG_GETARG_DATUM(1); /* start date */
Datum date_after = PG_GETARG_DATUM(2); /* return date after */
Datum new_next_date = evaluate_repeat_interval(string, start_date, date_after);
PG_RETURN_DATUM(new_next_date);
}

View File

@ -61,60 +61,63 @@ THR_LOCAL bool IsInitdb = false;
size_t mmap_threshold = (size_t)0xffffffff;
const char* progname = NULL;
const char *progname = NULL;
static void startup_hacks(const char* progname);
static void help(const char* progname);
static void check_root(const char* progname);
static char* get_current_username(const char* progname);
static void startup_hacks(const char *progname);
static void help(const char *progname);
static void check_root(const char *progname);
static char *get_current_username(const char *progname);
static void syscall_lock_init(void);
extern int encrypte_main(int argc, char* const argv[]);
extern int encrypte_main(int argc, char *const argv[]);
/*
* Any openGauss server process begins execution here.
*/
int main(int argc, char* argv[])
/*
* GaussDB
*
*
* argc
* argv
*
*
* GaussDB
* initdbPostmasterGucInfoMain等
* Postmaster
*/
int main(int argc, char *argv[])
{
char* mmap_env = NULL;
syscall_lock_init();
char *mmap_env = NULL;
syscall_lock_init(); // 初始化系统调用锁
mmap_env = gs_getenv_r("GAUSS_MMAP_THRESHOLD");
if (mmap_env != NULL) {
check_backend_env(mmap_env);
mmap_threshold = (size_t)atol(mmap_env);
check_backend_env(mmap_env); // 检查后端环境变量
mmap_threshold = (size_t)atol(mmap_env); // 设置内存映射阈值
}
knl_instance_init();
knl_instance_init(); // 初始化内核实例
// 创建增量检查点上下文
g_instance.increCheckPoint_context = AllocSetContextCreate(
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE),
"IncreCheckPointContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), "IncreCheckPointContext", ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
g_instance.account_context = AllocSetContextCreate(g_instance.instance_context,
"StandbyAccontContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
g_instance.comm_cxt.comm_global_mem_cxt = AllocSetContextCreate(g_instance.instance_context,
"CommunnicatorGlobalMemoryContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
// 创建帐户上下文
g_instance.account_context =
AllocSetContextCreate(g_instance.instance_context, "StandbyAccontContext", ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
g_instance.builtin_proc_context = AllocSetContextCreate(g_instance.instance_context,
"builtin_procGlobalMemoryContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
// 创建通信全局内存上下文
g_instance.comm_cxt.comm_global_mem_cxt =
AllocSetContextCreate(g_instance.instance_context, "CommunnicatorGlobalMemoryContext", ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
// 创建内置过程全局内存上下文
g_instance.builtin_proc_context =
AllocSetContextCreate(g_instance.instance_context, "builtin_procGlobalMemoryContext", ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
/*
* Fire up essential subsystems: error and memory management
*
@ -126,8 +129,9 @@ int main(int argc, char* argv[])
PmTopMemoryContext = t_thrd.top_mem_cxt;
knl_thread_init(MASTER_THREAD);
knl_thread_init(MASTER_THREAD); // 初始化内核线程
// 创建伪会话上下文
t_thrd.fake_session = create_session_context(t_thrd.top_mem_cxt, 0);
t_thrd.fake_session->status = KNL_SESS_FAKE;
@ -137,19 +141,21 @@ int main(int argc, char* argv[])
MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT));
progname = get_progname(argv[0]);
progname = get_progname(argv[0]); // 获取程序名
/*
* Platform-specific startup hacks
*/
// 平台特定的启动操作
startup_hacks(progname);
/* if gaussdb's name is gs_encrypt, so run in encrypte_main() */
// 如果程序名是 "gs_encrypt",则执行 encrypte_main() 函数
if (!strcmp(progname, "gs_encrypt")) {
return encrypte_main(argc, argv);
}
init_plog_global_mem();
init_plog_global_mem(); // 初始化全局日志内存
/*
* Remember the physical location of the initially given argv[] array for
@ -191,7 +197,7 @@ int main(int argc, char* argv[])
* environment. If there is nothing there we fall back on the codepage.
*/
{
char* env_locale = NULL;
char *env_locale = NULL;
if ((env_locale = gs_getenv_r("LC_COLLATE")) != NULL) {
check_backend_env(env_locale);
@ -258,8 +264,8 @@ int main(int argc, char* argv[])
pgwin32_signal_initialize();
#endif
t_thrd.mem_cxt.gs_signal_mem_cxt = AllocSetContextCreate(
t_thrd.top_mem_cxt, "gs_signal", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
t_thrd.mem_cxt.gs_signal_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, "gs_signal", ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
if (NULL == t_thrd.mem_cxt.gs_signal_mem_cxt) {
ereport(LOG, (errmsg("could not start a new thread, because of no enough system resource. ")));
proc_exit(1);
@ -310,7 +316,7 @@ int main(int argc, char* argv[])
* is too brain-dead to provide a standard C execution environment
* without help. Avoid adding more here, if you can.
*/
static void startup_hacks(const char* progname)
static void startup_hacks(const char *progname)
{
/*
* On some platforms, unaligned memory accesses result in a kernel trap;
@ -363,7 +369,8 @@ static void startup_hacks(const char* progname)
* Help display should match the options accepted by PostmasterMain()
* and PostgresMain().
*/
static void help(const char* progname)
// 此函数用于对它支持的命令行选项和用法进行说明。这有助于理解代码的功能和如何使用这些选项来运行程序。
static void help(const char *progname)
{
printf(_("%s is the gaussdb server.\n\n"), progname);
printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
@ -462,7 +469,7 @@ static void help(const char* progname)
#endif
}
static void check_root(const char* progname)
static void check_root(const char *progname)
{
#ifndef WIN32
if (geteuid() == 0) {
@ -496,29 +503,49 @@ static void check_root(const char* progname)
}
#endif /* WIN32 */
}
static char* get_current_username(const char* progname)
/*
*
*
*
* progname
*
*
*
*
*
* 使
* Unix-like 使 getpwuid
* Windows 使 GetUserName
* 线线
*/
static char *get_current_username(const char *progname)
{
#ifndef WIN32
struct passwd* pw = NULL;
char* pRet = NULL;
struct passwd *pw = NULL;
char *pRet = NULL;
/* 获取 getpwuid 函数的锁,以确保线程安全 */
(void)syscalllockAcquire(&getpwuid_lock);
/* 获取当前用户的密码项 */
pw = getpwuid(geteuid());
if (pw == NULL) {
/* 释放锁并报告错误,如果获取密码项失败 */
(void)syscalllockRelease(&getpwuid_lock);
write_stderr("%s: invalid effective UID: %d\n", progname, (int)geteuid());
exit(1);
}
/* Allocate new memory because later getpwuid() calls can overwrite it. */
pRet = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), pw->pw_name);
/* 释放锁并返回用户名 */
(void)syscalllockRelease(&getpwuid_lock);
return pRet;
#else
unsigned long namesize = 256 /* UNLEN */ + 1;
char* name = NULL;
char *name = NULL;
/* 在内存上分配空间以存储用户名 */
name = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), namesize);
/* 尝试获取 Windows 用户名 */
if (!GetUserName(name, &namesize)) {
write_stderr("%s: could not determine user name (GetUserName failed)\n", progname);
exit(1);
@ -527,12 +554,29 @@ static char* get_current_username(const char* progname)
return name;
#endif
}
/*
*
*
*
*
*
* 线
*
*/
static void syscall_lock_init(void)
{
/* 初始化获取用户密码项的锁 */
syscalllockInit(&getpwuid_lock);
/* 初始化环境变量锁 */
syscalllockInit(&env_lock);
/* 初始化 dlerror 函数的锁 */
syscalllockInit(&dlerror_lock);
/* 初始化 Kerberos 连接锁 */
syscalllockInit(&kerberos_conn_lock);
/* 初始化读取加密数据的锁 */
syscalllockInit(&read_cipher_lock);
}

View File

@ -0,0 +1,8 @@
{
"files.associations": {
"array": "cpp",
"string_view": "cpp",
"initializer_list": "cpp",
"utility": "cpp"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
*
* -------------------------------------------------------------------------
*/
// 该文件实现了openGauss的报警检查线程功能主要用于检查数据库运行过程中的异常情况并进行相应的报警处理
#include "postgres.h"
#include "knl/knl_variable.h"
@ -48,64 +49,77 @@
#include "replication/walsender.h"
// declare the global variable of alarm module
int g_alarmReportInterval;
char g_alarmComponentPath[MAXPGPATH];
int g_alarmReportMaxCount;
// 声明用于控制报警模块行为的全局变量
int g_alarmReportInterval; // 报警上报的时间间隔(单位:秒)
char g_alarmComponentPath[MAXPGPATH]; // 报警组件路径存储报警信息的组件的路径长度为MAXPGPATH
int g_alarmReportMaxCount; // 最大报警上报次数
/* seconds, interval of alarm check loop. */
static const int AlarmCheckInterval = 1;
static const int AlarmCheckInterval = 1; // 报警检查循环的时间间隔初始设置为1秒
bool enable_alarm = false;
bool enable_alarm = false; // 表示是否启用报警功能。初始值为false通过设置为true来启用报警功能
static Alarm* DataInstAlarmList = NULL;
static Alarm* DataInstAlarmList = NULL; // 指向 Alarm 结构体的指针,表示报警项的列表。报警项是用于检测不同类型报警的配置信息和处理函数的集合。
static int DataInstAlarmListSize = 0;
static int DataInstAlarmListSize = 0; // 报警项列表的大小,即列表中报警项的数量
AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
// 函数原型声明
AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);//报警检查函数,用于检查数据目录或重做日志目录是否存在
static void DataInstAlarmItemInitialize(void);
static void acSighupHandler(SIGNAL_ARGS);
static void acSigquitHandler(SIGNAL_ARGS);
static void DataInstAlarmItemInitialize(void); // 报警项初始化函数,用于初始化数据实例的报警项列表。
extern AlarmCheckResult DataInstArchChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
extern AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
static void acSighupHandler(SIGNAL_ARGS); // SIGHUP信号处理函数用于在收到SIGHUP信号时设置相关标志
static void acSigquitHandler(SIGNAL_ARGS); // SIGQUIT信号处理函数的原型用于在收到SIGQUIT信号时设置相关标志。
// 数据实例归档检查函数,用于检查数据实例的归档状态
extern AlarmCheckResult DataInstArchChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
// 连接认证方法检查函数,用于检查连接的认证方法是否异常
extern AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
// 数据实例连接到GTM的检查函数用于检查数据实例连接到GTM的状态
extern AlarmCheckResult DataInstConnToGTMChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
// 初始化数据实例报警列表
void DataInstAlarmItemInitialize(void)
{
// 设置数据实例报警项的数量为6
DataInstAlarmListSize = 6;
// 分配内存来存储数据实例报警项
DataInstAlarmList = (Alarm*)AlarmAlloc(sizeof(Alarm) * DataInstAlarmListSize);
// 检查内存分配是否成功,如果失败则记录错误日志并退出程序
if (NULL == DataInstAlarmList) {
AlarmLog(ALM_LOG, "Out of memory: DataInstAlarmItemInitialize failed.");
exit(1);
}
// ALM_AI_MissingDataInstDataOrRedoDir
// 初始化各个数据实例报警项 参数列表中后三个分别为报警项的类型、严重性级别,以及对应的检查函数
// ALM_AI_MissingDataInstDataOrRedoDir 报警项
AlarmItemInitialize(
&(DataInstAlarmList[0]), ALM_AI_MissingDataInstDataOrRedoDir, ALM_AS_Normal, DataOrRedoDirNotExistChecker);
// ALM_AI_MissingDataInstWalSegmt
// ALM_AI_MissingDataInstWalSegmt 报警项
AlarmItemInitialize(
&(DataInstAlarmList[1]), ALM_AI_MissingDataInstWalSegmt, ALM_AS_Normal, WalSegmentsRemovedChecker);
// ALM_AI_TooManyDataInstConn
// ALM_AI_TooManyDataInstConn 报警项
AlarmItemInitialize(&(DataInstAlarmList[2]), ALM_AI_TooManyDataInstConn, ALM_AS_Normal, ConnectionOverloadChecker);
// ALM_AI_AbnormalDataInstArch
// ALM_AI_AbnormalDataInstArch 报警项
AlarmItemInitialize(&(DataInstAlarmList[3]), ALM_AI_AbnormalDataInstArch, ALM_AS_Normal, DataInstArchChecker);
// ALM_AI_AbnormalDataInstConnAuthMethod
// ALM_AI_AbnormalDataInstConnAuthMethod 报警项
AlarmItemInitialize(
&(DataInstAlarmList[4]), ALM_AI_AbnormalDataInstConnAuthMethod, ALM_AS_Normal, ConnAuthMethodChecker);
// ALM_AI_AbnormalDataInstConnToGTM
// ALM_AI_AbnormalDataInstConnToGTM 报警项
AlarmItemInitialize(
&(DataInstAlarmList[5]), ALM_AI_AbnormalDataInstConnToGTM, ALM_AS_Normal, DataInstConnToGTMChecker);
}
// 特定条件下启动报警检查线程,以便定期检查系统状态并进行报警处理。
ThreadId startAlarmChecker(void)
{
// 如果不是在Postmaster环境下或者报警功能被禁用则直接返回0表示未启动报警检查线程
if (!IsPostmasterEnvironment || !enable_alarm) {
return 0;
}
// 否则调用initialize_util_thread函数启动报警检查线程并返回线程ID
return initialize_util_thread(ALARMCHECK);
}
// 维护一个周期性的报警检查线程,用于及时发现系统异常情况并进行相应的处理。
NON_EXEC_STATIC void AlarmCheckerMain()
{
@ -113,21 +127,23 @@ NON_EXEC_STATIC void AlarmCheckerMain()
IsUnderPostmaster = true;
/* reset t_thrd.proc_cxt.MyProcPid */
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); //将当前线程的系统级线程ID分配给MyProcPid以标识当前线程
/* record Start Time for logging */
t_thrd.proc_cxt.MyStartTime = time(NULL);
t_thrd.proc_cxt.MyStartTime = time(NULL); //获取当前的系统时间,即记录线程的启动时间。
/* reord my name */
t_thrd.proc_cxt.MyProgName = "AlarmChecker";
t_thrd.proc_cxt.MyProgName = "AlarmChecker"; // 设置线程的名称,用于标识当前线程的名称
/* Identify myself via ps */
init_ps_display("AlarmChecker", "", "", "");
init_ps_display("AlarmChecker", "", "", ""); // 设置线程在进程状态ps显示中的标识为 "AlarmChecker"
AlarmLog(ALM_LOG, "alarm checker started.");
AlarmLog(ALM_LOG, "alarm checker started."); // 记录报警检查线程启动信息
// 初始化Latch支持用于等待Latch的触发
InitializeLatchSupport(); /* needed for latch waits */
// 初始化用于信号处理的私有Latch以便在信号到达时唤醒线程执行相应的处理
/* Initialize private latch for use by signal handlers */
InitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
@ -139,10 +155,15 @@ NON_EXEC_STATIC void AlarmCheckerMain()
* want to wait for the backends to exit, whereupon the postmaster will
* tell us it's okay to shut down (via SIGUSR2).
*/
// 处理了信号的设置和忽略,确保报警检查线程能够正确响应或忽略不同的信号
// 将SIGHUP信号的处理函数设置为acSighupHandler。当收到SIGHUP信号时会触发该信号处理函数用于读取配置文件的标志位。
(void)gspqsignal(SIGHUP, acSighupHandler); /* set flag to read config file */
// 将SIGINT和SIGTERM信号的处理设置为忽略状态当收到这两个信号时不会触发任何处理。
(void)gspqsignal(SIGINT, SIG_IGN);
(void)gspqsignal(SIGTERM, SIG_IGN);
// 将SIGQUIT信号的处理函数设置为acSigquitHandler。当收到SIGQUIT信号时会触发该信号处理函数用于执行快速退出操作。
(void)gspqsignal(SIGQUIT, acSigquitHandler);
// 将SIGALRM、SIGPIPE、SIGUSR1、SIGUSR2信号的处理设置为忽略状态即当收到以上信号时不会触发任何处理。
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, SIG_IGN);
@ -151,46 +172,57 @@ NON_EXEC_STATIC void AlarmCheckerMain()
/*
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGCHLD, SIG_DFL);
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
(void)gspqsignal(SIGCONT, SIG_DFL);
(void)gspqsignal(SIGWINCH, SIG_DFL);
// 重置一些信号的默认处理方式,以确保报警检查线程不会干扰其他信号的处理
// 将信号的处理设置为默认处理方式 SIG_DFL
(void)gspqsignal(SIGCHLD, SIG_DFL); // 子进程状态变化信号
(void)gspqsignal(SIGTTIN, SIG_DFL); // 后台进程试图从终端读取时发送的信号
(void)gspqsignal(SIGTTOU, SIG_DFL); // 后台进程试图向终端写入时发送的信号
(void)gspqsignal(SIGCONT, SIG_DFL); // 用于继续停止的进程的信号
(void)gspqsignal(SIGWINCH, SIG_DFL); // 终端窗口大小发生变化时发送的信号
// 处理信号掩码,以允许接收一些特定的信号
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
/* all is done info top memory context. */
// 切换当前线程的内存上下文到默认的内存上下文组
(void)MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT));
// 调用函数,初始化数据实例的报警列表
DataInstAlarmItemInitialize();
// 报警检查线程的主要工作循环,用于持续进行报警检查和处理
for (;;) {
/* Clear any already-pending wakeups */
//将报警检查线程的私有Latch重置为未触发状态防止在等待期间可能发生的竞争条件或意外触发。
ResetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
/* the normal shutdown case */
// 如果收到了终止信号,退出循环
if (t_thrd.alarm_cxt.gotSigdie)
break;
/*
* reload the postgresql.conf
*/
// 如果收到了重新加载配置文件的信号
if (t_thrd.alarm_cxt.gotSighup) {
// 设置为 false表示报警检查线程不再需要重新加载配置文件。
// 用于处理 SIGHUP 信号执行操作,标记已经理重新加载配置文件的请求,以便线程在下次循环迭代时不会再次触发重新加载
t_thrd.alarm_cxt.gotSighup = false;
ProcessConfigFile(PGC_SIGHUP);
ProcessConfigFile(PGC_SIGHUP); // 调用ProcessConfigFile函数重新加载配置文件
}
// 调用AlarmCheckerLoop函数进行报警检查
AlarmCheckerLoop(DataInstAlarmList, DataInstAlarmListSize);
/*
* Sleep until there's something to do
*/
// 等待一段时间等待Latch被设置或超时
(void)WaitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch, WL_LATCH_SET | WL_TIMEOUT, AlarmCheckInterval * 1000);
}
// 记录日志,标识报警检查线程正在关闭
AlarmLog(ALM_LOG, "alarm checker shutting down...");
// 调用 proc_exit 函数终止线程执行。参数 0 表示正常退出,线程将在此处终止并释放相关资源
proc_exit(0);
}
@ -203,15 +235,16 @@ NON_EXEC_STATIC void AlarmCheckerMain()
* Description :
* Notes :
*/
// 信号处理函数,用于处理 SIGHUP 信号,并设置相应的标志
static void acSighupHandler(SIGNAL_ARGS)
{
int save_errno = errno;
int save_errno = errno; // 保存当前 errno 的值,以便后续恢复
t_thrd.alarm_cxt.gotSighup = true;
t_thrd.alarm_cxt.gotSighup = true; // 将线程上下文中的 gotSighup 标志设置为 true表示收到了 SIGHUP 信号
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); // 使用 SetLatch 函数触发线程的私有 Latch以便唤醒线程并处理信号
errno = save_errno;
errno = save_errno;// 恢复之前保存的 errno 值,确保不影响其他代码对 errno 的操作
}
/*
@ -220,84 +253,96 @@ static void acSighupHandler(SIGNAL_ARGS)
* Description :
* Notes :
*/
// 信号处理函数,用于处理 SIGTERM 或 SIGINT 信号,并设置相应的标志
static void acSigquitHandler(SIGNAL_ARGS)
{
int save_errno = errno;
int save_errno = errno;// 保存当前 errno 的值,以便后续恢复
t_thrd.alarm_cxt.gotSigdie = true;
t_thrd.alarm_cxt.gotSigdie = true;// 将线程上下文中的 gotSigdie 标志设置为 true表示收到了 SIGTERM 或 SIGINT 信号
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);// 使用 SetLatch 函数触发线程的私有 Latch以便唤醒线程并处理信号
errno = save_errno;
errno = save_errno;// 恢复之前保存的 errno 值,确保不影响其他代码对 errno 的操作
}
// 用于检查目录是否存在,以及目录是否具有合适的属性和权限,即检查目录的有效性。
bool isDirExist(const char* dir)
{
// 创建一个用于存放文件/目录属性信息的结构体
struct stat stat_buf;
// 使用 stat 函数获取目录的状态信息如果返回值不等于0则表示目录不存在
if (stat(dir, &stat_buf) != 0)
return false;
// 使用 S_ISDIR 宏判断目录的文件类型是否为目录,如果不是目录类型,则返回 false
if (!S_ISDIR(stat_buf.st_mode))
return false;
// 如果不在 Windows 平台且不在 Cygwin 环境中
#if !defined(WIN32) && !defined(__CYGWIN__)
// 检查目录的拥有者是否为当前用户,如果不是则返回 false
if (stat_buf.st_uid != geteuid())
return false;
// 检查目录的权限是否为用户可读、写和执行权限,如果不是则返回 false
if ((stat_buf.st_mode & S_IRWXU) != S_IRWXU)
return false;
#endif
// 如果以上条件都满足,则返回 true表示目录存在且符合要求
return true;
}
AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam)
{
// 检查 data 目录和 pg_xlog 目录是否存在
if (isDirExist(t_thrd.proc_cxt.DataDir) && isDirExist("pg_xlog")) {
// fill the alarm message
WriteAlarmAdditionalInfo(additionalParam,
g_instance.attr.attr_common.PGXCNodeName,
// 填写报警信息
WriteAlarmAdditionalInfo(additionalParam, //additionalParam报警的附加参数用于存储报警信息的详细内容
g_instance.attr.attr_common.PGXCNodeName, // 数据库实例的名称,用于标识报警发生的实例
"",
"",
alarm,
ALM_AT_Resume,
g_instance.attr.attr_common.PGXCNodeName);
return ALM_ACR_Normal;
alarm, //报警的类型或描述,用于标识具体的报警原因或问题
ALM_AT_Resume,// 设置报警动作为“恢复”,表示报警条件已经解决
g_instance.attr.attr_common.PGXCNodeName); //数据库实例的名称,用于填写报警信息
return ALM_ACR_Normal;// 返回报警检查结果为“正常”
} else {
// fill the alarm message
// 填写报警信息
WriteAlarmAdditionalInfo(additionalParam,
g_instance.attr.attr_common.PGXCNodeName,
"",
"",
alarm,
ALM_AT_Fault,
ALM_AT_Fault, // 设置报警动作为“故障”
g_instance.attr.attr_common.PGXCNodeName);
return ALM_ACR_Abnormal;
return ALM_ACR_Abnormal;// 返回报警检查结果为“异常”
}
}
/* implementation of alarm module. */
// 用于释放动态分配的内存,避免内存泄漏
void AlarmFree(void* pointer)
{
if (pointer != NULL)
pfree(pointer);
if (pointer != NULL)// 检查指针是否非空
pfree(pointer); // 使用 pfree 函数释放内存
}
// 用于分配指定大小的内存块
void* AlarmAlloc(size_t size)
{
return palloc(size);
return palloc(size);// 调用 palloc 函数分配指定大小的内存块,并返回分配的内存块指针
}
// 日志输出函数,用于在不同级别输出报警信息
// 输入参数分别为报警级别、前缀和报警文本,根据需要输出不同级别的报警信息以进行监控和调试。
void AlarmLogImplementation(int level, const char* prefix, const char* logtext)
{
// 使用 switch 语句根据不同的级别选择不同的日志输出函数并输出信息
switch (level) {
case ALM_DEBUG:
case ALM_DEBUG:// 在 DEBUG3 级别输出报警信息,使用 errmsg 函数输出带有前缀和文本的日志
ereport(DEBUG3, (errmsg("%s%s", prefix, logtext)));
break;
case ALM_LOG:
case ALM_LOG:// 在 LOG 级别输出报警信息,使用 errmsg 函数输出带有前缀和文本的日志
ereport(LOG, (errmsg("%s%s", prefix, logtext)));
break;
default:

File diff suppressed because it is too large Load Diff

View File

@ -52,60 +52,72 @@
#define atolsn(x) ((XLogRecPtr)strtoul((x), NULL, 0))
// 将barrier_id 写入到OBS华为云对象存储用于实现数据存档
static void write_barrier_id_to_obs(const char* barrier_name, ArchiveConfig *archive_obs)
{
errno_t rc = 0;
ArchiveConfig obsConfig;
char pathPrefix[MAXPGPATH] = {0};
errno_t rc = 0; // 用于存储函数调用的返回值,以便检查错误
ArchiveConfig obsConfig; // 存储OBS配置的临时变量
char pathPrefix[MAXPGPATH] = {0}; // 用于存储OBS路径前缀的临时变量
ereport(LOG, (errmsg("Write barrierId <%s> to obs start", barrier_name)));
ereport(LOG, (errmsg("Write barrierId <%s> to obs start", barrier_name))); // 输出日志表示开始写入BarrierID到OBS
/* copy OBS configs to temporary variable for customising file path */
rc = memcpy_s(&obsConfig, sizeof(ArchiveConfig), archive_obs, sizeof(ArchiveConfig));
rc = memcpy_s(&obsConfig, sizeof(ArchiveConfig), archive_obs, sizeof(ArchiveConfig)); // 将OBS配置复制到临时变量以便自定义文件路径
securec_check(rc, "", "");
if (!IS_PGXC_COORDINATOR) {
rc = strcpy_s(pathPrefix, MAXPGPATH, obsConfig.archive_prefix);
if (!IS_PGXC_COORDINATOR) { // 如果不是PGXC协调器
rc = strcpy_s(pathPrefix, MAXPGPATH, obsConfig.archive_prefix); // 将OBS路径前缀复制到pathPrefix中
securec_check(rc, "\0", "\0");
char *p = strrchr(pathPrefix, '/');
// 查找路径中的最后一个 '/'
char *p = strrchr(pathPrefix, '/');
if (p == NULL) {
// 如果找不到最后一个 '/',则输出错误并终止
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Obs path prefix is invalid")));
}
// 将找到的最后一个 '/' 替换为字符串结束符,以截取路径前缀
*p = '\0';
// 将修改后的路径前缀赋值回obsConfig
obsConfig.archive_prefix = pathPrefix;
}
// 调用ArchiveWrite函数将BarrierID写入OBS
ArchiveWrite(BARRIER_FILE, barrier_name, MAX_BARRIER_ID_LENGTH - 1, &obsConfig);
}
// 等待Barrier归档操作直到达到指定的LSN
static void WaitBarrierArch(XLogRecPtr barrierLsn, const char *slotName)
{
// 输出日志表示开始等待Barrier归档操作
ereport(LOG,
(errmsg("WaitBarrierArch start: 0x%lx", barrierLsn)));
int cnt = 0;
const int interval = 100;
int cnt = 0; // 用于计数等待次数
const int interval = 100; // 定义计数间隔
do {
ArchiveTaskStatus *archive_task_status = NULL;
archive_task_status = find_archive_task_status(slotName);
ArchiveTaskStatus *archive_task_status = NULL; // 用于存储归档任务状态的指针
archive_task_status = find_archive_task_status(slotName); // 根据slotName查找归档任务状态
if (NULL == archive_task_status) {
// 如果找不到归档任务状态,输出错误信息并终止
ereport(ERROR, (errcode(ERRCODE_OPERATE_NOT_SUPPORTED), errmsg("Obs slot <%s> not exist.", slotName)));
}
// 比较barrierLsn和当前已归档的LSN如果达到了要求则跳出循环
if (XLByteLE(pg_atomic_read_u64(&barrierLsn),
pg_atomic_read_u64(&archive_task_status->archived_lsn))) {
break;
}
CHECK_FOR_INTERRUPTS();
CHECK_FOR_INTERRUPTS(); // 检查是否有中断请求
/* Also check stop flag */
if (t_thrd.barrier_arch.ready_to_stop) {
// 如果已经准备停止,则输出错误信息并终止
ereport(ERROR, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("[BarrierArch] terminating barrier arch"
" due to administrator command")));
}
pg_usleep(100000L);
pg_usleep(100000L); // 等待100000微秒0.1秒)
if (t_thrd.barrier_arch.lastArchiveLoc == pg_atomic_read_u64(&archive_task_status->archived_lsn)) {
// 如果归档位置没有发生变化,计数加一,并在一定间隔输出警告信息
cnt++;
if ((cnt % interval) == 0) {
ereport(WARNING, (errmsg("[WaitBarrierArch] arch thread now archived"
@ -113,23 +125,27 @@ static void WaitBarrierArch(XLogRecPtr barrierLsn, const char *slotName)
(uint32)t_thrd.barrier_arch.lastArchiveLoc, cnt)));
}
} else {
cnt = 0;
cnt = 0; // 如果归档位置发生变化,计数清零
}
// 更新归档位置,并检查是否超过了等待超时
t_thrd.barrier_arch.lastArchiveLoc = pg_atomic_read_u64(&archive_task_status->archived_lsn);
if (cnt > WAIT_ARCHIVE_TIMEOUT) {
// 如果等待次数超过了设定的超时次数,输出错误信息并终止
ereport(ERROR, (errcode(ERRCODE_OPERATE_NOT_SUPPORTED), errmsg("Wait archived timeout.")));
}
} while (1);
} while (1); // 无限循环直到达到要求的LSN
ereport(LOG, (errmsg("WaitBarrierArch archive lsn end")));
ereport(LOG, (errmsg("WaitBarrierArch archive lsn end"))); // 输出日志,表示等待归档操作结束
}
/*
* Make sure the current BARRIER WAL record has been archived.If not, wait until
* the BARRIER WAL record has been archived.
*/
// 确保当前的BARRIER WAL记录已被归档
void ProcessBarrierQueryArchive(char* id)
{
// 输出日志表示收到BARRIER QUERY消息
ereport(LOG,
(errmsg("Receive BARRIER <%s> QUERY message on Coordinator or Datanode", id)));
@ -137,84 +153,99 @@ void ProcessBarrierQueryArchive(char* id)
char *slotName;
char *lsn;
lsn = strtok_r(id, ":", &slotName);
lsn = strtok_r(id, ":", &slotName); // 使用":"分割id获取LSN和slotName
if (lsn == NULL) {
// 如果LSN为空输出错误信息并终止
ereport(ERROR,
(errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
errmsg("The BARRIER QUERY ARCHIVE target lsn is null")));
}
// 将LSN转换为XLogRecPtr类型
XLogRecPtr barrierTargetLsn = atolsn(lsn);
// 输出日志表示收到指定LSN消息
ereport(LOG,
(errmsg("Receive LSN <%lx> message on Coordinator or Datanode", barrierTargetLsn)));
// 输出日志表示收到BARRIER QUERY消息的slotName
ereport(LOG,
(errmsg("Receive BARRIER QUERY message slotname: %s", slotName)));
// 如果不是从协调器连接,输出错误信息并终止
if (!IsConnFromCoord())
ereport(ERROR,
(errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
errmsg("The BARRIER QUERY ARCHIVE message is expected to "
"arrive from a Coordinator")));
if (!IS_PGXC_COORDINATOR) {
WaitBarrierArch(barrierTargetLsn, slotName);
if (!IS_PGXC_COORDINATOR) { // 如果不是PGXC协调器
WaitBarrierArch(barrierTargetLsn, slotName); // 等待达到指定的LSN
}
pq_beginmessage(&buf, 'b');
pq_sendstring(&buf, id);
pq_endmessage(&buf);
pq_flush();
pq_beginmessage(&buf, 'b'); // 启动一个'b'类型的消息
pq_sendstring(&buf, id); // 向消息中添加id字符串
pq_endmessage(&buf); // 结束消息构建
pq_flush(); // 刷新消息 发送给客户端
}
// 处理用于终止Barrier归档的信号
static void BarrierArchWakenStop(SIGNAL_ARGS)
{
t_thrd.barrier_arch.ready_to_stop = true;
t_thrd.barrier_arch.ready_to_stop = true; // 设置标志,准备终止归档
}
// 处理收到SIGHUP信号的情况
static void BarrierArchSighupHandler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.barrier_arch.got_SIGHUP = true;
errno = save_errno;
int save_errno = errno; // 保存当前的错误码
t_thrd.barrier_arch.got_SIGHUP = true; // 设置标志表示收到了SIGHUP信号
errno = save_errno; // 恢复之前保存的错误码
}
/* Reset some signals that are accepted by postmaster but not here */
// 设置不同信号的处理方式,确保程序在收到不同的信号时能够正确地处理
static void BarrierArchSetupSignalHook(void)
{
(void)gspqsignal(SIGHUP, BarrierArchSighupHandler);
(void)gspqsignal(SIGINT, SIG_IGN);
(void)gspqsignal(SIGTERM, die);
(void)gspqsignal(SIGQUIT, quickdie);
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
(void)gspqsignal(SIGUSR2, BarrierArchWakenStop);
(void)gspqsignal(SIGHUP, BarrierArchSighupHandler); // 设置SIGHUP的处理函数为BarrierArchSighupHandler
(void)gspqsignal(SIGINT, SIG_IGN); // 忽略SIGINT信号
(void)gspqsignal(SIGTERM, die); // 设置SIGTERM的处理函数为die
(void)gspqsignal(SIGQUIT, quickdie); // 设置SIGQUIT的处理函数为quickdie
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略SIGALRM信号
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE信号
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); // 设置SIGUSR1的处理函数为procsignal_sigusr1_handler
(void)gspqsignal(SIGUSR2, BarrierArchWakenStop); // 设置SIGUSR2的处理函数为BarrierArchWakenStop
(void)gspqsignal(SIGCHLD, SIG_DFL);
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
(void)gspqsignal(SIGCONT, SIG_DFL);
(void)gspqsignal(SIGWINCH, SIG_DFL);
(void)gspqsignal(SIGCHLD, SIG_DFL); // 将SIGCHLD的处理函数设置为默认值
(void)gspqsignal(SIGTTIN, SIG_DFL); // 将SIGTTIN的处理函数设置为默认值
(void)gspqsignal(SIGTTOU, SIG_DFL); // 将SIGTTOU的处理函数设置为默认值
(void)gspqsignal(SIGCONT, SIG_DFL); // 将SIGCONT的处理函数设置为默认值
(void)gspqsignal(SIGWINCH, SIG_DFL); // 将SIGWINCH的处理函数设置为默认值
/* We allow SIGQUIT (quickdie) at all times */
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 从阻塞信号集中移除SIGQUIT信号
}
#ifdef ENABLE_MULTIPLE_NODES
// 当宏 ENABLE_MULTIPLE_NODES 被定义时,编译以下代码块
// 获取所有节点的连接句柄
static PGXCNodeAllHandles* GetAllNodesHandles()
{
// 获取所有数据节点和协调节点的列表
List* barrierDataNodeList = GetAllDataNodes();
List* barrierCoordList = GetAllCoordNodes();
PGXCNodeAllHandles* conn_handles = NULL;
// 获取连接句柄
conn_handles = get_handles(barrierDataNodeList, barrierCoordList, false);
// 释放节点列表内存
list_free(barrierCoordList);
list_free(barrierDataNodeList);
return conn_handles;
return conn_handles; // 返回连接句柄
}
// 向所有节点发送Barrier归档请求
static void SendBarrierArchRequest(const PGXCNodeAllHandles* handles, int count, ArchiveBarrierLsnInfo *barrierLsnInfo)
{
int conn;
@ -223,23 +254,26 @@ static void SendBarrierArchRequest(const PGXCNodeAllHandles* handles, int count,
errno_t rc;
char barrierInfo[BARRIER_ARCH_INFO_LEN];
// 输出日志表示开始向所有节点发送Barrier归档请求
ereport(LOG, (errmsg("Start to send barrier arch request to all nodes.")));
for (conn = 0; conn < count; conn++) {
for (conn = 0; conn < count; conn++) { // 循环遍历所有连接,向每个连接发送请求
PGXCNodeHandle* handle = NULL;
// 根据连接类型获取连接句柄
if (conn < handles->co_conn_count)
handle = handles->coord_handles[conn];
else
handle = handles->datanode_handles[conn - handles->co_conn_count];
/* Invalid connection state, return error */
if (handle->state != DN_CONNECTION_STATE_IDLE) {
if (handle->state != DN_CONNECTION_STATE_IDLE) { // 如果连接状态无效,输出错误信息并终止
ereport(ERROR,
(errcode(ERRCODE_OPERATE_FAILED),
errmsg("Failed to send BARRIER request to the node")));
}
// 遍历所有barrierLsnInfo找到匹配节点的信息
for (int i = 0; i < count; i++) {
if (barrierLsnInfo[i].nodeoid == handle->nodeoid) {
rc = snprintf_s(barrierInfo, BARRIER_ARCH_INFO_LEN, BARRIER_ARCH_INFO_LEN - 1, "0x%lx:%s",
@ -249,13 +283,15 @@ static void SendBarrierArchRequest(const PGXCNodeAllHandles* handles, int count,
barrier_idlen = strlen(barrierInfo) + 1;
// 计算消息的总长度
msglen = 4; /* for the length itself */
msglen += barrier_idlen;
msglen += 1; /* for barrier command itself */
/* msgType + msgLen */
ensure_out_buffer_capacity(1 + msglen, handle);
ensure_out_buffer_capacity(1 + msglen, handle); // 确保输出缓冲区足够容纳消息
// 添加消息类型 'b'
Assert(handle->outBuffer != NULL);
handle->outBuffer[handle->outEnd++] = 'b';
msglen = htonl(msglen);
@ -263,123 +299,147 @@ static void SendBarrierArchRequest(const PGXCNodeAllHandles* handles, int count,
securec_check(rc, "\0", "\0");
handle->outEnd += 4;
// 添加Barrier归档命令 BARRIER_QUERY_ARCHIVE
handle->outBuffer[handle->outEnd++] = BARRIER_QUERY_ARCHIVE;
// 添加Barrier信息
rc = memcpy_s(handle->outBuffer + handle->outEnd, handle->outSize - handle->outEnd, barrierInfo, barrier_idlen);
securec_check(rc, "\0", "\0");
handle->outEnd += barrier_idlen;
// 设置连接状态为查询状态
handle->state = DN_CONNECTION_STATE_QUERY;
// 刷新连接的输出缓冲区
pgxc_node_flush(handle);
}
}
// 检查在所有节点上执行BARRIER ARCH查询命令的状态
static void CheckBarrierArchCommandStatus(const PGXCNodeAllHandles* conn_handles, int count, const char *id)
{
int conn;
RemoteQueryState* combiner = NULL;
// 输出调试日志表示正在检查BARRIER ARCH查询命令状态
ereport(DEBUG1, (errmsg("Check BARRIER ARCH QUERY <%s> command status", id)));
// 创建一个响应组合器,用于合并来自多个节点的响应
combiner = CreateResponseCombiner(count, COMBINE_TYPE_NONE);
// 循环遍历所有连接,检查每个连接的响应
for (conn = 0; conn < count; conn++) {
PGXCNodeHandle* handle = NULL;
// 根据连接类型获取连接句柄
if (conn < conn_handles->co_conn_count)
handle = conn_handles->coord_handles[conn];
else
handle = conn_handles->datanode_handles[conn - conn_handles->co_conn_count];
// 接收来自节点的响应
if (pgxc_node_receive(1, &handle, NULL))
ereport(
ERROR, (errcode(ERRCODE_OPERATE_FAILED), errmsg("Failed to receive response from the remote side")));
// 处理响应并检查执行状态
if (handle_response(handle, combiner) != RESPONSE_BARRIER_OK)
ereport(ERROR,
(errcode(ERRCODE_OPERATE_FAILED),
errmsg("BARRIER ARCH QUERY failed with error %s", handle->error)));
}
// 关闭响应组合器
CloseCombiner(combiner);
// 输出日志表示在所有节点上成功完成了BARRIER ARCH查询命令
ereport(LOG,
(errmsg("Successfully completed BARRIER ARCH QUERY <%s> command on "
"all nodes",
id)));
}
// 执行归档相关的任务
static void QueryBarrierArch(PGXCNodeAllHandles* handles, ArchiveConfig *archive_obs)
{
int connCnt = handles->co_conn_count + handles->dn_conn_count;
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
// 获取最大节点数量
int archivMaxNodeCnt = g_instance.archive_obs_cxt.max_node_cnt;
// 如果连接数超过了最大节点数
if (connCnt >= archivMaxNodeCnt) {
// 释放全局锁
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
// 输出调试信息,表示当前连接数大于最大节点数
ereport(DEBUG2, (errmsg("current cn get connCnt: <%d> max than cluster connCnt: <%d>",
connCnt, archivMaxNodeCnt)));
return;
}
ArchiveBarrierLsnInfo barrierLsnInfo[g_instance.archive_obs_cxt.max_node_cnt];
// 如果当前的Barrier名称和已存储的名称相同直接返回
if (strncmp(t_thrd.barrier_arch.barrierName, g_instance.archive_obs_cxt.barrierName,
strlen(g_instance.archive_obs_cxt.barrierName)) == 0) {
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
return;
}
// 复制当前的Barrier名称到全局变量
errno_t errorno = memcpy_s(t_thrd.barrier_arch.barrierName, MAX_BARRIER_ID_LENGTH,
g_instance.archive_obs_cxt.barrierName,
sizeof(g_instance.archive_obs_cxt.barrierName));
securec_check(errorno, "\0", "\0");
// 检查是否所有节点的barrierLsn都不为0如果有一个为0直接返回
for (int i = 0; i < connCnt + 1; i++) {
if (g_instance.archive_obs_cxt.barrier_lsn_info[i].barrierLsn == 0x0) {
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
return;
}
}
// 复制barrierLsn信息到局部变量
errorno = memcpy_s(&barrierLsnInfo, sizeof(ArchiveBarrierLsnInfo) * g_instance.archive_obs_cxt.max_node_cnt,
g_instance.archive_obs_cxt.barrier_lsn_info,
sizeof(ArchiveBarrierLsnInfo) * g_instance.archive_obs_cxt.max_node_cnt);
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
securec_check(errorno, "\0", "\0");
// 发送Barrier归档请求
SendBarrierArchRequest(handles, connCnt, barrierLsnInfo);
// 检查Barrier归档命令的状态
CheckBarrierArchCommandStatus(handles, connCnt, t_thrd.barrier_arch.barrierName);
// 等待Barrier归档完成
WaitBarrierArch(barrierLsnInfo[connCnt].barrierLsn, t_thrd.barrier_arch.slot_name);
// 将Barrier名称写入归档存储
write_barrier_id_to_obs(t_thrd.barrier_arch.barrierName, archive_obs);
}
// 如果未定义ENABLE_MULTIPLE_NODES则执行以下代码块
#else
// 用于在单个节点上执行Barrier归档操作等待特定的LSN完成后将Barrier名称写入归档存储
static void SingleBarrierArch(ArchiveConfig *archive_obs)
{
XLogRecPtr barrierLsn;
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock); // 获取全局锁
// 检查当前的Barrier名称是否和已存储的名称相同如果相同则释放锁并返回
if (strncmp(t_thrd.barrier_arch.barrierName, g_instance.archive_obs_cxt.barrierName,
strlen(g_instance.archive_obs_cxt.barrierName)) == 0) {
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
return;
}
// 复制当前的Barrier名称到线程局部变量
errno_t errorno = memcpy_s(t_thrd.barrier_arch.barrierName, MAX_BARRIER_ID_LENGTH,
g_instance.archive_obs_cxt.barrierName,
sizeof(g_instance.archive_obs_cxt.barrierName));
securec_check(errorno, "\0", "\0");
// 复制BarrierLSN到局部变量
barrierLsn = g_instance.archive_obs_cxt.barrierLsn;
// 释放全局锁
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
// 等待Barrier归档完成
WaitBarrierArch(barrierLsn, t_thrd.barrier_arch.slot_name);
// 将Barrier名称写入归档存储
write_barrier_id_to_obs(t_thrd.barrier_arch.barrierName, archive_obs);
}
#endif
// 归档Barrier线程的入口函数
NON_EXEC_STATIC void BarrierArchMain(knl_thread_arg* arg)
{
ArchiveSlotConfig *obsArchiveSlot = NULL;
@ -388,25 +448,30 @@ NON_EXEC_STATIC void BarrierArchMain(knl_thread_arg* arg)
char username[NAMEDATALEN];
char *dbname = (char *)pstrdup(DEFAULT_DATABASE);
SetProcessingMode(InitProcessing);
SetProcessingMode(InitProcessing); // 设置当前线程的处理模式为初始化模式
// 设置线程的相关信息配置
t_thrd.role = BARRIER_ARCH;
t_thrd.proc_cxt.MyProgName = "BarrierArch";
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
t_thrd.barrier_arch.slot_name = pstrdup((char *)arg->payload);
u_sess->attr.attr_common.application_name = pstrdup("BarrierArch");
// 输出日志表示归档Barrier线程启动
ereport(LOG, (errmsg("[BarrierArch] barrier arch thread starts. slot name: %s", t_thrd.barrier_arch.slot_name)));
// 在进程退出时调用 PGXCNodeCleanAndRelease 函数
on_shmem_exit(PGXCNodeCleanAndRelease, 0);
// 设置信号处理函数
BarrierArchSetupSignalHook();
// 初始化
BaseInit();
// 设置数据库和用户信息
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username);
t_thrd.proc_cxt.PostInit->InitBarrierCreator();
// 创建一个内存上下文用于执行工作
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "BarrierArch",
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
@ -427,119 +492,139 @@ NON_EXEC_STATIC void BarrierArchMain(knl_thread_arg* arg)
* If an exception is encountered, processing resumes here.
* See notes in postgres.c about the design of this coding.
*/
// 捕获异常 以及进行异常处理
int curTryCounter;
int* oldTryCounter = NULL;
if (sigsetjmp(localSigjmpBuf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
/* Since not using PG_TRY, must reset error stack by hand */
t_thrd.log_cxt.error_context_stack = NULL;
t_thrd.log_cxt.error_context_stack = NULL; // 清理错误上下文
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
HOLD_INTERRUPTS(); // 阻止中断信号
/* Report the error to the server log */
EmitErrorReport();
EmitErrorReport(); // 将错误信息写入日志
// 释放资源
/* release resource held by lsc */
AtEOXact_SysDBCache(false);
AtEOXact_SysDBCache(false);
/* release resource */
LWLockReleaseAll();
LWLockReleaseAll();
/*
* Now return to normal top-level context and clear ErrorContext for
* next time.
*/
// 切换回初始内存上下文,清空错误状态
MemoryContextSwitchTo(barrierArchContext);
FlushErrorState();
MemoryContextResetAndDeleteChildren(barrierArchContext);
/* Now we can allow interrupts again */
RESUME_INTERRUPTS();
RESUME_INTERRUPTS(); // 恢复中断处理
/*
* Sleep at least 1 second after any error. A write error is likely
* to be repeated, and we don't want to be filling the error logs as
* fast as we can.
*/
// 等待一秒,以防止错误信息频繁写入日志
pg_usleep(1000000L);
}
destroy_handles();
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
destroy_handles(); // 销毁连接句柄
oldTryCounter = gstrace_tryblock_entry(&curTryCounter); // 设置捕获异常的计数器,以便后续异常处理
/* We can now handle ereport(ERROR) */
t_thrd.log_cxt.PG_exception_stack = &localSigjmpBuf;
t_thrd.log_cxt.PG_exception_stack = &localSigjmpBuf; // 将当前的异常捕获状态保存到线程的异常堆栈上
/*
* Unblock signals (they were blocked when the postmaster forked us)
*/
// 解除在 BarrierArchSetupSignalHook 函数中阻塞的一些信号
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
// 将处理模式设置为正常处理模式,表明线程正在正常运行并处理任务
SetProcessingMode(NormalProcessing);
// 等待1 秒
pg_usleep_retry(1000000L, 0);
obsArchiveSlot = getArchiveReplicationSlotWithName(t_thrd.barrier_arch.slot_name);
if (obsArchiveSlot == NULL) {
t_thrd.barrier_arch.ready_to_stop = true;
ereport(WARNING, (errmsg("[BarrierArch] obs slot not created.")));
obsArchiveSlot = getArchiveReplicationSlotWithName(t_thrd.barrier_arch.slot_name); // 获取与给定名称匹配的归档复制槽
if (obsArchiveSlot == NULL) { // 如果找不到匹配的槽
t_thrd.barrier_arch.ready_to_stop = true; // 准备停止线程
ereport(WARNING, (errmsg("[BarrierArch] obs slot not created."))); // 输出警告消息
return;
}
exec_init_poolhandles();
exec_init_poolhandles(); // 初始化连接池句柄
#ifdef ENABLE_MULTIPLE_NODES
// 开启多节点编译选项时执行以下代码块
do {
// 如果当前节点不是第一个协调器节点,跳出循环
if (IsFirstCn())
break;
// 输出日志,显示当前节点不是第一个协调器节点
ereport(DEBUG1, (errmsg("[BarrierArch] Current node is not first node: %s",
g_instance.attr.attr_common.PGXCNodeName)));
if (IsGotPoolReload()) {
// 如果收到了连接池重载标志,执行连接池重载操作
processPoolerReload();
// 重置连接池重载标志为 false
ResetGotPoolReload(false);
}
// 检查是否有中断请求
CHECK_FOR_INTERRUPTS();
// 暂停 10 秒
pg_usleep(10000000L);
} while (1);
// 获取一个名为 barrier_lock 的自旋锁,用于保护关键资源
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
// 如果 barrier_lsn_info 为空,或者 max_node_cnt 为 0
if (g_instance.archive_obs_cxt.barrier_lsn_info == NULL ||
g_instance.archive_obs_cxt.max_node_cnt == 0) {
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
ereport(WARNING, (errmsg("[BarrierArch] barrier_lsn_info not alloc.")));
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放自旋锁
// 输出警告消息,提示 barrier_lsn_info 未分配
ereport(WARNING, (errmsg("[BarrierArch] barrier_lsn_info not alloc.")));
return;
}
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放自旋锁
#endif
// 结束多节点编译选项的条件编译
// 输出日志,显示正在初始化与协调器和数据节点的连接,以及数据节点和协调器的数量
ereport(DEBUG1,
(errmsg("[BarrierArch] Init connections with CN/DN, dn count : %d, cn count : %d",
u_sess->pgxc_cxt.NumDataNodes, u_sess->pgxc_cxt.NumCoords)));
// 进入循环,只要 ready_to_stop 标志不为真,就一直执行循环体
while (!t_thrd.barrier_arch.ready_to_stop) {
CHECK_FOR_INTERRUPTS();
CHECK_FOR_INTERRUPTS(); // 检查是否有中断请求
// 如果 barrierName 为空或长度为 0
if (g_instance.archive_obs_cxt.barrierName == NULL || strlen(g_instance.archive_obs_cxt.barrierName) == 0) {
ereport(WARNING, (errmsg("[BarrierArch] barrierName is null.")));
ereport(WARNING, (errmsg("[BarrierArch] barrierName is null."))); // 输出警告消息,提示 barrierName 为空
break;
}
#ifdef ENABLE_MULTIPLE_NODES
// 开启了多节点编译选项时执行以下代码块
// 如果收到了连接池重载标志
if (IsGotPoolReload()) {
processPoolerReload();
ResetGotPoolReload(false);
if (!IsFirstCn())
processPoolerReload(); // 执行连接池重载操作
ResetGotPoolReload(false); // 重置连接池重载标志为 false
if (!IsFirstCn()) // 如果当前节点不是第一个协调器节点,跳出循环
break;
}
PGXCNodeAllHandles* handles = GetAllNodesHandles();
// 获取所有节点的连接句柄
PGXCNodeAllHandles* handles = GetAllNodesHandles();
// 调用 QueryBarrierArch 函数处理Barrier归档
QueryBarrierArch(handles, &obsArchiveSlot->archive_config);
// 释放所有节点的连接句柄内存
pfree_pgxc_all_handles(handles);
#else
// 没有开启多节点编译选项时执行以下代码块
// 调用 SingleBarrierArch 函数处理Barrie归档
SingleBarrierArch(&obsArchiveSlot->archive_config);
#endif
}
// 输出日志,显示障碍归档线程已退出
ereport(LOG, (errmsg("[BarrierArch] barrier arch thread exits.")));
}

View File

@ -52,100 +52,116 @@ const int BARRIER_NAME_LEN = 40;
const char* CSN_BARRIER_PATTREN_STR = "csn_%021lu_%013ld";
const char* CSN_SWITCHOVER_BARRIER_PATTREN_STR = "csn_%021lu_dr_switchover";
/*
Barrier归档的名称
barrierRet: Barrier
isSwitchoverBarrier: Barrier
*/
void GetCsnBarrierName(char* barrierRet, bool isSwitchoverBarrier)
{
struct timeval tv;
int rc;
CommitSeqNo csn;
struct timeval tv; // 用于存储时间的结构体变量
int rc; // 用于存储函数返回值的变量
CommitSeqNo csn; // 用于存储事务提交序列号的变量
// 如果处于GTM模式获取全局事务管理器的事务提交序列号
if (GTM_MODE)
csn = GetCSNGTM();
else
csn = CommitCSNGTM(false);
csn = CommitCSNGTM(false); // 否则获取本地事务提交序列号
gettimeofday(&tv, NULL);
gettimeofday(&tv, NULL); // 获取当前时间信息
if (isSwitchoverBarrier) {
if (isSwitchoverBarrier) { // 如果要切换Barrier
// 构造用于切换Barrier的名称将CSN插入到特定的格式字符串中
rc = snprintf_s(barrierRet, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_SWITCHOVER_BARRIER_PATTREN_STR, csn);
} else {
// 构造普通Barrier的名称将CSN和时间戳信息插入到格式字符串中
rc = snprintf_s(barrierRet, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_BARRIER_PATTREN_STR, csn,
TIME_GET_MILLISEC(tv));
}
securec_check_ss_c(rc, "\0", "\0");
securec_check_ss_c(rc, "\0", "\0"); // 检查格式化操作的返回值,确保操作成功
// 记录调试日志输出生成的Barrier名称和对应的CSN
elog(DEBUG1, "GetCsnBarrierName csn = %lu, barrier_name = %s", csn, barrierRet);
}
/* 根据传入的CSN Barrier名称解析出其中的CSN值并返回
: csnBarrier CSN Barrier
CSN
*/
CommitSeqNo CsnBarrierNameGetCsn(const char *csnBarrier)
{
CommitSeqNo csn;
long ts = 0;
// 使用格式化字符串解析CSN Barrier名称提取其中的CSN值和时间戳如果有
if ((strstr(csnBarrier, "_dr_switchover") != NULL &&
sscanf_s(csnBarrier, CSN_SWITCHOVER_BARRIER_PATTREN_STR, &csn) == 1) ||
sscanf_s(csnBarrier, CSN_BARRIER_PATTREN_STR, &csn, &ts) == 2) {
return csn;
}
return 0;
return 0; // 解析失败时返回0
}
// 根据传入的CSN Barrier名称解析出其中的时间戳值并返回
int64 CsnBarrierNameGetTimeStamp(const char *csnBarrier)
{
CommitSeqNo csn;
int64 ts = 0;
// 使用格式化字符串解析CSN Barrier名称提取其中的CSN值和时间戳如果有
if (sscanf_s(csnBarrier, CSN_BARRIER_PATTREN_STR, &csn, &ts) == 2) {
return ts;
}
return 0;
return 0; // 解析失败时返回0
}
// 判断传入的CSN Barrier名称是否为切换Barrier
bool IsSwitchoverBarrier(const char *csnBarrier)
{
// 判断CSN Barrier名称是否符合要求且是否包含 "_dr_switchover" 子串
if (!IS_CSN_BARRIER(csnBarrier) || (strstr(csnBarrier, "_dr_switchover") == NULL)) {
return false;
}
return true;
return true; // 如果符合要求则认为是切换Barrier
}
// 判断当前节点是否为第一个执行的协调器节点
bool IsFirstCn()
{
char *firstExecNode = find_first_exec_cn();
char *firstExecNode = find_first_exec_cn(); // 查找第一个执行的协调器节点
// 将当前节点的名称与firstExecNode进行比较如果两者相同则返回true表示当前节点是第一个执行的协调器节点否则返回false
return (strcmp(firstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
// 关闭Barrier创建线程
void barrier_creator_thread_shutdown(void)
{
g_instance.barrier_creator_cxt.stop = true;
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread shutting down.")));
g_instance.barrier_creator_cxt.stop = true; // 设置标志,表示停止线程
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread shutting down."))); // 输出日志
}
// SIGHUP信号处理函数用于重新加载配置
static void barrier_creator_sighup_handler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.barrier_creator_cxt.got_SIGHUP = true;
errno = save_errno;
}
int save_errno = errno; // 保存当前errno
t_thrd.barrier_creator_cxt.got_SIGHUP = true; // 设置标志表示收到SIGHUP信号
errno = save_errno; // 恢复errno
}
/* Reset some signals that are accepted by postmaster but not here */
static void barrier_creator_setup_signal_hook(void)
{
(void)gspqsignal(SIGHUP, barrier_creator_sighup_handler);
(void)gspqsignal(SIGINT, SIG_IGN);
(void)gspqsignal(SIGTERM, die);
(void)gspqsignal(SIGQUIT, quickdie);
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
(void)gspqsignal(SIGUSR2, SIG_IGN);
(void)gspqsignal(SIGHUP, barrier_creator_sighup_handler); // 设置SIGHUP信号处理函数
(void)gspqsignal(SIGINT, SIG_IGN); // 忽略SIGINT信号
(void)gspqsignal(SIGTERM, die); // 设置SIGTERM信号处理函数为die
(void)gspqsignal(SIGQUIT, quickdie); // 设置SIGQUIT信号处理函数为quickdie
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略SIGALRM信号
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE信号
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); // 设置SIGUSR1信号处理函数为procsignal_sigusr1_handler
(void)gspqsignal(SIGUSR2, SIG_IGN); // 忽略SIGUSR2信号
(void)gspqsignal(SIGCHLD, SIG_DFL);
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
(void)gspqsignal(SIGCONT, SIG_DFL);
(void)gspqsignal(SIGWINCH, SIG_DFL);
(void)gspqsignal(SIGCHLD, SIG_DFL); // 设置SIGCHLD信号处理函数为默认
(void)gspqsignal(SIGTTIN, SIG_DFL); // 设置SIGTTIN信号处理函数为默认
(void)gspqsignal(SIGTTOU, SIG_DFL); // 设置SIGTTOU信号处理函数为默认
(void)gspqsignal(SIGCONT, SIG_DFL); // 设置SIGCONT信号处理函数为默认
(void)gspqsignal(SIGWINCH, SIG_DFL); // 设置SIGWINCH信号处理函数为默认
/* We allow SIGQUIT (quickdie) at all times */
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许随时处理SIGQUITquickdie信号
}
// 从OBS中读取Barrier标识
static uint64_t read_barrier_id_from_obs(const char *slotName, long *currBarrierTime)
{
char barrier_name[BARRIER_NAME_LEN];
@ -153,146 +169,153 @@ static uint64_t read_barrier_id_from_obs(const char *slotName, long *currBarrier
uint64_t barrier_id;
if (ArchiveReplicationReadFile(BARRIER_FILE, (char *)barrier_name, MAX_BARRIER_ID_LENGTH, slotName)) {
barrier_name[BARRIER_NAME_LEN - 1] = '\0';
barrier_name[BARRIER_NAME_LEN - 1] = '\0'; // 将读取的数据末尾设置为字符串结束符
// 输出日志表示从归档存储中读取了barrier ID
ereport(LOG, (errmsg("[BarrierCreator] read barrier id from obs %s", barrier_name)));
} else {
// 输出日志表示从归档存储中读取barrier ID失败将从0开始
ereport(LOG, (errmsg("[BarrierCreator] failed to read barrier id from obs, start barrier from 0")));
return 0;
}
#ifdef ENABLE_MULTIPLE_NODES
// 根据不同的编译选项解析Barrier名称更新barrier_id和currBarrierTime
ret = sscanf_s(barrier_name, "csn_%021" PRIu64 "_%013ld", &barrier_id, currBarrierTime);
#else
ret = sscanf_s(barrier_name, "hadr_%020" PRIu64 "_%013ld", &barrier_id, currBarrierTime);
#endif
// 如果解析成功更新barrier_id并返回
if (ret == 2) {
barrier_id++;
return barrier_id;
}
return 0;
return 0; // 解析失败返回0
}
// 获取归档槽中的最大barrier索引和最新的barrier时间
uint64_t GetObsBarrierIndex(const List *archiveSlotNames, long *last_barrier_time)
{
uint64_t maxIndex = 0;
long maxBarrierTime = 0;
foreach_cell(cell, archiveSlotNames) {
long currBarrierTime = 0;
char* slotName = (char*)lfirst(cell);
if (slotName == NULL || strlen(slotName) == 0) {
uint64_t maxIndex = 0; // 最大barrier索引
long maxBarrierTime = 0; // 最大barrier时间
foreach_cell(cell, archiveSlotNames) { // 遍历归档槽名称列表
long currBarrierTime = 0; // 当前barrier时间
char* slotName = (char*)lfirst(cell); // 获取归档槽名称
if (slotName == NULL || strlen(slotName) == 0) { // 如果归档槽名称为空,跳过本次循环
continue;
}
uint64_t readIndex = read_barrier_id_from_obs(slotName, &currBarrierTime);
maxIndex = (readIndex > maxIndex) ? readIndex : maxIndex;
maxBarrierTime = (currBarrierTime > maxBarrierTime) ? currBarrierTime : maxBarrierTime;
uint64_t readIndex = read_barrier_id_from_obs(slotName, &currBarrierTime); // 从归档存储中读取barrier索引和时间
maxIndex = (readIndex > maxIndex) ? readIndex : maxIndex; // 更新最大barrier索引
maxBarrierTime = (currBarrierTime > maxBarrierTime) ? currBarrierTime : maxBarrierTime; // 更新最大barrier时间
}
*last_barrier_time = maxBarrierTime;
*last_barrier_time = maxBarrierTime; // 将最大barrier时间赋值给指针参数
return maxIndex;
return maxIndex; // 返回最大barrier索引
}
// 获取归档槽中第一个协调器节点的barrier时间线
uint64 GetObsFirstCNBarrierTimeline(const List *archiveSlotNames)
{
uint64 timeline = 0;
uint64 timeline = 0; // 时间线初始值为0
foreach_cell(cell, archiveSlotNames) {
char* slotName = (char*)lfirst(cell);
if (slotName == NULL || strlen(slotName) == 0) {
foreach_cell(cell, archiveSlotNames) { // 遍历归档槽名称列表
char* slotName = (char*)lfirst(cell); // 获取归档槽名称
if (slotName == NULL || strlen(slotName) == 0) { // 如果归档槽名称为空,跳过本次循环
continue;
}
timeline = ReadBarrierTimelineRecordFromObs(slotName);
break;
timeline = ReadBarrierTimelineRecordFromObs(slotName); // 从归档存储中读取协调器节点的barrier时间线
break; // 跳出循环,只获取第一个归档槽的时间线
}
return timeline;
return timeline; // 返回获取的时间线
}
#ifdef ENABLE_MULTIPLE_NODES
// 分配和初始化BarrierLsnInfo数组
static void AllocBarrierLsnInfo(int nodeSize)
{
int rc;
g_instance.archive_obs_cxt.barrier_lsn_info = (ArchiveBarrierLsnInfo *)palloc0(
sizeof(ArchiveBarrierLsnInfo) * nodeSize);
sizeof(ArchiveBarrierLsnInfo) * nodeSize); // 分配内存
rc = memset_s(g_instance.archive_obs_cxt.barrier_lsn_info,
sizeof(ArchiveBarrierLsnInfo) * nodeSize, 0,
sizeof(ArchiveBarrierLsnInfo) * nodeSize);
securec_check(rc, "", "");
sizeof(ArchiveBarrierLsnInfo) * nodeSize); // 将分配的内存内容初始化为0
securec_check(rc, "", ""); // 检查内存操作是否成功,如果不成功,输出错误信息
}
#endif
#ifdef ENABLE_MULTIPLE_NODES
// BarrierCreator连接池重新加载
static void BarrierCreatorPoolerReload(void)
{
destroy_handles();
processPoolerReload();
destroy_handles(); // 销毁连接句柄
processPoolerReload(); // 重新加载连接池
ereport(LOG,
(errmsg("[BarrierCreatorPoolerReload] Reload connections with CN/DN, dn count : %d, cn count : %d",
u_sess->pgxc_cxt.NumDataNodes,
u_sess->pgxc_cxt.NumCoords)));
if (g_instance.archive_obs_cxt.archive_slot_num == 0) {
return;
return; // 如果没有归档槽,直接返回
}
int maxNodeCnt = *t_thrd.pgxc_cxt.shmemNumCoords + *t_thrd.pgxc_cxt.shmemNumDataNodes;
if (maxNodeCnt > g_instance.archive_obs_cxt.max_node_cnt) {
if (maxNodeCnt > g_instance.archive_obs_cxt.max_node_cnt) { // 如果节点数量超过最大值
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
g_instance.archive_obs_cxt.max_node_cnt = 0;
g_instance.archive_obs_cxt.max_node_cnt = 0; // 清零最大节点数量
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
int nodeSize = maxNodeCnt;
if (g_instance.archive_obs_cxt.barrier_lsn_info != NULL) {
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info);
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info); // 释放旧的BarrierLsnInfo数组内存
}
AllocBarrierLsnInfo(nodeSize);
AllocBarrierLsnInfo(nodeSize); // 重新分配和初始化BarrierLsnInfo数组
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
g_instance.archive_obs_cxt.max_node_cnt = nodeSize;
g_instance.archive_obs_cxt.max_node_cnt = nodeSize; // 更新最大节点数量
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
}
}
#endif
// 用于释放之前分配的存储归档操作的Barrier LSN信息的内存
static void FreeBarrierLsnInfo()
{
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
g_instance.archive_obs_cxt.max_node_cnt = 0;
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info);
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock); // 获取全局锁
g_instance.archive_obs_cxt.max_node_cnt = 0; // 将最大节点数设置为0
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放全局锁
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info); // 释放分配的内存
}
// 创建不同类型的 Barrier以实现数据一致性和灾备需求
void barrier_creator_main(void)
{
uint64_t index = 0;
long last_barrier_time = 0;
struct timeval tv;
int rc;
char barrier_name[BARRIER_NAME_LEN];
List* archiveSlotNames;
MemoryContext barrier_creator_context;
sigjmp_buf local_sigjmp_buf;
t_thrd.barrier_creator_cxt.is_first_barrier = true;
char username[NAMEDATALEN];
char *dbname = (char *)pstrdup(DEFAULT_DATABASE);
bool startCsnBarrier = g_instance.attr.attr_storage.auto_csn_barrier;
uint64_t index = 0; // 初始化用于记录创建Barrier的索引
long last_barrier_time = 0; // 初始化上一个Barrier的时间
struct timeval tv; // 用于获取当前时间
int rc; // 用于记录函数返回值
char barrier_name[BARRIER_NAME_LEN]; // 用于存储Barrier名称的字符数组
List* archiveSlotNames; // 存储归档槽名的列表
MemoryContext barrier_creator_context; // 用于存储Barrier Creator线程的内存上下文
sigjmp_buf local_sigjmp_buf; // 用于实现异常处理跳转
t_thrd.barrier_creator_cxt.is_first_barrier = true; // 标识是否是首次创建Barrier
char username[NAMEDATALEN]; // 存储用户名的字符数组
char *dbname = (char *)pstrdup(DEFAULT_DATABASE); // 存储默认数据库名称的指针
bool startCsnBarrier = g_instance.attr.attr_storage.auto_csn_barrier; // 表示是否启用自动CSN Barrier的标志
// use InnerMaintenanceTools mode to avoid deadlock with thread pool
u_sess->proc_cxt.IsInnerMaintenanceTools = true;
ereport(LOG, (errmsg("[BarrierCreator] barrier creator started")));
g_instance.archive_obs_cxt.max_node_cnt = 0;
SetProcessingMode(InitProcessing);
u_sess->proc_cxt.IsInnerMaintenanceTools = true; // 使用InnerMaintenanceTools模式以避免与线程池产生死锁
ereport(LOG, (errmsg("[BarrierCreator] barrier creator started"))); // 输出日志表示Barrier Creator线程已启动
g_instance.archive_obs_cxt.max_node_cnt = 0; // 初始化最大节点数为0
SetProcessingMode(InitProcessing); // 设置处理模式为InitProcessing
t_thrd.role = BARRIER_CREATOR;
t_thrd.proc_cxt.MyProgName = "BarrierCreator";
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
u_sess->attr.attr_common.application_name = pstrdup("BarrierCreator");
g_instance.barrier_creator_cxt.stop = false;
t_thrd.role = BARRIER_CREATOR; // 设置线程的角色为BARRIER_CREATOR
t_thrd.proc_cxt.MyProgName = "BarrierCreator"; // 设置线程的程序名称为"BarrierCreator"
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); // 设置线程的进程ID为当前线程ID
u_sess->attr.attr_common.application_name = pstrdup("BarrierCreator"); // 设置应用程序名称为"BarrierCreator"
g_instance.barrier_creator_cxt.stop = false; // 初始化停止标志为false表示Barrier Creator线程不停止
on_shmem_exit(PGXCNodeCleanAndRelease, 0);
on_shmem_exit(PGXCNodeCleanAndRelease, 0); // 注册一个在进程退出时执行的回调函数用于清理PGXC节点资源
barrier_creator_setup_signal_hook();
barrier_creator_setup_signal_hook(); // 设置信号处理函数
BaseInit();
BaseInit(); // 初始化
// 设置当前线程的数据库和用户信息
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username);
// 初始化Barrier Creator模块的上下文
t_thrd.proc_cxt.PostInit->InitBarrierCreator();
// 创建一个新的资源拥有者用于管理Barrier Creator线程的资源
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "BarrierCreator",
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
@ -302,6 +325,7 @@ void barrier_creator_main(void)
* possible memory leaks. Formerly this code just ran in
* t_thrd.top_mem_cxt, but resetting that would be a really bad idea.
*/
// 创建内存上下文
barrier_creator_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
"BarrierCreator",
ALLOCSET_DEFAULT_MINSIZE,
@ -313,83 +337,92 @@ void barrier_creator_main(void)
* If an exception is encountered, processing resumes here.
* See notes in postgres.c about the design of this coding.
*/
// 如果遇到异常,将从这里恢复
int curTryCounter;
int *oldTryCounter = NULL;
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
destroy_handles();
destroy_handles(); // 销毁句柄
gstrace_tryblock_exit(true, oldTryCounter);
/* Since not using PG_TRY, must reset error stack by hand */
// 重置错误堆栈
t_thrd.log_cxt.error_context_stack = NULL;
t_thrd.log_cxt.call_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
HOLD_INTERRUPTS(); // 在清理期间阻止中断
/* Report the error to the server log */
EmitErrorReport();
EmitErrorReport(); // 将错误报告记录到服务器日志中
/* release resource held by lsc */
AtEOXact_SysDBCache(false);
AtEOXact_SysDBCache(false); // 释放lsc持有的资源
/* release resource */
LWLockReleaseAll();
// 释放资源
LWLockReleaseAll(); // 释放所有的轻量级锁
FreeBarrierLsnInfo();
FreeBarrierLsnInfo(); // 释放Barrier信息结构体的内存
/*
* Now return to normal top-level context and clear ErrorContext for
* next time.
*/
MemoryContextSwitchTo(barrier_creator_context);
FlushErrorState();
MemoryContextResetAndDeleteChildren(barrier_creator_context);
MemoryContextSwitchTo(barrier_creator_context); // 切换到 barrier_creator_context 内存上下文
FlushErrorState(); // 清除错误状态信息
MemoryContextResetAndDeleteChildren(barrier_creator_context); // 重置并删除 barrier_creator_context 内存上下文的子节点
/* Now we can allow interrupts again */
RESUME_INTERRUPTS();
RESUME_INTERRUPTS(); // 恢复中断处理
return;
}
// 进入异常处理尝试块,保存旧的异常处理计数器并获取当前计数器
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
/* We can now handle ereport(ERROR) */
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; // 将本地的跳转缓冲区设置为异常处理的跳转缓冲区
/*
* Unblock signals (they were blocked when the postmaster forked us)
*/
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
SetProcessingMode(NormalProcessing);
exec_init_poolhandles();
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除先前阻塞的信号
(void)gs_signal_unblock_sigusr2(); // 解除对 SIGUSR2 信号的阻塞
SetProcessingMode(NormalProcessing); // 设置处理模式为正常处理模式
exec_init_poolhandles(); // 初始化连接池句柄
#ifdef ENABLE_MULTIPLE_NODES
/*
* Ensure all barrier commond execuet on first coordinator
*/
do {
if (IsFirstCn())
if (IsFirstCn()) // 如果当前节点是第一个协调器节点
break;
// 输出调试信息,指示当前节点不是第一个协调器节点
ereport(DEBUG1, (errmsg("[BarrierCreator] Current node is not first node: %s",
g_instance.attr.attr_common.PGXCNodeName)));
// 如果收到重新加载连接池的信号,重新加载连接池并重置标志
if (IsGotPoolReload()) {
BarrierCreatorPoolerReload();
ResetGotPoolReload(false);
BarrierCreatorPoolerReload(); // 重新加载连接池
ResetGotPoolReload(false); // 重置标志,表示已处理连接池重新加载
}
CHECK_FOR_INTERRUPTS();
pg_usleep(1000000L);
CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号,如果是则处理中断
pg_usleep(1000000L); // 休眠1秒钟
} while (1);
#endif
ereport(DEBUG1,
(errmsg("[BarrierCreator] Init connections with CN/DN, dn count : %d, cn count : %d",
u_sess->pgxc_cxt.NumDataNodes, u_sess->pgxc_cxt.NumCoords)));
// 输出日志,指明当前节点是 barrier creator
ereport(LOG, (errmsg("[BarrierCreator] %s is barrier creator", g_instance.attr.attr_common.PGXCNodeName)));
// 设置停止标志为 false表示不停止 barrier creator 线程
g_instance.barrier_creator_cxt.stop = false;
if (g_instance.archive_obs_cxt.archive_slot_num != 0) {
if (g_instance.archive_obs_cxt.archive_slot_num != 0) { // 如果存在归档槽位
t_thrd.barrier_creator_cxt.archive_slot_names = GetAllArchiveSlotsName();
if (t_thrd.barrier_creator_cxt.archive_slot_names == NIL ||
t_thrd.barrier_creator_cxt.archive_slot_names->length == 0) {
return;
return; // 没有获取到归档槽位名称,直接返回
}
// 获取归档槽位的 barrier 索引和last barrier 时间
index = GetObsBarrierIndex(t_thrd.barrier_creator_cxt.archive_slot_names, &last_barrier_time);
// 获取归档槽位的 barrier 索引和最后的 barrier 时间
t_thrd.barrier_creator_cxt.first_cn_timeline =
GetObsFirstCNBarrierTimeline(t_thrd.barrier_creator_cxt.archive_slot_names);
/*
@ -397,83 +430,94 @@ void barrier_creator_main(void)
* wait for a while to prevent barrier time rollback.
*/
do {
gettimeofday(&tv, NULL);
gettimeofday(&tv, NULL); // 获取当前时间
long current_time = TIME_GET_MILLISEC(tv);
if (last_barrier_time < current_time) {
if (last_barrier_time < current_time) { // 如果最后一个barrier时间比当前时间小则跳出循环
break;
}
// 计算时间差并打印日志信息
long time_diff = last_barrier_time - current_time;
ereport(LOG, (errmsg("[BarrierCreator] current time %ld is smaller than barrier time %ld, and sleep %ld ms",
current_time, last_barrier_time, time_diff)));
CHECK_FOR_INTERRUPTS();
pg_usleep(time_diff * 1000L);
CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号,如果有,则处理中断
pg_usleep(time_diff * 1000L); // 休眠指定的时间差
} while (1);
// 如果是第一个barrier记录全局barrier列表的开始时间
if (t_thrd.barrier_creator_cxt.is_first_barrier) {
gettimeofday(&tv, NULL);
WriteGlobalBarrierListStartTimeOnMedia(TIME_GET_MILLISEC(tv));
}
#ifdef ENABLE_MULTIPLE_NODES
// 如果启用了多节点模式
while (!START_AUTO_CSN_BARRIER) {
CHECK_FOR_INTERRUPTS();
pg_usleep(1000000L);
// 在未收到 START_AUTO_CSN_BARRIER 信号之前,循环等待
CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号,如果有,则处理中断
pg_usleep(1000000L); // 等待1秒
}
#endif
}
#ifdef ENABLE_MULTIPLE_NODES
CleanupBarrierLock();
// 如果启用了多节点模式
CleanupBarrierLock(); // 清理barrier锁
#endif
while (!g_instance.barrier_creator_cxt.stop) {
if (t_thrd.barrier_creator_cxt.got_SIGHUP) {
// 如果收到 SIGHUP 信号,执行配置文件处理
t_thrd.barrier_preparse_cxt.got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
startCsnBarrier = g_instance.attr.attr_storage.auto_csn_barrier;
}
/* in hadr switchover, barrier creator thread stop creating new barriers during service truncate.*/
// 如果归档槽数量不为0且服务截断标志为true
if (g_instance.archive_obs_cxt.archive_slot_num != 0 &&
g_instance.archive_obs_cxt.in_service_truncate == true) {
continue;
continue; // 在服务截断期间继续循环下一次迭代
}
if (g_instance.archive_obs_cxt.archive_slot_num != 0) {
if (g_instance.archive_obs_cxt.archive_slot_num != 0) { // 如果存在归档槽
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info == NULL) {
// 如果barrier更新的时间信息为空则分配内存并初始化为0
t_thrd.barrier_creator_cxt.barrier_update_last_time_info = (BarrierUpdateLastTimeInfo*)palloc0(
sizeof(BarrierUpdateLastTimeInfo) * g_instance.attr.attr_storage.max_replication_slots);
}
#ifdef ENABLE_MULTIPLE_NODES
if (g_instance.archive_obs_cxt.barrier_lsn_info == NULL) {
if (g_instance.archive_obs_cxt.barrier_lsn_info == NULL) { // 如果barrier LSN 信息为空
int nodeSize = *t_thrd.pgxc_cxt.shmemNumCoords + *t_thrd.pgxc_cxt.shmemNumDataNodes;
AllocBarrierLsnInfo(nodeSize);
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
g_instance.archive_obs_cxt.max_node_cnt = nodeSize;
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
AllocBarrierLsnInfo(nodeSize); // 分配barrier LSN 信息的内存
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock); // 获取全局barrier锁
g_instance.archive_obs_cxt.max_node_cnt = nodeSize; // 设置最大节点数为指定值
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放全局barrier锁
}
#endif
archiveSlotNames = GetAllArchiveSlotsName();
if (archiveSlotNames == NIL || archiveSlotNames->length == 0) {
archiveSlotNames = GetAllArchiveSlotsName(); // 获取所有归档槽的名称
if (archiveSlotNames == NIL || archiveSlotNames->length == 0) { // 如果无法获取归档槽名称,发出警告并返回
ereport(WARNING, (errmsg("[BarrierCreator] could not get archive slot name when barrier start")));
return;
}
if (t_thrd.barrier_creator_cxt.archive_slot_names == NULL) {
t_thrd.barrier_creator_cxt.archive_slot_names = archiveSlotNames;
if (t_thrd.barrier_creator_cxt.archive_slot_names == NULL) { // 如果归档槽名称尚未初始化
t_thrd.barrier_creator_cxt.archive_slot_names = archiveSlotNames; // 获取第一个协调器节点的时间线信息
t_thrd.barrier_creator_cxt.first_cn_timeline =
GetObsFirstCNBarrierTimeline(t_thrd.barrier_creator_cxt.archive_slot_names);
}
if (archiveSlotNames->length > t_thrd.barrier_creator_cxt.archive_slot_names->length) {
if (archiveSlotNames->length > t_thrd.barrier_creator_cxt.archive_slot_names->length) { // 如果当前归档槽数量大于之前记录的数量
t_thrd.barrier_creator_cxt.archive_slot_names = archiveSlotNames;
t_thrd.barrier_creator_cxt.is_first_barrier = true;
gettimeofday(&tv, NULL);
WriteGlobalBarrierListStartTimeOnMedia(TIME_GET_MILLISEC(tv));
t_thrd.barrier_creator_cxt.is_first_barrier = true; // 将标志位设为true表示是第一次创建屏障
gettimeofday(&tv, NULL); // 获取当前时间
WriteGlobalBarrierListStartTimeOnMedia(TIME_GET_MILLISEC(tv)); // 记录全局屏障列表的开始时间
} else if (archiveSlotNames->length < t_thrd.barrier_creator_cxt.archive_slot_names->length) {
// 如果当前归档槽数量小于之前记录的数量
t_thrd.barrier_creator_cxt.archive_slot_names = archiveSlotNames;
}
}
pg_usleep_retry(500000L, 0);
pg_usleep_retry(500000L, 0);
if (!startCsnBarrier && g_instance.archive_obs_cxt.archive_slot_num == 0) {
// 如果不需要启动CSN barrier且归档槽数量为0
g_instance.barrier_creator_cxt.stop = true;
for (int i = 0; i < g_instance.attr.attr_storage.max_replication_slots; i++) {
if (g_instance.archive_thread_info.obsBarrierArchPID[i] != 0) {
// 向子进程发送退出信号
signal_child(g_instance.archive_thread_info.obsBarrierArchPID[i], SIGUSR2, -1);
}
}
@ -483,41 +527,47 @@ void barrier_creator_main(void)
/* create barrier with increasing index */
#ifdef ENABLE_MULTIPLE_NODES
if (IsGotPoolReload()) {
if (IsGotPoolReload()) { // 如果收到重新加载的信号
BarrierCreatorPoolerReload();
ResetGotPoolReload(false);
if (!IsFirstCn())
if (!IsFirstCn()) // 如果当前节点不是第一个协调器节点
break;
}
ereport(DEBUG1, (errmsg("[BarrierCreator] auto_csn_barrier: %d", startCsnBarrier)));
if (startCsnBarrier) {
rc = snprintf_s(barrier_name, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_BARRIER_NAME);
if (startCsnBarrier) { // 如果启用了自动CSN barrier
rc = snprintf_s(barrier_name, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_BARRIER_NAME); //构造CSN barrier名称
securec_check_ss_c(rc, "\0", "\0");
RequestBarrier(barrier_name, NULL);
ereport(LOG, (errmsg("[BarrierCreator]barrier %s created", barrier_name)));
RequestBarrier(barrier_name, NULL); // 请求创建CSN barrier
ereport(LOG, (errmsg("[BarrierCreator]barrier %s created", barrier_name))); // 日志中记录barrier的创建
}
#else
//构造CSN barrier名称
rc = snprintf_s(barrier_name, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, "hadr_%020" PRIu64 "_%013ld", index,
TIME_GET_MILLISEC(tv));
securec_check_ss_c(rc, "\0", "\0");
DisasterRecoveryRequestBarrier(barrier_name);
ereport(LOG, (errmsg("[BarrierCreator] barrier %s created", barrier_name)));
DisasterRecoveryRequestBarrier(barrier_name); // 请求创建灾备 barrier
ereport(LOG, (errmsg("[BarrierCreator] barrier %s created", barrier_name))); // 日志中记录barrier的创建
#endif
index++;
}
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread exits.")));
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info != 0) {
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread exits."))); // 记录日志指示Barrier Creator线程即将退出
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info != 0) { // 检查是否分配了barrier_update_last_time_info结构的内存
// 循环遍历barrier_update_last_time_info结构数组以释放资源
for (int i = 0; i < g_instance.attr.attr_storage.max_replication_slots; i++) {
// 检查当前索引处的archiveSlotName是否不为NULL
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info[i].archiveSlotName != NULL) {
// 释放与archiveSlotName相关联的内存
pfree_ext(t_thrd.barrier_creator_cxt.barrier_update_last_time_info[i].archiveSlotName);
}
}
// 释放与barrier_update_last_time_info结构数组相关联的内存
pfree_ext(t_thrd.barrier_creator_cxt.barrier_update_last_time_info);
}
destroy_handles();
FreeBarrierLsnInfo();
// 执行清理操作
destroy_handles(); // 销毁句柄
FreeBarrierLsnInfo(); // 释放Barrier LSN信息
proc_exit(0);
}

View File

@ -40,17 +40,27 @@
#include "postmaster/barrier_preparse.h"
typedef struct XLogPageReadPrivate {
const char *datadir;
TimeLineID tli;
const char *datadir; // 存储数据库的数据目录路径
TimeLineID tli; // 存储 WAL 日志所在的时间线标识符
} XLogPageReadPrivate;
/*
* xl_rmid
* - RM_BARRIER_IDinfo为XLOG_BARRIER_SWITCHOVER
* info为XLOG_BARRIER_COMMIT
* info为XLOG_BARRIER_CREATE
*
*/
#define NEED_INSERT_INTO_HASH \
((record->xl_rmid == RM_BARRIER_ID) && ((info == XLOG_BARRIER_SWITCHOVER) || \
(IS_PGXC_COORDINATOR && info == XLOG_BARRIER_COMMIT) || (IS_PGXC_DATANODE && info == XLOG_BARRIER_CREATE)))
//初始化与barrier相关的哈希表的函数
static void InitBarrierHash()
{
// 检查是否已经创建了barrier上下文如果没有则创建一个
if (g_instance.csn_barrier_cxt.barrier_context == NULL) {
// 创建一个新的内存上下文命名为CsnBarrierContext用于存储与barrier相关信息
g_instance.csn_barrier_cxt.barrier_context = AllocSetContextCreate(g_instance.instance_context,
"CsnBarrierContext",
ALLOCSET_DEFAULT_MINSIZE,
@ -58,66 +68,72 @@ static void InitBarrierHash()
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
}
// 定义HASHCTL结构用于初始化哈希表的属性
HASHCTL ctl;
errno_t rc = 0;
/* Init hash table */
rc = memset_s(&ctl, sizeof(HASHCTL), 0, sizeof(HASHCTL));
rc = memset_s(&ctl, sizeof(HASHCTL), 0, sizeof(HASHCTL)); // 将ctl结构清零确保结构中的各字段正确初始化
securec_check(rc, "", "");
// 设置哈希表的键大小和每个条目的大小
ctl.keysize = MAX_BARRIER_ID_LENGTH * sizeof(char);
ctl.entrysize = MAX_BARRIER_ID_LENGTH * sizeof(char);
ctl.hash = string_hash;
ctl.hash = string_hash; // 设置哈希函数为string_hash用于计算键的哈希值
// 设置哈希表使用的上下文为之前创建的CsnBarrierContext上下文
ctl.hcxt = g_instance.csn_barrier_cxt.barrier_context;
// 创建屏障哈希表,指定上述哈希表的名称、初始大小和属性
g_instance.csn_barrier_cxt.barrier_hash_table = hash_create("Barrier Id Storage Table", INIBARRIERCACHESIZE,
&ctl, HASH_ELEM | HASH_FUNCTION | HASH_SHRCTX);
// 为屏障哈希表分配轻量级锁,用于并发访问控制
g_instance.csn_barrier_cxt.barrier_hashtbl_lock = LWLockAssign(LWTRANCHE_BARRIER_TBL);
}
// 设置barrier ID
static void SetBarrieID(const char *barrierId, XLogRecPtr lsn)
{
errno_t rc = EOK;
const uint32 shiftSize = 32;
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
SpinLockAcquire(&walrcv->mutex);
SpinLockAcquire(&walrcv->mutex); // 获取互斥锁,确保数据一致性
// 使用strncpy_s函数将barrierId复制到lastReceivedBarrierId中确保字符串安全性
rc = strncpy_s((char *)walrcv->lastReceivedBarrierId, MAX_BARRIER_ID_LENGTH, barrierId, MAX_BARRIER_ID_LENGTH - 1);
securec_check(rc, "\0", "\0");
walrcv->lastReceivedBarrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0';
walrcv->lastReceivedBarrierLSN = lsn;
SpinLockRelease(&walrcv->mutex);
walrcv->lastReceivedBarrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0'; // 在末尾添加字符串结束符
walrcv->lastReceivedBarrierLSN = lsn; // 设置最后接收到的barrier LSN
SpinLockRelease(&walrcv->mutex); // 释放互斥锁
// 输出日志记录设置的barrier ID和barrier LSN
ereport(LOG, (errmsg("SetBarrieID set the barrier ID is %s, the barrier LSN is %08X/%08X", barrierId,
(uint32)(lsn >> shiftSize), (uint32)lsn)));
}
// 处理SIGHUP信号
static void BarrierPreParseSigHupHandler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.barrier_preparse_cxt.got_SIGHUP = true;
t_thrd.barrier_preparse_cxt.got_SIGHUP = true; // 设置标志位表示收到SIGHUP信号
if (t_thrd.proc) {
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch); // 设置进程的Latch用于唤醒进程处理SIGHUP信号
}
errno = save_errno;
}
// 处理关闭请求信号
static void BarrierPreParseShutdownHandler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.barrier_preparse_cxt.shutdown_requested = true;
t_thrd.barrier_preparse_cxt.shutdown_requested = true; // 设置关闭请求标志为true表示收到了关闭请求信号
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch); // 设置线程的Latch用于唤醒线程处理关闭请求信号
errno = save_errno;
}
// 处理快速终止信号 用于在出现严重问题时强制终止进程
static void BarrierPreParseQuickDie(SIGNAL_ARGS)
{
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); // 解除对信号的阻塞,以便进行快速终止
/*
* We DO NOT want to run proc_exit() callbacks -- we're here because
@ -127,7 +143,7 @@ static void BarrierPreParseQuickDie(SIGNAL_ARGS)
* things by calling exit() directly, we have to reset the callbacks
* explicitly to make this work as intended.
*/
on_exit_reset();
on_exit_reset(); // 重置退出回调函数
/*
* Note we do exit(2) not exit(0). This is to force the postmaster into a
@ -137,105 +153,108 @@ static void BarrierPreParseQuickDie(SIGNAL_ARGS)
* should ensure the postmaster sees this as a crash, too, but no harm in
* being doubly sure.)
*/
exit(2);
exit(2); //以代码2 退出进程
}
// 处理SIGUSR1信号
static void BarrierPreParseSigUsr1Handler(SIGNAL_ARGS)
{
int saveErrno = errno;
int saveErrno = errno; // 保存当前错误码的值
latch_sigusr1_handler();
latch_sigusr1_handler(); // 处理SIGUSR1信号的回调函数
errno = saveErrno;
errno = saveErrno; // 恢复之前保存的错误码
}
/*
* Called when the BarrierPreParseMain is ending.
*/
// 在BarrierPreParseMain结束时调用
static void ShutdownBarrierPreParse(int code, Datum arg)
{
// 将BarrierPreParseLatch设为NULL表示关闭BarrierPreParse线程
g_instance.proc_base->BarrierPreParseLatch = NULL;
}
// 设置BarrierPreParse线程的LSN
void SetBarrierPreParseLsn(XLogRecPtr startptr)
{
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
SpinLockAcquire(&walrcv->mutex);
walrcv->lastReceivedBarrierLSN = startptr;
SpinLockRelease(&walrcv->mutex);
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv; // 获取WalRcvData结构体的指针
SpinLockAcquire(&walrcv->mutex); // 获取互斥锁,保证操作的原子性
walrcv->lastReceivedBarrierLSN = startptr; // 设置lastReceivedBarrierLSN为指定的LSN
SpinLockRelease(&walrcv->mutex); // 释放互斥锁
}
void BarrierPreParseMain(void)
{
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
MemoryContext preParseContext;
XLogRecord *record = NULL;
XLogReaderState *xlogreader = NULL;
char *errormsg = NULL;
XLogPageReadPrivate readprivate;
XLogRecPtr startLSN = InvalidXLogRecPtr;
XLogRecPtr preStartLSN = InvalidXLogRecPtr;
XLogRecPtr lastReadLSN = InvalidXLogRecPtr;
bool found = false;
XLogRecPtr barrierLSN = InvalidXLogRecPtr;
char *xLogBarrierId = NULL;
char barrierId[MAX_BARRIER_ID_LENGTH] = {0};
const uint32 shiftSize = 32;
int rc;
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv; // 获取WalRcvData结构体的指针
MemoryContext preParseContext; // 内存上下文,用于分配内存
XLogRecord *record = NULL; // XLog记录指针
XLogReaderState *xlogreader = NULL; // XLog读取器的状态结构体指针
char *errormsg = NULL; // 错误消息
XLogPageReadPrivate readprivate; // XLog页读取的私有数据
XLogRecPtr startLSN = InvalidXLogRecPtr; // 起始LSN
XLogRecPtr preStartLSN = InvalidXLogRecPtr; // 前一个起始LSN
XLogRecPtr lastReadLSN = InvalidXLogRecPtr; // 上一次读取的LSN
bool found = false; // 是否找到待处理的记录
XLogRecPtr barrierLSN = InvalidXLogRecPtr; // barrier记录的LSN
char *xLogBarrierId = NULL; // XLog中的barrierID
char barrierId[MAX_BARRIER_ID_LENGTH] = {0}; // barrier ID字符串
const uint32 shiftSize = 32; // 位移大小
int rc; // 函数返回值
ereport(LOG, (errmsg("[BarrierPreParse] barrier preparse thread started")));
ereport(LOG, (errmsg("[BarrierPreParse] barrier preparse thread started"))); // 记录日志,标记线程开始
/*
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGHUP, BarrierPreParseSigHupHandler);
(void)gspqsignal(SIGINT, SIG_IGN);
(void)gspqsignal(SIGTERM, BarrierPreParseShutdownHandler);
(void)gspqsignal(SIGQUIT, BarrierPreParseQuickDie); /* hard crash time */
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, BarrierPreParseSigUsr1Handler);
(void)gspqsignal(SIGUSR2, SIG_IGN);
(void)gspqsignal(SIGHUP, BarrierPreParseSigHupHandler); // 处理SIGHUP信号的回调函数
(void)gspqsignal(SIGINT, SIG_IGN); // 忽略SIGINT信号
(void)gspqsignal(SIGTERM, BarrierPreParseShutdownHandler); // 处理SIGTERM信号的回调函数
(void)gspqsignal(SIGQUIT, BarrierPreParseQuickDie); /* hard crash time */ // 处理SIGQUIT信号的回调函数用于快速终止
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略SIGALRM信号
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE信号
(void)gspqsignal(SIGUSR1, BarrierPreParseSigUsr1Handler); // 处理SIGUSR1信号的回调函数
(void)gspqsignal(SIGUSR2, SIG_IGN); // 忽略SIGUSR2信号
/*
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGCHLD, SIG_DFL);
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
(void)gspqsignal(SIGCONT, SIG_DFL);
(void)gspqsignal(SIGWINCH, SIG_DFL);
(void)gspqsignal(SIGCHLD, SIG_DFL); // 恢复SIGCHLD信号的默认处理
(void)gspqsignal(SIGTTIN, SIG_DFL); // 恢复SIGTTIN信号的默认处理
(void)gspqsignal(SIGTTOU, SIG_DFL); // 恢复SIGTTOU信号的默认处理
(void)gspqsignal(SIGCONT, SIG_DFL); // 恢复SIGCONT信号的默认处理
(void)gspqsignal(SIGWINCH, SIG_DFL); // 恢复SIGWINCH信号的默认处理
/* We allow SIGQUIT (quickdie) at all times */
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许在任何时间接收SIGQUIT 信号
on_shmem_exit(ShutdownBarrierPreParse, 0);
on_shmem_exit(ShutdownBarrierPreParse, 0); // 在进程退出时调用ShutdownBarrierPreParse函数
preParseContext = AllocSetContextCreate(t_thrd.top_mem_cxt, "Barrier PreParse", ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
(void)MemoryContextSwitchTo(preParseContext);
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); // 创建内存上下文
(void)MemoryContextSwitchTo(preParseContext); // 切换到preParseContext上下文
/*
* Unblock signals (they were blocked when the postmaster forked us)
*/
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除对信号的阻塞
(void)gs_signal_unblock_sigusr2(); // 解除对SIGUSR2信号的阻塞
g_instance.proc_base->BarrierPreParseLatch = &t_thrd.proc->procLatch;
g_instance.proc_base->BarrierPreParseLatch = &t_thrd.proc->procLatch; // 设置BarrierPreParseLatch
startLSN = walrcv->lastReceivedBarrierLSN;
startLSN = walrcv->lastReceivedBarrierLSN; // 获取上次接收的barrier LSN
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread start at %08X/%08X", (uint32)(startLSN >> shiftSize),
(uint32)startLSN)));
(uint32)startLSN))); // 记录日志,标记线程起始位置
if (g_instance.csn_barrier_cxt.barrier_hash_table == NULL) {
if (g_instance.csn_barrier_cxt.barrier_hash_table == NULL) {// 如果barrier哈希表为空初始化
InitBarrierHash();
}
readprivate.datadir = t_thrd.proc_cxt.DataDir;
readprivate.tli = GetRecoveryTargetTLI();
readprivate.datadir = t_thrd.proc_cxt.DataDir; // 设置读取私有数据的数据目录
readprivate.tli = GetRecoveryTargetTLI(); // 获取恢复目标的时间线ID
xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &readprivate);
xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &readprivate); // 分配XLog读取器
if (xlogreader == NULL)
// 如果分配失败,报错
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("memory is temporarily unavailable while allocate xlog reader")));
@ -244,91 +263,98 @@ void BarrierPreParseMain(void)
*/
for (;;) {
/* Clear any already-pending wakeups */
ResetLatch(&t_thrd.proc->procLatch);
ResetLatch(&t_thrd.proc->procLatch); //清除已挂起的唤醒
if (t_thrd.barrier_preparse_cxt.got_SIGHUP) {
t_thrd.barrier_preparse_cxt.got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
if (t_thrd.barrier_preparse_cxt.got_SIGHUP) { // 如果收到SIGHUP信号重新读取配置文件
t_thrd.barrier_preparse_cxt.got_SIGHUP = false; // 重置信号标志
ProcessConfigFile(PGC_SIGHUP); // 处理SIGHUP信号重新读取配置文件
}
if (t_thrd.barrier_preparse_cxt.shutdown_requested) {
if (t_thrd.barrier_preparse_cxt.shutdown_requested) { // 如果收到关闭请求,结束线程
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down")));
XLogReaderFree(xlogreader);
XLogReaderFree(xlogreader); // 释放XLog读取器的资源
proc_exit(0); /* done */
}
found = false;
preStartLSN = startLSN;
found = false; // 初始化found标志为false
preStartLSN = startLSN; // 保存上一次的起始LSN
// 记录日志,标记预解析开始位置
ereport(DEBUG1, (errmsg("[BarrierPreParse] start to preparse at: %08X/%08X",
(uint32)(startLSN >> shiftSize), (uint32)startLSN)));
startLSN = XLogFindNextRecord(xlogreader, startLSN);
if (XLogRecPtrIsInvalid(startLSN)) {
startLSN = preStartLSN;
startLSN = XLogFindNextRecord(xlogreader, startLSN); // 查找下一个XLog记录的LSN
if (XLogRecPtrIsInvalid(startLSN)) { // 如果找不到回到上一个起始LSN
startLSN = preStartLSN; // 使用之前记录的起始LSN
if (!XLByteEQ(walrcv->receiver_flush_location, startLSN) &&
!XLByteEQ(walrcv->lastRecoveredBarrierLSN, startLSN)) {
/* reset startLSN */
startLSN = walrcv->lastRecoveredBarrierLSN;
startLSN = walrcv->lastRecoveredBarrierLSN; // 使用上次恢复的Barrier LSN
ereport(LOG, (errmsg("[BarrierPreParse] reset startLSN with lastRecoveredBarrierLSN: %08X/%08X",
(uint32)(startLSN >> shiftSize), (uint32)startLSN)));
(uint32)(startLSN >> shiftSize), (uint32)startLSN))); // 记录日志标记重置startLSN
}
continue;
}
do {
// 从XLog中读取记录从startLSN开始
record = XLogReadRecord(xlogreader, startLSN, &errormsg);
if (record == NULL) {
if (record == NULL) { // 如果读取到了NULL记录即无法继续读取跳出循环
break;
}
lastReadLSN = xlogreader->EndRecPtr;
uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
lastReadLSN = xlogreader->EndRecPtr; // 记录最后读取的LSN
// 获取XLog记录的信息位去除XLR_INFO_MASK标志位
uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
if (NEED_INSERT_INTO_HASH) {
xLogBarrierId = XLogRecGetData(xlogreader);
if (!IS_CSN_BARRIER(xLogBarrierId)) {
// 如果需要将记录插入到哈希表中
xLogBarrierId = XLogRecGetData(xlogreader); // 获取XLog记录的数据部分
if (!IS_CSN_BARRIER(xLogBarrierId)) { // 如果不是用于备机集群的barrier记录
ereport(WARNING, (errmsg("[BarrierPreParse] %s is not for standby cluster", xLogBarrierId)));
} else {
// insert into hash table
found = true;
barrierLSN = xlogreader->EndRecPtr;
// 插入到哈希表中
found = true; // 标记找到适用于备用集群的barrier记录
barrierLSN = xlogreader->EndRecPtr; // 记录barrier的LSN
rc = strncpy_s((char *)barrierId, MAX_BARRIER_ID_LENGTH, xLogBarrierId, MAX_BARRIER_ID_LENGTH - 1);
securec_check(rc, "\0", "\0");
barrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0';
barrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0'; // 确保barrierId字符串有效性
// 获取哈希表锁插入barrierId
LWLockAcquire(g_instance.csn_barrier_cxt.barrier_hashtbl_lock, LW_EXCLUSIVE);
BarrierCacheInsertBarrierId(barrierId);
LWLockRelease(g_instance.csn_barrier_cxt.barrier_hashtbl_lock);
BarrierCacheInsertBarrierId(barrierId); // 将barrierId插入哈希表
LWLockRelease(g_instance.csn_barrier_cxt.barrier_hashtbl_lock); // 释放哈希表锁
// 记录日志说明插入了barrierId到哈希表中
ereport(LOG, (errmsg("[BarrierPreParse] insert barrierID %s to the hash table, rmid: %d, crc: %d.",
barrierId, record->xl_rmid, record->xl_crc)));
}
}
startLSN = InvalidXLogRecPtr;
} while (!t_thrd.barrier_preparse_cxt.shutdown_requested);
startLSN = InvalidXLogRecPtr; // 将startLSN重置为InvalidXLogRecPtr以便下次循环处理下一个XLog记录
} while (!t_thrd.barrier_preparse_cxt.shutdown_requested); // 收到关闭请求退出循环,否则继续
/* close xlogreadfd after circulation */
CloseXlogFile();
CloseXlogFile(); // 关闭当前使用的XLOG文件
if (found) {
if (found) { // 如果找到了需要插入到哈希表的barrier将其插入
SetBarrieID(barrierId, barrierLSN);
}
startLSN = XLogRecPtrIsInvalid(lastReadLSN) ? preStartLSN : lastReadLSN;
startLSN = XLogRecPtrIsInvalid(lastReadLSN) ? preStartLSN : lastReadLSN; // 更新起始LSN
if (XLogRecPtrIsInvalid(xlogreader->ReadRecPtr) && errormsg) {
if (XLogRecPtrIsInvalid(xlogreader->ReadRecPtr) && errormsg) { // 如果在解析过程中出现错误,记录错误信息
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread get an error info %s", errormsg)));
}
const long sleepTime = 1000;
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, sleepTime);
const long sleepTime = 1000; // 定义休眠时间为1000毫秒
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, sleepTime); // 等待信号量或超时
if (((unsigned int)rc) & WL_POSTMASTER_DEATH) {
XLogReaderFree(xlogreader);
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down with code 1")));
gs_thread_exit(1);
XLogReaderFree(xlogreader); // 释放XLogReader资源
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down with code 1"))); // 记录线程以代码1关闭的日志
gs_thread_exit(1);// 以代码1退出线程
}
}
}
// 用于唤醒 BarrierPreParse 后台进程
void WakeUpBarrierPreParseBackend()
{
if (g_instance.pid_cxt.BarrierPreParsePID != 0) {
if (g_instance.proc_base->BarrierPreParseLatch != NULL) {
SetLatch(g_instance.proc_base->BarrierPreParseLatch);
if (g_instance.pid_cxt.BarrierPreParsePID != 0) { // 如果存在BarrierPreParse进程
if (g_instance.proc_base->BarrierPreParseLatch != NULL) { // 如果存在BarrierPreParse进程的Latch
SetLatch(g_instance.proc_base->BarrierPreParseLatch); // 触发Latch唤醒BarrierPreParse进程
}
}
}

View File

@ -25,119 +25,163 @@
#include "utils/snapmgr.h"
#include "commands/dbcommands.h"
#include "pgstat.h"
// 外部函数声明:
// 函数声明StreamSaveTxnContext用于保存流复制事务上下文
extern void StreamSaveTxnContext(StreamTxnContext* stc);
// 函数声明StreamRestoreTxnContext用于恢复流复制事务上下文
extern void StreamRestoreTxnContext(StreamTxnContext* stc);
// 函数声明CopySnapshotByCurrentMcxt通过当前内存上下文复制快照
extern Snapshot CopySnapshotByCurrentMcxt(Snapshot snapshot);
// 函数声明SetGlobalSnapshotData设置全局快照数据
extern void SetGlobalSnapshotData(
TransactionId xmin, TransactionId xmax, uint64 csn, GTM_Timeline timeline, bool ssNeedSyncWaitAll);
// 全局变量:最大后台工作进程数,初始值为 64
int g_max_worker_processes = 64;
/*
* Return true if the thread is bgworker.
*/
// 判断当前进程是否为后台工作进程
// 返回值:若为后台工作进程,返回 true否则返回 false
bool IsBgWorkerProcess(void)
{
return t_thrd.role == BGWORKER;
return t_thrd.role == BGWORKER; // 返回当前线程是否为 BGWORKER后台工作进程
}
// 内联函数定义BgworkerPutBackToFreeList
// 功能:将后台工作进程放回空闲列表
// 参数bgworker - 后台工作进程指针
static inline void BgworkerPutBackToFreeList(BackgroundWorker* bgworker)
{
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;// 获取后台工作进程头指针
// 使用 memset_s 清空 bgworker 结构体的内容,确保隐私信息被清除
errno_t rc = memset_s(bgworker, sizeof(BackgroundWorker), 0, sizeof(BackgroundWorker));
securec_check(rc, "", "");
securec_check(rc, "", ""); // 检查正确性
// 将 bgworker 放回空闲后台工作进程链表
bgworker->links.next = (SHM_QUEUE *)bgworker_base->free_bgws;
bgworker_base->free_bgws = bgworker;
}
// 内联函数定义GetFreeBgworker
// 功能:获取空闲后台工作进程
// 返回值:获取的空闲后台工作进程指针,若没有可用的返回 NULL
static inline BackgroundWorker* GetFreeBgworker()
{
// 获取后台工作进程头指针
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;
// 若空闲后台工作进程链表为空,则返回 NULL
if (!bgworker_base->free_bgws) {
return NULL;
}
// 从空闲后台工作进程链表中获取一个后台工作进程
BackgroundWorker* bgworker = bgworker_base->free_bgws;
bgworker_base->free_bgws = (BackgroundWorker *)bgworker->links.next;
return bgworker;
}
// 初始化后台工作进程全局数据
void InitBgworkerGlobal(void)
{
BGW_HDR* bgworker_base = NULL;
BackgroundWorker* bgws = NULL;
bool needPalloc = false;
BGW_HDR* bgworker_base = NULL; // 后台工作进程头指针
BackgroundWorker* bgws = NULL; // 后台工作进程指针
bool needPalloc = false; // 是否需要进行 palloc 内存分配
// 切换内存上下文到 MEMORY_CONTEXT_CBB 组中
MemoryContext oldContext = MemoryContextSwitchTo(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB));
// 若 g_instance.bgw_base 为空,表示尚未创建后台工作进程共享结构
if (g_instance.bgw_base == NULL) {
/* Create the g_instance.proc_base shared structure */
// 创建 g_instance.bgw_base 共享结构,为其分配内存,确保地址对齐
bgworker_base = (BGW_HDR *)CACHELINEALIGN(palloc(sizeof(BGW_HDR) + PG_CACHE_LINE_SIZE));
// 将分配的内存设置为后台工作进程共享结构
g_instance.bgw_base = (void *)bgworker_base;
needPalloc = true;
needPalloc = true;// 标记需要进行内存分配
} else {
// 将 g_instance.bgw_base 转换为 BGW_HDR 指针,表示后台工作进程共享头部
bgworker_base = (BGW_HDR *)g_instance.bgw_base;
// 断言确保 bgworker_base 的 bgws 指针不为空,即后台工作进程数组已存在
Assert(bgworker_base->bgws != NULL);
}
// 初始化 bgworker_base 的 bgw_id_seq 计数为 1用于跟踪后台工作进程的唯一标识符
pg_atomic_init_u64(&bgworker_base->bgw_id_seq, 1);
// 如果需要进行内存分配
if (needPalloc) {
// 分配足够大小的内存用于存储后台工作进程数组,确保地址对齐
bgws = (BackgroundWorker*)CACHELINEALIGN(
palloc0(g_max_worker_processes * sizeof(BackgroundWorker) + PG_CACHE_LINE_SIZE));
bgworker_base->bgws = bgws;
} else {
bgws = bgworker_base->bgws;
bgworker_base->bgws = bgws; // 将分配的内存设置为后台工作进程数组
} else { // 如果不需要进行内存分配
bgws = bgworker_base->bgws; // 直接获取已存在的后台工作进程数组指针
}
// 将所有后台工作进程放回空闲列表
for (int i = 0; i < g_max_worker_processes; i++) {
BgworkerPutBackToFreeList(&bgws[i]);
}
// 初始化后台工作进程数据锁
pthread_mutex_init(&g_instance.bgw_base_lock, NULL);
// 切换回原来的内存上下文
MemoryContextSwitchTo(oldContext);
}
// 设置后台工作进程的事务环境
void SetUpBgWorkerTxnEnvironment()
{
/* resotre transaction context. */
// 获取后台工作进程上下文
BgWorkerContext *bwc = (BgWorkerContext *)t_thrd.bgworker_cxt.bgwcontext;
// 恢复事务上下文
StreamRestoreTxnContext(&bwc->transactionCxt);
/* transaction id. */
// SetNextTransactionId 函数的第二个参数用于控制是否自动增加事务ID。
// 当设为 false 时函数不会自动增加事务ID而是使用传递的 txnId 参数作为下一个事务ID
// 确保后续的事务使用指定的事务ID保持一致性
SetNextTransactionId(bwc->transactionCxt.txnId, false);
StreamTxnContextSetTransactionState(&bwc->transactionCxt);
StreamTxnContextSetTransactionState(&bwc->transactionCxt); // 设置事务状态为当前状态
/* snapshot. */
Snapshot snapshot = CopySnapshotByCurrentMcxt(bwc->transactionCxt.snapshot);
SetGlobalSnapshotData(snapshot->xmin, snapshot->xmax, snapshot->snapshotcsn, snapshot->timeline, false);
StreamTxnContextSetSnapShot(snapshot);
StreamTxnContextSetMyPgXactXmin(snapshot->xmin);
Snapshot snapshot = CopySnapshotByCurrentMcxt(bwc->transactionCxt.snapshot); // 复制当前内存上下文中的快照
// SetGlobalSnapshotData 函数的最后一个参数用于控制是否需要等待所有事务同步完成。
// 当设为 false 时,函数将不会等待所有事务同步完成,而是立即返回,以提高响应速度和效率
SetGlobalSnapshotData(snapshot->xmin, snapshot->xmax, snapshot->snapshotcsn, snapshot->timeline, false); // 设置全局快照数据
StreamTxnContextSetSnapShot(snapshot); // 将快照信息设置到流复制事务上下文中
StreamTxnContextSetMyPgXactXmin(snapshot->xmin); // 设置 PGXACT 的 xmin
/* command id. */
SaveReceivedCommandId(bwc->transactionCxt.currentCommandId);
SaveReceivedCommandId(bwc->transactionCxt.currentCommandId); // 保存接收到的命令ID
/* timestamp. */
SetCurrentGTMDeltaTimestamp();
SetCurrentGTMDeltaTimestamp(); // 设置当前的 GTM Delta 时间戳
}
// 保存后台工作进程的错误信息
static void BgWorkerSaveError()
{
// 获取当前后台工作进程的指针
BackgroundWorker *bgw = (BackgroundWorker *)t_thrd.bgworker_cxt.bgworker;
// 获取当前错误信息的指针
ErrorData *edata = &t_thrd.log_cxt.errordata[t_thrd.log_cxt.errordata_stack_depth];
errno_t rc = EOK;
int len;
// 设置错误信息和详情的默认字符串
char *failmsg = "Worker failed during parallel build index.";
char *nulldetail = "N/A";
// 将错误级别和 SQL 错误码保存到 bgw_edata 结构
bgw->bgw_edata.elevel = edata->elevel;
bgw->bgw_edata.sqlerrcode = edata->sqlerrcode;
// 获取错误消息,如果为空则使用默认消息
char *message = (edata->message != NULL ? edata->message : failmsg);
// 限制消息长度,并复制到 bgw_edata 的 message 字段
len = Min(strlen(message), BGWORKER_MAX_ERROR_LEN - 1);
rc = strncpy_s(bgw->bgw_edata.message, BGWORKER_MAX_ERROR_LEN, message, len);
// 确保字符串的复制操作不会造成缓冲区溢出,增加代码的健壮性
securec_check_c(rc, "", "");
bgw->bgw_edata.message[len] = '\0';
bgw->bgw_edata.message[len] = '\0'; // 确保字符串以 C 字符串的形式结束
// 获取错误详情,如果为空则使用默认详情
char *detail = (edata->detail != NULL ? edata->detail : nulldetail);
// 限制详情长度,并复制到 bgw_edata 的 detail 字段
len = Min(strlen(detail), BGWORKER_MAX_ERROR_LEN - 1);
rc = strncpy_s(bgw->bgw_edata.detail, BGWORKER_MAX_ERROR_LEN, detail, len);
securec_check_c(rc, "", "");
@ -147,43 +191,55 @@ static void BgWorkerSaveError()
/*
* Called when the Bgworker thread is ending.
*/
/*
* BgworkerQuitAndClean
* 线
* :
* code: 退
* arg:
*/
static void BgworkerQuitAndClean(int code, Datum arg)
{
// 获取当前后台工作进程的指针
BackgroundWorker *bgw = (BackgroundWorker *)t_thrd.bgworker_cxt.bgworker;
// 根据后台工作进程的状态设置相应状态
if (bgw->bgw_status == BGW_STOPPED) {
bgw->bgw_status = BGW_TERMINATED;
bgw->bgw_status = BGW_TERMINATED; // 将状态更新为 BGW_TERMINATED表示进程已正常终止
} else {
bgw->bgw_status = BGW_FAILED;
bgw->bgw_status = BGW_FAILED; // 将状态更新为 BGW_FAILED表示进程因某种原因失败
}
}
// 后台工作进程的初始化函数
static void BackgroundWorkerInit(void)
{
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
t_thrd.role = BGWORKER;
IsUnderPostmaster = true; // 将 IsUnderPostmaster 标志设置为 true表示当前进程是在后台运行
t_thrd.role = BGWORKER; // 设置当前线程的角色为 BGWORKER表示当前线程是一个后台工作进程
/* reset t_thrd.proc_cxt.MyProcPid */
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); // 重置当前线程的进程ID将其设置为当前线程的实际线程ID
t_thrd.proc_cxt.MyProgName = "BgWorker";
t_thrd.proc_cxt.MyProgName = "BgWorker"; // 设置当前线程的进程名称为 "BgWorker"
/* record Start Time for logging */
t_thrd.proc_cxt.MyStartTime = time(NULL);
t_thrd.proc_cxt.MyStartTime = time(NULL); // 记录当前线程的开始时间,用于日志记录
init_ps_display("Bgworker process", "", "", "");
init_ps_display("Bgworker process", "", "", ""); // 初始化显示进程状态
SetProcessingMode(InitProcessing);
SetProcessingMode(InitProcessing); // 设置当前的处理模式为初始化阶段
on_proc_exit(BgworkerQuitAndClean, 0);
on_proc_exit(BgworkerQuitAndClean, 0); // 在进程退出时调用 BgworkerQuitAndClean 函数,执行清理操作
/*
* SIGINT is used to signal canceling the current action
*/
// 为 SIGINT、SIGTERM 和 SIGALRM 信号设置相应的处理函数
(void)gspqsignal(SIGINT, StatementCancelHandler);
(void)gspqsignal(SIGTERM, die);
(void)gspqsignal(SIGALRM, handle_sig_alarm);
// 对于 SIGQUIT、SIGPIPE、SIGUSR1 、SIGUSR2 和 SIGHUP 信号,设置忽略处理
(void)gspqsignal(SIGQUIT, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, SIG_IGN);
@ -191,6 +247,7 @@ static void BackgroundWorkerInit(void)
(void)gspqsignal(SIGHUP, SIG_IGN);
/* Reset some signals that are accepted by postmaster but not here */
// 重置一些在 postmaster 中接受但在这里不接受的信号的处理方式
(void)gspqsignal(SIGCHLD, SIG_DFL);
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
@ -198,11 +255,11 @@ static void BackgroundWorkerInit(void)
(void)gspqsignal(SIGWINCH, SIG_DFL);
/* Early initialization */
BaseInit();
BaseInit(); // 执行早期初始化操作
#ifndef EXEC_BACKEND
InitProcess();
#endif
InitProcess(); // 初始化进程数据结构和状态
#endif
}
/*
@ -211,6 +268,7 @@ static void BackgroundWorkerInit(void)
* This is the main entry point for background worker, to be called from
* postmaster.
*/
// 后台工作进程的主要入口函数,从 postmaster 被调用
void BackgroundWorkerMain(void)
{
BgWorkerContext *bwc = (BgWorkerContext *)t_thrd.bgworker_cxt.bgwcontext;
@ -222,57 +280,67 @@ void BackgroundWorkerMain(void)
int *oldTryCounter = NULL;
int curTryCounter;
// 获取后台工作进程上下文锁,用于确保安全访问 bgworker 数据结构
pthread_mutex_lock(&g_instance.bgw_base_lock);
// 如果 bgwId 与 bgw->bgw_id 不匹配,或者已经被禁用,则退出
if (bgwId != bgw->bgw_id || pg_atomic_fetch_add_u32(&bgw->disable_count, 1) > 0) {
/* The leader disallowed this worker to do index build due to startup time longer than 5s. */
ereport(WARNING, (errmsg("BgWorker thread %lu was disabled for long startup time.",
t_thrd.proc_cxt.MyProcPid)));
t_thrd.proc_cxt.MyProcPid))); // 报告警告信息
/* Note that we are in the state BGW_NOT_YET_STARTED. */
pthread_mutex_unlock(&g_instance.bgw_base_lock);
// 解锁并跳转到 out 标签,退出函数
pthread_mutex_unlock(&g_instance.bgw_base_lock);
goto out;
}
pthread_mutex_unlock(&g_instance.bgw_base_lock);
pthread_mutex_unlock(&g_instance.bgw_base_lock); // 解锁
BackgroundWorkerInit();
BackgroundWorkerInit(); // 初始化后台工作进程
// 创建一个内存上下文来管理后台工作进程的内存
workerContext = AllocSetContextCreate(t_thrd.top_mem_cxt, "BgWorker", ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
(void)MemoryContextSwitchTo(workerContext);
(void)MemoryContextSwitchTo(workerContext); // 切换到工作内存上下文
/* Unblock signals (they were blocked when the postmaster forked us) */
// 解除对信号的阻塞(在 postmaster fork 时会阻塞信号)
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
/* If an exception is encountered, processing resumes here. */
// 如果遇到异常,处理将会跳转到此处
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
gstrace_tryblock_exit(true, oldTryCounter); // 退出异常处理块,恢复之前的 try counter
/* Since not using PG_TRY, must reset error stack by hand */
// 手动重置错误堆栈和调用堆栈
t_thrd.log_cxt.error_context_stack = NULL;
t_thrd.log_cxt.call_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
HOLD_INTERRUPTS(); // 在清理期间阻止中断
/* save bgworker error data for leader reporting */
BgWorkerSaveError();
BgWorkerSaveError(); // 保存 bgworker 错误信息
/* Report the error to the parallel leader and the server log */
EmitErrorReport();
EmitErrorReport(); // 报告错误给主进程和日志
/* release resource held by lsc */
AtEOXact_SysDBCache(false);
AtEOXact_SysDBCache(false); // 释放系统缓存中的资源
/*
* These operations are really just a minimal subset of
* AbortTransaction(). We don't have very many resources to worry
* about in bgwriter, but we do have LWLocks, buffers, and temp files.
*/
LWLockReleaseAll();
AbortBufferIO();
UnlockBuffers();
LWLockReleaseAll(); // 释放所有的轻量级锁
AbortBufferIO(); // 中止所有的缓冲区输入/输出操作
UnlockBuffers(); // 解锁所有缓冲区
/* buffer pins are released here */
// 如果当前资源拥有者存在,进行资源释放
if (t_thrd.utils_cxt.CurrentResourceOwner != NULL) {
// 释放资源拥有者的资源,并指定 RESOURCE_RELEASE_BEFORE_LOCKS 模式
// 第三个参数为 false表示不释放连接级别的资源
// 第四个参数为 true表示在释放资源后也执行锁的释放
ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true);
}
@ -280,6 +348,7 @@ void BackgroundWorkerMain(void)
* Now return to normal top-level context and clear ErrorContext for
* next time.
*/
// 切换回原始的内存上下文,刷新错误状态并清理内存
(void)MemoryContextSwitchTo(workerContext);
FlushErrorState();
@ -287,35 +356,44 @@ void BackgroundWorkerMain(void)
MemoryContextResetAndDeleteChildren(workerContext);
/* and go away */
proc_exit(1);
proc_exit(1); // 退出进程
}
// 获取旧的 try 计数器的值,以便在异常恢复时进行恢复
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
/* We can now handle ereport(ERROR) */
// 设置异常处理机制的跳转点,以便在发生错误时进行处理
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
// 设置会话的开始时间戳
u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp();
bgw->bgw_status = BGW_STARTED;
bgw->bgw_status = BGW_STARTED; // 设置 bgworker 状态
/* General initialization. */
/* user_name and database_name in u_sess->proc_cxt.MyProcPort is under t_thrd.top_mem_cxt */
// 切换到存储上下文,对 MyProcPort 的数据库名和用户名进行重置
oldcontext = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
// 如果当前 MyProcPort 的数据库名不为 NULL则释放其内存
if (u_sess->proc_cxt.MyProcPort->database_name != NULL) {
pfree_ext(u_sess->proc_cxt.MyProcPort->database_name);
}
// 如果当前 MyProcPort 的用户名不为 NULL则释放其内存
if (u_sess->proc_cxt.MyProcPort->user_name != NULL) {
pfree_ext(u_sess->proc_cxt.MyProcPort->user_name);
}
// 设置 MyProcPort 的数据库名和用户名
u_sess->proc_cxt.MyProcPort->database_name = pstrdup(bwc->databaseName);
u_sess->proc_cxt.MyProcPort->user_name = pstrdup(bwc->userName);
// 切换回之前的上下文
(void)MemoryContextSwitchTo(oldcontext);
// 设置数据库和用户,初始化后台工作
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(bwc->databaseName, InvalidOid, bwc->userName);
t_thrd.proc_cxt.PostInit->InitBgWorker();
t_thrd.proc_cxt.PostInit->GetDatabaseName(u_sess->proc_cxt.MyProcPort->database_name);
// 记录日志,表示 bgworker 的线程 ID
ereport(LOG, (errmsg("bgworker threadId is %lu.", t_thrd.proc_cxt.MyProcPid)));
StartTransactionCommand();
SetUpBgWorkerTxnEnvironment();
StartTransactionCommand(); // 开启事务
SetUpBgWorkerTxnEnvironment(); // 设置后台工作的事务环境
/*
* Join locking group. We must do this before anything that could try to
@ -326,21 +404,28 @@ void BackgroundWorkerMain(void)
* deadlock. (If we can't join the lock group, the leader has gone away,
* so just exit quietly.)
*/
BecomeLockGroupMember(bwc->leader);
/*
退退
*/
BecomeLockGroupMember(bwc->leader); // 加入锁定组,避免死锁
u_sess->attr.attr_sql.enable_cluster_resize = bwc->enable_cluster_resize;
u_sess->attr.attr_sql.enable_cluster_resize = bwc->enable_cluster_resize; // 设置是否启用集群调整大小
/*
* Now invoke the user-defined worker code
*/
bwc->main_entry(bwc);
bwc->main_entry(bwc); // 调用用户定义的后台工作函数
// 结束并重置后台工作的事务
EndParallelWorkerTransaction();
ResetTransactionInfo();
/* ... and if it returns, we're done */
bgw->bgw_status = BGW_STOPPED;
bgw->bgw_status = BGW_STOPPED; // 设置 bgworker 状态为已停止
out:
proc_exit(0);
proc_exit(0); // 退出进程
}
/*
@ -349,27 +434,46 @@ out:
* This can only be called in the _PG_init function of a module library
* that's loaded by shared_preload_libraries; otherwise it has no effect.
*/
/*
* shared_preload_libraries
*
* _PG_init shared_preload_libraries
*
*
*
* :
* bwc -
*
* :
* true false
*/
bool RegisterBackgroundWorker(BgWorkerContext *bwc)
{
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base; // 获取指向后台工作进程池的指针
// 声明后台工作进程结构和后台工作进程参数结构的指针
BackgroundWorker *bgw = NULL;
BackgroundWorkerArgs *bwa = NULL;
// 加锁,以便在操作后台工作进程池时保持同步
pthread_mutex_lock(&g_instance.bgw_base_lock);
bgw = GetFreeBgworker();
if (bgw == NULL) {
pthread_mutex_unlock(&g_instance.bgw_base_lock);
bgw = GetFreeBgworker(); // 获取一个空闲的后台工作进程
if (bgw == NULL) { // 如果没有空闲的后台工作进程可用
pthread_mutex_unlock(&g_instance.bgw_base_lock); // 解锁后台工作进程池
// 输出警告信息,表示没有空闲的后台工作进程可用
ereport(WARNING, (errmsg("There are no more free background workers available")));
return false;
return false; // 返回 false表示注册失败
}
// 为该后台工作进程分配唯一的 ID
bgw->bgw_id = pg_atomic_fetch_add_u64(&bgworker_base->bgw_id_seq, 1);
pthread_mutex_unlock(&g_instance.bgw_base_lock);
pthread_mutex_unlock(&g_instance.bgw_base_lock); // 解锁后台工作进程池
// 设置后台工作进程的状态为尚未启动,以及相关的状态持续时间和禁用计数
bgw->bgw_status = BGW_NOT_YET_STARTED;
bgw->bgw_status_dur = 0;
bgw->disable_count = 0;
/* Construct bgworker thread args */
// 构造后台工作线程参数
bwa = (BackgroundWorkerArgs*)MemoryContextAllocZero(
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), sizeof(BackgroundWorkerArgs));
bwa->bgwcontext = bwc;
@ -377,153 +481,221 @@ bool RegisterBackgroundWorker(BgWorkerContext *bwc)
bwa->bgworkerId = bgw->bgw_id;
/* Fork a new worker thread */
// 创建一个新的工作线程
bgw->bgw_notify_pid = initialize_util_thread(BGWORKER, bwa);
/* failed to fork a new thread */
// 如果创建线程失败
if (bgw->bgw_notify_pid == 0) {
pfree_ext(bwa);
return false;
pfree_ext(bwa); // 释放分配的参数内存
return false; // 返回注册失败
}
/* Copy the registration data into the registered workers list. */
slist_push_head(&t_thrd.bgworker_cxt.bgwlist, &bgw->rw_lnode);
return true;
slist_push_head(&t_thrd.bgworker_cxt.bgwlist, &bgw->rw_lnode); // 将注册数据复制到已注册工作线程列表中
return true; // 返回注册成功
}
// 功能:清理后台工作进程的共享上下文
static void BgworkerCleanupSharedContext()
{
Assert(!IsBgWorkerProcess());
Assert(!IsBgWorkerProcess()); // 确保不是后台工作进程调用该函数
/* clean up backgroud shared context */
if (t_thrd.bgworker_cxt.bgwcontext) {
if (t_thrd.bgworker_cxt.bgwcontext) { // 如果存在后台工作上下文
BgWorkerContext *bwc = (BgWorkerContext*)t_thrd.bgworker_cxt.bgwcontext;
if (bwc->exit_entry) {
bwc->exit_entry(bwc);
if (bwc->exit_entry) { // 如果有退出函数,即程序退出时执行操作
bwc->exit_entry(bwc); // 调用
}
pfree_ext(bwc->bgshared);
pfree_ext(t_thrd.bgworker_cxt.bgwcontext);
pfree_ext(bwc->bgshared); // 释放分配的后台工作共享内存
pfree_ext(t_thrd.bgworker_cxt.bgwcontext); // 释放分配的后台工作上下文内存
}
slist_init(&t_thrd.bgworker_cxt.bgwlist);
slist_init(&t_thrd.bgworker_cxt.bgwlist); // 初始化后台工作列表
}
/*
*
*
*
*
*
*/
void BgworkerListSyncQuit()
{
slist_mutable_iter iter;
bool alldone = false;
bool sigsent = false;
// 如果后台工作进程列表为空,直接返回
if (slist_is_empty(&t_thrd.bgworker_cxt.bgwlist)) {
return;
}
loop:
alldone = true;
// 遍历后台工作进程列表
slist_foreach_modify(iter, &t_thrd.bgworker_cxt.bgwlist) {
// 获取当前遍历到的后台工作进程
BackgroundWorker *bgw = slist_container(BackgroundWorker, rw_lnode, iter.cur);
// 如果后台工作进程状态为 BGW_FAILED 或 BGW_TERMINATED
if (bgw->bgw_status == BGW_FAILED || bgw->bgw_status == BGW_TERMINATED) {
slist_delete_current(&iter);
slist_delete_current(&iter); // 从列表中删除后台工作进程
// 获取全局后台工作进程列表互斥锁,防止多个线程同时访问列表
pthread_mutex_lock(&g_instance.bgw_base_lock);
// 将当前后台工作进程放回空闲列表
BgworkerPutBackToFreeList(bgw);
// 释放全局后台工作进程列表互斥锁,允许其他线程访问列表
pthread_mutex_unlock(&g_instance.bgw_base_lock);
} else if (bgw->bgw_status == BGW_NOT_YET_STARTED) {
} else if (bgw->bgw_status == BGW_NOT_YET_STARTED) { // 如果后台工作进程状态为 BGW_NOT_YET_STARTED
alldone = false;
// 增加状态持续时间,如果超过限制且尚未禁用,则标记为失败
if (++bgw->bgw_status_dur > BGWORKER_STATUS_DURLIMIT &&
(pg_atomic_fetch_add_u32(&bgw->disable_count, 1) == 0)) {
bgw->bgw_status = BGW_FAILED;
}
} else {
// 如果后台工作进程状态为其他值
// 如果之前没有发送过信号且成功地发送了 SIGINT 信号给指定的进程
if (!sigsent && gs_signal_send(bgw->bgw_notify_pid, SIGINT) != 0) {
ereport(WARNING, (errmsg("BgworkerListSyncQuit kill(pid %lu, stat %d) failed: %m",
bgw->bgw_notify_pid, bgw->bgw_status)));
bgw->bgw_notify_pid, bgw->bgw_status))); // 警告消息记录到日志
}
alldone = false;
alldone = false; // 表示尚未完成所有后台工作进程的处理。
}
}
// 如果未完成遍历,等待一段时间并继续
if (!alldone) {
usleep(BGWORKER_LOOP_SLEEP_TIME);
sigsent = true;
goto loop;
}
// 清理后台工作进程的共享上下文
BgworkerCleanupSharedContext();
}
/*
*
*
* BGW_NOT_YET_STARTED
*
*
*
* nunstarts
*/
static inline void CleanupUnstartBgworkers(int nunstarts)
{
slist_mutable_iter iter;
// 如果存在未能成功启动的后台工作进程
if (nunstarts > 0) {
// 遍历后台工作进程列表
slist_foreach_modify(iter, &t_thrd.bgworker_cxt.bgwlist) {
// 获取当前迭代器指向的后台工作进程结构
BackgroundWorker *bgw = slist_container(BackgroundWorker, rw_lnode, iter.cur);
// 如果当前后台工作进程的状态为 BGW_NOT_YET_STARTED即未能成功启动
if (bgw->bgw_status == BGW_NOT_YET_STARTED) {
/* the bgworker thread is unable to start, remove it from the waiting list */
slist_delete_current(&iter);
pthread_mutex_lock(&g_instance.bgw_base_lock);
BgworkerPutBackToFreeList(bgw);
pthread_mutex_unlock(&g_instance.bgw_base_lock);
slist_delete_current(&iter); // 从列表中删除当前迭代器指向的元素
pthread_mutex_lock(&g_instance.bgw_base_lock); // 获取全局互斥锁,以便对全局数据进行修改
BgworkerPutBackToFreeList(bgw); // 将未能成功启动的后台工作进程返回到空闲列表中
pthread_mutex_unlock(&g_instance.bgw_base_lock); // 释放全局互斥锁
}
}
}
}
/*
* BgworkerListWaitFinish
*
*
*
*
* nparticipants
*/
void BgworkerListWaitFinish(int *nparticipants)
{
slist_iter iter;
bool alldone = false;
uint32 disable_count;
int nfinished;
int nunstarts = 0;
bool alldone = false; // 表示是否所有后台工作者都已完成任务
uint32 disable_count; // 用于存储禁用计数的变量
int nfinished; // 记录已完成任务的后台工作者数量
int nunstarts = 0; // 记录未能启动的后台工作者数量
Assert(nparticipants != NULL);
Assert(nparticipants != NULL); // 断言,确保传入的参数 nparticipants 不为空
// 在等待状态报告中设置当前状态为等待同步的后台工作者
WaitState oldStatus = pgstat_report_waitstatus(STATE_WAIT_SYNC_BGWORKERS);
// 循环,直到所有后台工作者都完成了任务
while (!alldone) {
nfinished = 0;
// 遍历后台工作者列表
slist_foreach(iter, &t_thrd.bgworker_cxt.bgwlist) {
// 获取当前迭代中的后台工作者
BackgroundWorker *bgw = slist_container(BackgroundWorker, rw_lnode, iter.cur);
// 如果后台工作者状态为 BGW_NOT_YET_STARTED且超过了状态持续时间限制
if (bgw->bgw_status == BGW_NOT_YET_STARTED && ++bgw->bgw_status_dur > BGWORKER_STATUS_DURLIMIT) {
disable_count = pg_atomic_fetch_add_u32(&bgw->disable_count, 1);
disable_count = pg_atomic_fetch_add_u32(&bgw->disable_count, 1); // 原子操作地增加后台工作者的禁用计数
// 如果禁用计数为 0表示该后台工作者需要被禁用
if (disable_count == 0) {
// 输出警告信息,表示该后台工作者在 5 秒内未能启动,被禁用
ereport(WARNING, (errmsg("The bgworker thread %lu hasn't started in 5 seconds, disable it.",
bgw->bgw_notify_pid)));
(*nparticipants)--;
nunstarts++;
}
} else if (bgw->bgw_status == BGW_FAILED) {
(*nparticipants)--; // 减少参与任务的后台工作者数量
nunstarts++; // 增加未启动的后台工作者数量
}
} else if (bgw->bgw_status == BGW_FAILED) { // 如果后台工作者状态为 BGW_FAILED
// 检查后台工作者的错误级别是否大于或等于 ERROR
if (bgw->bgw_edata.elevel >= ERROR) {
// 输出错误信息,包括错误码、错误消息和错误详情
ereport(bgw->bgw_edata.elevel, (errcode(bgw->bgw_edata.sqlerrcode), errmsg("%s",
bgw->bgw_edata.message), errdetail("%s", bgw->bgw_edata.detail)));
} else {
// 输出错误信息,表示后台工作者在并行索引构建过程中失败了
ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("Background worker failed during parallel index building.")));
}
} else if (bgw->bgw_status == BGW_TERMINATED) {
nfinished++;
} else if (bgw->bgw_status == BGW_TERMINATED) { // 如果后台工作者状态为 BGW_TERMINATED
nfinished++; // 已完成任务的后台工作者数量+1
}
}
alldone = (nfinished >= *nparticipants);
alldone = (nfinished >= *nparticipants); // 判断是否所有后台工作者都已完成任务
// 如果所有后台工作者都已完成任务,进行清理未启动的后台工作者
if (alldone) {
CleanupUnstartBgworkers(nunstarts);
} else {
// 在等待期间检查是否有中断请求,然后进行短暂的等待
CHECK_FOR_INTERRUPTS();
usleep(BGWORKER_LOOP_SLEEP_TIME);
}
}
pgstat_report_waitstatus(oldStatus);
pgstat_report_waitstatus(oldStatus); // 恢复之前的等待状态报告
}
/*
* LaunchBackgroundWorkers
*
*
*
*
* nworkers:
* bgshared:
* bgmain:
* bgexit: 退
*
*
*
*/
int LaunchBackgroundWorkers(int nworkers, void *bgshared, bgworker_main bgmain, bgworker_exit bgexit)
{
int actualWorkers = 0;
MemoryContext oldcontext;
BgWorkerContext *bwc;
Assert(nworkers > 0);
Assert(nworkers > 0); // 确保要启动的后台工作者数量大于 0
/* We need to be a lock group leader. */
BecomeLockGroupLeader();
BecomeLockGroupLeader(); // 成为锁组的领导者
/* We might be running in a short-lived memory context. */
oldcontext = MemoryContextSwitchTo(u_sess->top_transaction_mem_cxt);
oldcontext = MemoryContextSwitchTo(u_sess->top_transaction_mem_cxt); // 保存旧的内存上下文
/*
* Start workers.
@ -533,30 +705,34 @@ int LaunchBackgroundWorkers(int nworkers, void *bgshared, bgworker_main bgmain,
* fails. It wouldn't help much anyway, because registering the worker in
* no way guarantees that it will start up and initialize successfully.
*/
// 开始创建工作者
// 分配一个后台工作者上下文内存,并将其初始化为零
bwc = (BgWorkerContext*)MemoryContextAllocZero(
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), sizeof(BgWorkerContext));
bwc->transactionCxt.txnId = GetCurrentTransactionIdIfAny();
bwc->transactionCxt.snapshot = GetActiveSnapshot();
bwc->bgshared = bgshared;
bwc->databaseName = get_database_name(u_sess->proc_cxt.MyDatabaseId);
bwc->userName = u_sess->proc_cxt.MyProcPort->user_name;
bwc->transactionCxt.txnId = GetCurrentTransactionIdIfAny(); // 设置事务上下文的事务ID
bwc->transactionCxt.snapshot = GetActiveSnapshot(); // 设置事务上下文的快照
bwc->bgshared = bgshared; // 将共享的数据结构指针存储在上下文中
bwc->databaseName = get_database_name(u_sess->proc_cxt.MyDatabaseId); // 获取当前数据库的名称并存储在上下文中
bwc->userName = u_sess->proc_cxt.MyProcPort->user_name; // 获取当前会话用户的用户名并存储在上下文中
/* pass enable_cluster_resize to bgwokers to optimize parallel index building performance during redistribution */
bwc->enable_cluster_resize = u_sess->attr.attr_sql.enable_cluster_resize;
bwc->leader = t_thrd.proc;
bwc->main_entry = bgmain;
bwc->exit_entry = bgexit;
bwc->enable_cluster_resize = u_sess->attr.attr_sql.enable_cluster_resize; // 将集群调整标志传递给后台工作者以优化并行索引构建性能
bwc->leader = t_thrd.proc; // 设置后台工作者的领导者为当前线程
bwc->main_entry = bgmain; // 设置后台工作者的主要入口点
bwc->exit_entry = bgexit; // 设置后台工作者的退出入口点
t_thrd.bgworker_cxt.bgwcontext = bwc;
t_thrd.bgworker_cxt.bgwcontext = bwc; // 将刚刚初始化的后台工作者上下文设置到全局上下文中
StreamSaveTxnContext(&bwc->transactionCxt);
StreamSaveTxnContext(&bwc->transactionCxt); // 将事务上下文保存到流复制上下文中,以便在后续流复制进程中使用
for (int i = 0; i < nworkers; ++i) {
for (int i = 0; i < nworkers; ++i) { // 遍历注册指定数量的后台工作者
// 调用 RegisterBackgroundWorker 函数注册后台工作者
// 如果注册成功,则增加 actualWorkers 计数
if (RegisterBackgroundWorker(bwc)) {
actualWorkers++;
}
}
/* Restore previous memory context. */
MemoryContextSwitchTo(oldcontext);
return actualWorkers;
MemoryContextSwitchTo(oldcontext); // 恢复之前的内存上下文
return actualWorkers; // 返回实际注册的后台工作者数量
}

View File

@ -88,33 +88,50 @@ const int MAX_THREAD_NAME_LEN = 128;
static void drop_rel_all_forks_buffers();
static void drop_rel_one_fork_buffers();
/*
* bgwriter
*
*
*
*
*
*/
static void setup_bgwriter_signalhook(void)
{
/*
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGHUP, bgwriter_sighup_handler); /* set flag to read config file */
(void)gspqsignal(SIGINT, SIG_IGN);
(void)gspqsignal(SIGTERM, bgwriter_request_shutdown_handler); /* shutdown */
(void)gspqsignal(SIGQUIT, bgwriter_quickdie); /* hard crash time */
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, bgwriter_sigusr1_handler);
(void)gspqsignal(SIGUSR2, SIG_IGN);
(void)gspqsignal(SIGHUP, bgwriter_sighup_handler); // 当收到SIGHUP信号时设置标志以重新读取配置文件
(void)gspqsignal(SIGINT, SIG_IGN); // 忽略SIGINT信号中断信号
(void)gspqsignal(SIGTERM, bgwriter_request_shutdown_handler); // 当收到SIGTERM信号时请求关闭进程
(void)gspqsignal(SIGQUIT, bgwriter_quickdie); // 当收到SIGQUIT信号时执行快速崩溃
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略SIGALRM信号定时器信号
(void)gspqsignal(SIGPIPE, SIG_IGN); // SIGPIPE信号的默认行为是终止进程将SIGPIPE信号的处理方式设置为忽略不会导致进程终止而是允许程序继续执行
(void)gspqsignal(SIGUSR1, bgwriter_sigusr1_handler); // 当收到SIGUSR1信号时调用相应的处理函数
(void)gspqsignal(SIGUSR2, SIG_IGN); // 忽略SIGUSR2信号
/*
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGCHLD, SIG_DFL);
(void)gspqsignal(SIGCHLD, SIG_DFL); // 设置SIGCHLD信号的默认处理方式
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
(void)gspqsignal(SIGCONT, SIG_DFL);
(void)gspqsignal(SIGWINCH, SIG_DFL);
/* We allow SIGQUIT (quickdie) at all times */
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 从信号屏蔽集中移除 SIGQUIT 信号
}
/*
*
*
* :
* - wb_context:
* - bgwriter_cxt:
*
* :
*/
static void bgwriter_handle_exceptions(WritebackContext wb_context, MemoryContext bgwriter_cxt)
{
/*
@ -187,90 +204,102 @@ static void bgwriter_handle_exceptions(WritebackContext wb_context, MemoryContex
* This is invoked from AuxiliaryProcessMain, which has already created the
* basic execution environment, but not enabled signals yet.
*/
/*
* (bgwriter)
* AuxiliaryProcessMain AuxiliaryProcessMain
*
*
* :
*
* :
*/
void BackgroundWriterMain(void)
{
sigjmp_buf local_sigjmp_buf;
MemoryContext bgwriter_context;
bool prev_hibernate = false;
WritebackContext wb_context;
sigjmp_buf local_sigjmp_buf; // 声明用于保存跳转位置的缓冲区
MemoryContext bgwriter_context; // 声明后台写入进程的内存上下文
bool prev_hibernate = false; // 声明前一个休眠状态,用于控制休眠
WritebackContext wb_context; // 写回上下文,用于控制后台写入进程的行为
t_thrd.role = BGWRITER;
t_thrd.role = BGWRITER; // 设置当前线程角色为后台写入进程
ereport(LOG, (errmsg("bgwriter started")));
ereport(LOG, (errmsg("bgwriter started"))); // 记录日志,标记 bgwriter 开始运行
setup_bgwriter_signalhook();
setup_bgwriter_signalhook(); // 设置信号处理函数
/*
* We just started, assume there has been either a shutdown or
* end-of-recovery snapshot.
*/
last_snapshot_ts = GetCurrentTimestamp();
last_snapshot_ts = GetCurrentTimestamp(); // 获取当前时间戳作为最后快照时间戳
/*
* Create a resource owner to keep track of our resources (currently only
* buffer pins).
*/
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Background Writer",
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE)); // 创建资源所有者并设置名称
/*
* Create a memory context that we will do all our work in. We do this so
* that we can reset the context during error recovery and thereby avoid
* possible memory leaks. Formerly this code just ran in
* t_thrd.top_mem_cxt, but resetting that would be a really bad idea.
*/
// 创建内存上下文,在其中执行所有工作。便于在错误恢复期间能够重置上下文,避免可能的内存泄漏
bgwriter_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
"Background Writer",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
MemoryContextSwitchTo(bgwriter_context);
ALLOCSET_DEFAULT_MAXSIZE); // 创建后台写入进程的内存上下文
MemoryContextSwitchTo(bgwriter_context); // 切换到新创建的内存上下文
WritebackContextInit(&wb_context, &u_sess->attr.attr_storage.bgwriter_flush_after);
WritebackContextInit(&wb_context, &u_sess->attr.attr_storage.bgwriter_flush_after); // 初始化写回上下文
/*
* If an exception is encountered, processing resumes here.
*
* See notes in postgres.c about the design of this coding.
*/
int curTryCounter;
int* oldTryCounter = NULL;
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
bgwriter_handle_exceptions(wb_context, bgwriter_context);
// 遇到异常,从此开始执行
int curTryCounter; // 用于保存当前的错误尝试计数器
int* oldTryCounter = NULL; // 用于保存旧的错误尝试计数器指针
if (sigsetjmp(local_sigjmp_buf, 1) != 0) { // 设置跳转点以处理异常
gstrace_tryblock_exit(true, oldTryCounter); // 退出错误尝试块
bgwriter_handle_exceptions(wb_context, bgwriter_context); // 处理异常
/* Report wait end here, when there is no further possibility of wait */
pgstat_report_waitevent(WAIT_EVENT_END);
pgstat_report_waitevent(WAIT_EVENT_END); // 报告等待事件结束
}
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
oldTryCounter = gstrace_tryblock_entry(&curTryCounter); // 进入错误尝试块,并获取旧的错误尝试计数器
/* We can now handle ereport(ERROR) */
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; // 设置异常堆栈
/*
* Unblock signals (they were blocked when the postmaster forked us)
*/
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除信号掩码
(void)gs_signal_unblock_sigusr2(); // 解除 SIGUSR2 信号的阻塞
/*
* Use the recovery target timeline ID during recovery
*/
if (RecoveryInProgress())
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI();
if (RecoveryInProgress()) // 如果正在进行恢复操作
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI(); // 获取恢复目标时间线 ID
/*
* Reset hibernation state after any error.
*/
prev_hibernate = false;
prev_hibernate = false; // 设置初始化休眠状态
pgstat_report_appname("Background writer");
pgstat_report_activity(STATE_IDLE, NULL);
pgstat_report_appname("Background writer"); // 报告应用程序名称到统计信息
pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态为空闲
/*
* Loop forever
*/
for (;;) {
bool can_hibernate = false;
bool can_hibernate = false; // 是否可以休眠的标志
int rc;
/*
@ -279,25 +308,25 @@ void BackgroundWriterMain(void)
*/
if (pg_atomic_read_u32(&g_instance.dw_batch_cxt.dw_version) < DW_SUPPORT_REABLE_DOUBLE_WRITE
&& t_thrd.proc->workingVersionNum >= DW_SUPPORT_REABLE_DOUBLE_WRITE) {
dw_upgrade_renable_double_write();
dw_upgrade_renable_double_write(); // 执行双写升级操作
}
/* Clear any already-pending wakeups */
ResetLatch(&t_thrd.proc->procLatch);
ResetLatch(&t_thrd.proc->procLatch); // 重置进程的 latch
pgstat_report_activity(STATE_RUNNING, NULL);
pgstat_report_activity(STATE_RUNNING, NULL); // 报告活动状态为运行中
if (t_thrd.bgwriter_cxt.got_SIGHUP) {
t_thrd.bgwriter_cxt.got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
if (t_thrd.bgwriter_cxt.got_SIGHUP) { // 如果收到了 SIGHUP 信号
t_thrd.bgwriter_cxt.got_SIGHUP = false; // 清除 SIGHUP 信号标志
ProcessConfigFile(PGC_SIGHUP); // 处理配置文件变更
}
if (t_thrd.bgwriter_cxt.shutdown_requested) {
/*
if (t_thrd.bgwriter_cxt.shutdown_requested) { // 如果请求了关闭
/*
* From here on, elog(ERROR) should end with exit(1), not send
* control back to the sigsetjmp block above
*/
u_sess->attr.attr_common.ExitOnAnyError = true;
u_sess->attr.attr_common.ExitOnAnyError = true; // 设置在任何错误时退出
/* Normal exit from the bgwriter is here */
proc_exit(0); /* done */
}
@ -310,16 +339,24 @@ void BackgroundWriterMain(void)
/*
* Send off activity statistics to the stats collector
*/
pgstat_send_bgwriter();
pgstat_send_bgwriter(); // 发送后台写入进程的统计信息
if (FirstCallSinceLastCheckpoint()) {
if (FirstCallSinceLastCheckpoint()) { // 如果自上次检查点以来是首次调用
/*
* After any checkpoint, close all smgr files. This is so we
* won't hang onto smgr references to deleted files indefinitely.
*/
smgrcloseall();
smgrcloseall(); // 关闭所有 smgr 文件
}
// 描述在后台写入进程bgwriter中定期记录 xl_running_xacts 的目的和原因
/*
*1.: xl_running_xacts
*2.: KnownXids*
*3.: xl_running_xacts
*4.: 4
*5.:
xl_running_xacts
* Log a new xl_running_xacts every now and then so replication can get
* into a consistent state faster (think of suboverflowed snapshots)
* and clean up resources (locks, KnownXids*) more frequently. The
@ -340,21 +377,21 @@ void BackgroundWriterMain(void)
* time. E.g. Checkpointer, when active, is barely ever in its
* mainloop and thus makes it hard to log regularly.
*/
if (XLogStandbyInfoActive() && !RecoveryInProgress()) {
if (XLogStandbyInfoActive() && !RecoveryInProgress()) { // 如果正在运行热备,且不在恢复过程中
TimestampTz timeout = 0;
TimestampTz now = GetCurrentTimestamp();
timeout = TimestampTzPlusMilliseconds(last_snapshot_ts, LOG_SNAPSHOT_INTERVAL_MS);
TimestampTz now = GetCurrentTimestamp(); // 获取当前时间戳
timeout = TimestampTzPlusMilliseconds(last_snapshot_ts, LOG_SNAPSHOT_INTERVAL_MS); // 计算下次记录时间
/*
* only log if enough time has passed and some xlog record has been
* inserted.
*/
if (now >= timeout && !XLByteEQ(last_snapshot_lsn, GetXLogInsertRecPtr())) {
last_snapshot_lsn = LogStandbySnapshot();
last_snapshot_ts = now;
if (now >= timeout && !XLByteEQ(last_snapshot_lsn, GetXLogInsertRecPtr())) { // 检查是否应记录
last_snapshot_lsn = LogStandbySnapshot(); // 记录热备快照
last_snapshot_ts = now; // 更新最后快照时间
}
if (now >= timeout) {
LogCheckSlot();
LogCheckSlot(); // 记录检查槽信息
}
}
@ -368,12 +405,16 @@ void BackgroundWriterMain(void)
* down with latch events that are likely to happen frequently during
* normal operation.
*/
pgstat_report_activity(STATE_IDLE, NULL);
pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态为空闲
rc = WaitLatch(&t_thrd.proc->procLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
u_sess->attr.attr_storage.BgWriterDelay /* ms */);
u_sess->attr.attr_storage.BgWriterDelay /* ms */); // 等待信号或超时
/*
*1. BgBufferSync "休眠"
bgwriter_delay
*2. BgBufferSync
* If no latch event and BgBufferSync says nothing's happening, extend
* the sleep in "hibernation" mode, where we sleep for much longer
* than bgwriter_delay says. Fewer wakeups save electricity. When a
@ -406,10 +447,10 @@ void BackgroundWriterMain(void)
* Emergency bailout if postmaster has died. This is to avoid the
* necessity for manual cleanup of all postmaster children.
*/
if (rc & WL_POSTMASTER_DEATH)
gs_thread_exit(1);
if (rc & WL_POSTMASTER_DEATH) // 如果 postmaster 已经死亡
gs_thread_exit(1); // 紧急退出
prev_hibernate = can_hibernate;
prev_hibernate = can_hibernate; // 更新前一个休眠状态
}
}
@ -423,9 +464,23 @@ void BackgroundWriterMain(void)
* Some backend has bought the farm,
* so we need to stop what we're doing and exit.
*/
/*
*
* postmaster SIGQUIT
* 退退
* proc_exit()
* postmaster 使 exit(2) 2退
*
*
* SIGNAL_ARGS:
*
*
*
*/
static void bgwriter_quickdie(SIGNAL_ARGS)
{
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); // 解除信号屏蔽
/*
* We DO NOT want to run proc_exit() callbacks -- we're here because
@ -435,7 +490,7 @@ static void bgwriter_quickdie(SIGNAL_ARGS)
* things by calling exit() directly, we have to reset the callbacks
* explicitly to make this work as intended.
*/
on_exit_reset();
on_exit_reset(); // 重置 exit() 回调函数
/*
* Note we do exit(2) not exit(0). This is to force the postmaster into a
@ -445,88 +500,139 @@ static void bgwriter_quickdie(SIGNAL_ARGS)
* should ensure the postmaster sees this as a crash, too, but no harm in
* being doubly sure.)
*/
exit(2);
exit(2); // 使用 exit(2) 退出进程,强制 postmaster 进入系统复位周期
}
/* SIGHUP: set flag to re-read config file at next convenient time */
// 功能:当收到 SIGHUP 信号时,设置标志以在下一个合适的时机重新读取配置文件
static void bgwriter_sighup_handler(SIGNAL_ARGS)
{
int save_errno = errno;
int save_errno = errno; // 保存当前的 errno 值
t_thrd.bgwriter_cxt.got_SIGHUP = true;
t_thrd.bgwriter_cxt.got_SIGHUP = true; // 设置收到 SIGHUP 信号的标志
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch); // 设置进程的标志事件,以便重新读取配置文件
errno = save_errno;
errno = save_errno; // 恢复之前保存的 errno 值
}
/* SIGTERM: set flag to shutdown and exit */
// 功能:当收到 SIGTERM 信号时,设置标志以请求关闭并退出
static void bgwriter_request_shutdown_handler(SIGNAL_ARGS)
{
int save_errno = errno;
int save_errno = errno; // 保存当前的 errno 值
t_thrd.bgwriter_cxt.shutdown_requested = true;
t_thrd.bgwriter_cxt.shutdown_requested = true; // 设置请求关闭的标志
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch);// 设置进程的标志事件,以便请求关闭并退出
errno = save_errno;
errno = save_errno; // 恢复之前保存的 errno 值
}
/* SIGUSR1: used for latch wakeups */
// 功能:用于标志事件唤醒
static void bgwriter_sigusr1_handler(SIGNAL_ARGS)
{
int save_errno = errno;
int save_errno = errno; // 保存当前的 errno 值
latch_sigusr1_handler();
latch_sigusr1_handler(); // 处理 SIGUSR1 信号,通常用于唤醒标志事件
errno = save_errno;
errno = save_errno; // 恢复之前保存的 errno 值
}
// 检查当前进程是否为后台写入进程
// 参数: 无
// 返回值bool类型 [如果当前进程的角色是后台写入进程,则返回 true]
bool IsBgwriterProcess(void)
{
return (t_thrd.role == BGWRITER);
return (t_thrd.role == BGWRITER); // 如果当前进程的角色是后台写入进程,则返回 true
}
/* bgwriter view function */
/*
*
*
*
*
* :
* "not define"
*
*/
Datum bgwriter_view_get_node_name()
{
if (g_instance.attr.attr_common.PGXCNodeName == NULL || g_instance.attr.attr_common.PGXCNodeName[0] == '\0') {
return CStringGetTextDatum("not define");
return CStringGetTextDatum("not define");// 如果节点名称未定义,则返回 "not define" 的文本数据
} else {
return CStringGetTextDatum(g_instance.attr.attr_common.PGXCNodeName);
return CStringGetTextDatum(g_instance.attr.attr_common.PGXCNodeName); // 否则返回节点名称的文本数据
}
}
/*
*
*
*
*
* : 0
*/
Datum bgwriter_view_get_actual_flush_num()
{
return Int64GetDatum(0);
return Int64GetDatum(0); // 返回整数 0表示实际刷新次数
}
/*
*
*
*
*
* : 0
*/
Datum bgwriter_view_get_last_flush_num()
{
return Int32GetDatum(0);
return Int32GetDatum(0); // 返回整数 0表示上次刷新次数
}
/*
*
*
*
*
* :
*/
Datum bgwriter_view_get_candidate_nums()
{
int candidate_num = get_curr_candidate_nums(true) + get_curr_candidate_nums(false);
return Int32GetDatum(candidate_num);
return Int32GetDatum(candidate_num); // 返回候选缓冲区数量
}
/*
*
*
*
*
* :
*/
Datum bgwriter_view_get_num_candidate_list()
{
return Int64GetDatum(g_instance.ckpt_cxt_ctl->get_buf_num_candidate_list);
return Int64GetDatum(g_instance.ckpt_cxt_ctl->get_buf_num_candidate_list); // 返回缓冲区候选列表数量
}
/*
*
*
*
*
* :
*/
Datum bgwriter_view_get_num_clock_sweep()
{
return Int64GetDatum(g_instance.ckpt_cxt_ctl->get_buf_num_clock_sweep);
return Int64GetDatum(g_instance.ckpt_cxt_ctl->get_buf_num_clock_sweep); // 返回缓冲区时钟扫描次数
}
// 定义了后台写入进程视图的列信息
const incre_ckpt_view_col g_bgwriter_view_col[INCRE_CKPT_BGWRITER_VIEW_COL_NUM] = {
// 列名 数据类型 获取数据需要调用的函数
{"node_name", TEXTOID, bgwriter_view_get_node_name},
{"bgwr_actual_flush_total_num", INT8OID, bgwriter_view_get_actual_flush_num},
{"bgwr_last_flush_num", INT4OID, bgwriter_view_get_last_flush_num},
@ -535,17 +641,24 @@ const incre_ckpt_view_col g_bgwriter_view_col[INCRE_CKPT_BGWRITER_VIEW_COL_NUM]
{"get_buf_clock_sweep", INT8OID, bgwriter_view_get_num_clock_sweep}};
const uint THREAD_SLEEP_TIME = 10 * 60 * 1000;
const uint THREAD_SLEEP_TIME = 10 * 60 * 1000; // 后台写入进程睡眠时间(以毫秒为单位)
/*
*
*
*
*
* :
*/
void invalid_buffer_bgwriter_main()
{
sigjmp_buf localSigjmpBuf;
MemoryContext bgwriter_context;
char name[MAX_THREAD_NAME_LEN] = {0};
WritebackContext wb_context;
t_thrd.role = SPBGWRITER;
sigjmp_buf localSigjmpBuf; // 用于处理异常的跳转标记
MemoryContext bgwriter_context; // 内存上下文,用于执行工作并处理错误恢复
char name[MAX_THREAD_NAME_LEN] = {0}; // 线程名称
WritebackContext wb_context; // 写入上下文,用于配置写入行为
t_thrd.role = SPBGWRITER; // 设置线程角色为 SPBGWRITER
setup_bgwriter_signalhook();
ereport(LOG, (errmsg("invalidate buffer bgwriter started")));
setup_bgwriter_signalhook(); // 设置信号处理函数
ereport(LOG, (errmsg("invalidate buffer bgwriter started"))); // 记录日志,表示后台无效缓冲区写入进程已启动
errno_t err_rc = snprintf_s(name, MAX_THREAD_NAME_LEN, MAX_THREAD_NAME_LEN - 1, "%s", "spbgwriter");
securec_check_ss(err_rc, "", "");
@ -564,10 +677,10 @@ void invalid_buffer_bgwriter_main()
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
MemoryContextSwitchTo(bgwriter_context);
WritebackContextInit(&wb_context, &u_sess->attr.attr_storage.bgwriter_flush_after);
WritebackContextInit(&wb_context, &u_sess->attr.attr_storage.bgwriter_flush_after); // 初始化写入上下文
if (sigsetjmp(localSigjmpBuf, 1) != 0) {
ereport(WARNING, (errmsg("invalidate buffer bgwriter exception occured.")));
ereport(WARNING, (errmsg("invalidate buffer bgwriter exception occured."))); // 处理异常情况
bgwriter_handle_exceptions(wb_context, bgwriter_context);
}
@ -575,7 +688,7 @@ void invalid_buffer_bgwriter_main()
t_thrd.log_cxt.PG_exception_stack = &localSigjmpBuf;
/* Unblock signals (they were blocked when the postmaster forked us) */
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
/* Use the recovery target timeline ID during recovery */
@ -583,187 +696,230 @@ void invalid_buffer_bgwriter_main()
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI();
}
pgstat_report_appname("InvalidBufferBgWriter");
pgstat_report_activity(STATE_IDLE, NULL);
g_instance.bgwriter_cxt.invalid_buf_proc_latch = &t_thrd.proc->procLatch;
pgstat_report_appname("InvalidBufferBgWriter"); // 设置应用程序名称以进行统计
pgstat_report_activity(STATE_IDLE, NULL); // 报告进程状态为空闲
g_instance.bgwriter_cxt.invalid_buf_proc_latch = &t_thrd.proc->procLatch; // 设置 Latch 用于等待事件触发
/* Loop forever */
for (;;) {
int rc;
if (t_thrd.bgwriter_cxt.got_SIGHUP) {
t_thrd.bgwriter_cxt.got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
ProcessConfigFile(PGC_SIGHUP); // 处理 SIGHUP 信号,重新加载配置文件
}
if (t_thrd.bgwriter_cxt.shutdown_requested) {
ereport(LOG, (errmsg("invalidate buffer bgwriter thread shut down")));
u_sess->attr.attr_common.ExitOnAnyError = true;
proc_exit(0);
ereport(LOG, (errmsg("invalidate buffer bgwriter thread shut down"))); // 记录日志,表示线程即将关闭
u_sess->attr.attr_common.ExitOnAnyError = true;
proc_exit(0); // 退出线程
}
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, THREAD_SLEEP_TIME);
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, THREAD_SLEEP_TIME); // 等待事件触发
if (rc & WL_POSTMASTER_DEATH) {
gs_thread_exit(1);
gs_thread_exit(1); // 如果 Postmaster 已经关闭,线程退出
}
/* Clear any already-pending wakeups */
ResetLatch(&t_thrd.proc->procLatch);
drop_rel_all_forks_buffers();
drop_rel_one_fork_buffers();
ResetLatch(&t_thrd.proc->procLatch); // 清除已经触发的事件
drop_rel_all_forks_buffers(); // 执行释放所有关系的缓冲区的操作
drop_rel_one_fork_buffers(); // 执行释放一个关系的缓冲区的操作
}
}
const int HASH_TABLE_ELEMENT_MIN_NUM = 512;
/*
*
*
*
* name -
* use_heap_mem - 使
*
*
*
*/
HTAB *relfilenode_hashtbl_create(const char *name, bool use_heap_mem)
{
HASHCTL hashCtrl;
HTAB *hashtbl = NULL;
errno_t rc;
HASHCTL hashCtrl; // 哈希表控制信息结构体
HTAB *hashtbl = NULL; // 哈希表指针,初始化为空
errno_t rc; // 用于错误处理的返回码
rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl));
securec_check(rc, "", "");
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext;
hashCtrl.hash = tag_hash;
hashCtrl.keysize = sizeof(RelFileNode);
rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl)); // 初始化 hashCtrl 结构体为 0
securec_check(rc, "", ""); // 检查 memset_s 的返回值
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext; // 设置哈希表的内存上下文
hashCtrl.hash = tag_hash; // 设置哈希函数
hashCtrl.keysize = sizeof(RelFileNode); // 设置键的大小
/* keep entrysize >= keysize, stupid limits */
hashCtrl.entrysize = sizeof(DelFileTag);
hashCtrl.entrysize = sizeof(DelFileTag); // 设置哈希表中每个条目的大小
if (use_heap_mem) {
if (use_heap_mem) { // 根据 use_heap_mem 参数判断是否使用堆内存
hashtbl = HeapMemInitHash(name, HASH_TABLE_ELEMENT_MIN_NUM,
Max(g_instance.attr.attr_common.max_files_per_process, t_thrd.storage_cxt.max_userdatafiles), &hashCtrl,
(HASH_FUNCTION | HASH_ELEM));
(HASH_FUNCTION | HASH_ELEM)); // 在堆内存上创建哈希表
if (hashtbl == NULL) {
ereport(FATAL, (errmsg("could not initialize unlinik relation hash table")));
ereport(FATAL, (errmsg("could not initialize unlinik relation hash table"))); // 如果创建失败,报致命错误
}
} else {
// 在当前内存上下文中创建哈希表
hashtbl = hash_create(name, HASH_TABLE_ELEMENT_MIN_NUM, &hashCtrl, (HASH_CONTEXT | HASH_FUNCTION | HASH_ELEM));
}
return hashtbl;
return hashtbl; // 返回创建的哈希表指针
}
/*
*
*
*
* name -
* use_heap_mem - 使
*
*
*
*/
HTAB *relfilenode_fork_hashtbl_create(const char* name, bool use_heap_mem)
{
HASHCTL hashCtrl;
HTAB *hashtbl = NULL;
errno_t rc;
HASHCTL hashCtrl; // 哈希表控制信息结构体
HTAB *hashtbl = NULL; // 哈希表指针,初始化为空
errno_t rc; // 用于错误处理的返回码
rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl));
securec_check(rc, "", "");
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext;
hashCtrl.hash = tag_hash;
hashCtrl.keysize = sizeof(ForkRelFileNode);
rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl)); // 初始化 hashCtrl 结构体为 0
securec_check(rc, "", ""); // 检查 memset_s 的返回值
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext; // 设置哈希表的内存上下文
hashCtrl.hash = tag_hash; // 设置哈希函数
hashCtrl.keysize = sizeof(ForkRelFileNode); // 设置键的大小
/* keep entrysize >= keysize, stupid limits */
hashCtrl.entrysize = sizeof(DelForkFileTag);
hashCtrl.entrysize = sizeof(DelForkFileTag); // 设置哈希表中每个条目的大小
if (use_heap_mem) {
if (use_heap_mem) { // 根据 use_heap_mem 参数判断是否使用堆内存
hashtbl = HeapMemInitHash(name, HASH_TABLE_ELEMENT_MIN_NUM,
Max(g_instance.attr.attr_common.max_files_per_process, t_thrd.storage_cxt.max_userdatafiles),
&hashCtrl, (HASH_FUNCTION | HASH_ELEM));
&hashCtrl, (HASH_FUNCTION | HASH_ELEM)); // 在堆内存上创建哈希表
if (hashtbl == NULL) {
ereport(FATAL, (errmsg("could not initialize unlinik relation hash table")));
}
} else {
hashtbl = hash_create(name, HASH_TABLE_ELEMENT_MIN_NUM, &hashCtrl, (HASH_CONTEXT | HASH_FUNCTION | HASH_ELEM));
}
return hashtbl;
return hashtbl; // 返回创建的哈希表指针
}
/*
*
*
*
*
*
*
*/
static void drop_rel_all_forks_buffers()
{
HASH_SEQ_STATUS status;
DelFileTag *entry = NULL;
DelFileTag *temp_entry = NULL;
bool found = false;
uint rel_num = 0;
HTAB *unlink_rel_hashtbl = g_instance.bgwriter_cxt.unlink_rel_hashtbl;
HTAB *rel_bak = relfilenode_hashtbl_create("unlink_rel_bak", false);
HASH_SEQ_STATUS status; // 哈希表遍历状态
DelFileTag *entry = NULL; // 哈希表中的条目指针
DelFileTag *temp_entry = NULL; // 临时条目指针
bool found = false; // 是否找到标志
uint rel_num = 0; // 关系数量
HTAB *unlink_rel_hashtbl = g_instance.bgwriter_cxt.unlink_rel_hashtbl; // 获取哈希表指针
HTAB *rel_bak = relfilenode_hashtbl_create("unlink_rel_bak", false); // 创建临时哈希表
/* Obtains the entry in hashtable. */
LWLockAcquire(g_instance.bgwriter_cxt.rel_hashtbl_lock, LW_SHARED);
hash_seq_init(&status, unlink_rel_hashtbl);
LWLockAcquire(g_instance.bgwriter_cxt.rel_hashtbl_lock, LW_SHARED); // 获取共享锁
hash_seq_init(&status, unlink_rel_hashtbl); // 初始化哈希表遍历状态
while ((temp_entry = (DelFileTag *)hash_seq_search(&status)) != NULL) {
entry = (DelFileTag*)hash_search(rel_bak, (void *)&temp_entry->rnode, HASH_ENTER, &found);
entry = (DelFileTag*)hash_search(rel_bak, (void *)&temp_entry->rnode, HASH_ENTER, &found); // 将哈希表中的数据复制到临时哈希表
if (!found) {
entry->rnode = temp_entry->rnode;
entry->maxSegNo = temp_entry->maxSegNo;
rel_num++;
}
}
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock);
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock); // 释放共享锁
if (rel_num > 0) {
DropRelFileNodeAllBuffersUsingHash(rel_bak);
DropRelFileNodeAllBuffersUsingHash(rel_bak); // 释放哈希表中所有关系的缓冲区
hash_seq_init(&status, rel_bak);
while ((temp_entry = (DelFileTag *)hash_seq_search(&status)) != NULL) {
if (temp_entry->maxSegNo == -1) {
hash_seq_init(&status, rel_bak); // 初始化哈希表遍历状态
while ((temp_entry = (DelFileTag *)hash_seq_search(&status)) != NULL) { // 循环遍历哈希表中的每个条目
if (temp_entry->maxSegNo == -1) { // 如果maxSegNo为-1表示不处理该关系
ereport(DEBUG1, (errmodule(MOD_INCRE_BG),
errmsg("the max segno is -1, skip forget this rel %u/%u/%u, bucketNode is %d",
temp_entry->rnode.spcNode, temp_entry->rnode.dbNode, temp_entry->rnode.relNode,
temp_entry->rnode.bucketNode)));
continue;
continue; // 跳过不处理的关系
}
for (int32 i = 0; i < temp_entry->maxSegNo; i++) {
for (int fork_num = 0; fork_num <= (int)MAX_FORKNUM; fork_num++) {
md_register_forget_request(temp_entry->rnode, fork_num, i);
for (int32 i = 0; i < temp_entry->maxSegNo; i++) { // 循环处理关系的每个段
for (int fork_num = 0; fork_num <= (int)MAX_FORKNUM; fork_num++) { // 循环处理关系的每个分支
md_register_forget_request(temp_entry->rnode, fork_num, i); // 注册忘记请求
}
}
LWLockAcquire(g_instance.bgwriter_cxt.rel_hashtbl_lock, LW_EXCLUSIVE);
LWLockAcquire(g_instance.bgwriter_cxt.rel_hashtbl_lock, LW_EXCLUSIVE); // 获取锁以操作关系哈希表
// 如果在哈希表中没有找到关系,报告数据损坏错误
if (hash_search(unlink_rel_hashtbl, (void *)&temp_entry->rnode, HASH_REMOVE, NULL) == NULL) {
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock);
hash_destroy(rel_bak);
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("unlink rel hash table corrupted")));
} else {
// 关系处理完成,记录日志
ereport(DEBUG1, (errmodule(MOD_INCRE_BG),
errmsg("invalidate buffer has been finished for rel %u/%u/%u, bucketNode is %d",
temp_entry->rnode.spcNode, temp_entry->rnode.dbNode, temp_entry->rnode.relNode,
temp_entry->rnode.bucketNode)));
}
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock);
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock); // 释放关系哈希表锁
}
}
hash_destroy(rel_bak);
hash_destroy(rel_bak); // 销毁临时哈希表
}
/*
*
*
*
*
*
*
*/
static void drop_rel_one_fork_buffers()
{
HASH_SEQ_STATUS status;
DelForkFileTag *entry = NULL;
DelForkFileTag *temp_entry = NULL;
bool found = false;
uint rel_num = 0;
HTAB *unlink_rel_fork_hashtbl = g_instance.bgwriter_cxt.unlink_rel_fork_hashtbl;
HTAB *rel_bak = relfilenode_fork_hashtbl_create("unlink_rel_one_fork_bak", false);
HASH_SEQ_STATUS status; // 哈希表遍历状态
DelForkFileTag *entry = NULL; // 哈希表中的条目指针
DelForkFileTag *temp_entry = NULL; // 临时条目指针
bool found = false; // 是否找到标志
uint rel_num = 0; // 关系数量
HTAB *unlink_rel_fork_hashtbl = g_instance.bgwriter_cxt.unlink_rel_fork_hashtbl; // 获取哈希表指针
HTAB *rel_bak = relfilenode_fork_hashtbl_create("unlink_rel_one_fork_bak", false); // 创建临时哈希表
/* Obtains the entry in hashtable. */
LWLockAcquire(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock, LW_SHARED);
hash_seq_init(&status, unlink_rel_fork_hashtbl);
while ((temp_entry = (DelForkFileTag *)hash_seq_search(&status)) != NULL) {
entry = (DelForkFileTag*)hash_search(rel_bak, temp_entry, HASH_ENTER, &found);
LWLockAcquire(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock, LW_SHARED); // 获取哈希表锁(共享模式)
hash_seq_init(&status, unlink_rel_fork_hashtbl); // 初始化哈希表遍历状态
while ((temp_entry = (DelForkFileTag *)hash_seq_search(&status)) != NULL) { // 循环遍历哈希表中的每个条目
entry = (DelForkFileTag*)hash_search(rel_bak, temp_entry, HASH_ENTER, &found); // 在临时哈希表中查找或插入条目
if (!found) {
// 如果没有找到,初始化条目数据
entry->forkrnode.rnode.spcNode = temp_entry->forkrnode.rnode.spcNode;
entry->forkrnode.rnode.dbNode = temp_entry->forkrnode.rnode.dbNode;
entry->forkrnode.rnode.relNode = temp_entry->forkrnode.rnode.relNode;
entry->forkrnode.rnode.bucketNode = temp_entry->forkrnode.rnode.bucketNode;
entry->forkrnode.forkNum = temp_entry->forkrnode.forkNum;
entry->maxSegNo = temp_entry->maxSegNo;
rel_num++;
rel_num++; // 增加关系数量
}
}
LWLockRelease(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock);
LWLockRelease(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock); // 释放哈希表锁
if (rel_num > 0) {
DropRelFileNodeOneForkAllBuffersUsingHash(rel_bak);
hash_seq_init(&status, rel_bak);
if (rel_num > 0) { // 如果有要处理的关系
DropRelFileNodeOneForkAllBuffersUsingHash(rel_bak); // 释放临时哈希表中所有关系的缓冲区
hash_seq_init(&status, rel_bak); // 重新初始化哈希表遍历状态
while ((temp_entry = (DelForkFileTag *)hash_seq_search(&status)) != NULL) {
// 再次遍历临时哈希表中的每个条目
if (temp_entry->maxSegNo == -1) {
// 如果maxSegNo为-1表示不处理该关系
ereport(DEBUG1, (errmodule(MOD_INCRE_BG),
errmsg("the max segno is -1, skip forget this rel %u/%u/%u, bucketNode is %d",
temp_entry->forkrnode.rnode.spcNode, temp_entry->forkrnode.rnode.dbNode,
temp_entry->forkrnode.rnode.relNode, temp_entry->forkrnode.rnode.bucketNode)));
continue;
continue; // 跳过不处理的关系
}
for (int32 i = 0; i < temp_entry->maxSegNo; i++) {
for (int32 i = 0; i < temp_entry->maxSegNo; i++) { // 循环处理关系的每个段
md_register_forget_request(temp_entry->forkrnode.rnode, temp_entry->forkrnode.forkNum, i);
}
LWLockAcquire(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock, LW_EXCLUSIVE);

View File

@ -56,12 +56,23 @@ static void CBMwriter_sigusr1_handler(SIGNAL_ARGS);
* This is invoked from AuxiliaryProcessMain, which has already created the
* basic execution environment, but not enabled signals yet.
*/
/*
*
* cbmwriter CBM
*
*
*
*
*
*
*/
void CBMWriterMain(void)
{
sigjmp_buf local_sigjmp_buf;
ResourceOwner cbmwriter_resourceOwner;
sigjmp_buf local_sigjmp_buf; // 用于错误处理的跳转点
ResourceOwner cbmwriter_resourceOwner; // 用于跟踪 CBM Writer 进程的资源
ereport(LOG, (errmsg("cbm writer started")));
// 根据配置决定检查点超时时间
u_sess->attr.attr_storage.CheckPointTimeout = ENABLE_INCRE_CKPT
? u_sess->attr.attr_storage.incrCheckPointTimeout
: u_sess->attr.attr_storage.fullCheckPointTimeout;
@ -75,31 +86,33 @@ void CBMWriterMain(void)
/*
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGHUP, CBMSigHupHandler); /* set flag to read config file */
(void)gspqsignal(SIGINT, CBMShutdownHandler); /* request shutdown */
(void)gspqsignal(SIGTERM, CBMShutdownHandler); /* request shutdown */
(void)gspqsignal(SIGQUIT, CBM_quickdie); /* hard crash time */
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, CBMwriter_sigusr1_handler);
(void)gspqsignal(SIGUSR2, SIG_IGN); /* not used */
(void)gspqsignal(SIGHUP, CBMSigHupHandler); /* set flag to read config file */ // 收到 SIGHUP 信号时重新读取配置文件
(void)gspqsignal(SIGINT, CBMShutdownHandler); /* request shutdown */ // 收到 SIGINT 信号时请求正常关闭
(void)gspqsignal(SIGTERM, CBMShutdownHandler); /* request shutdown */ // 收到 SIGTERM 信号时请求正常关闭
(void)gspqsignal(SIGQUIT, CBM_quickdie); /* hard crash time */ // 收到 SIGQUIT 信号时执行硬崩溃操作
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略 SIGALRM 信号
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略 SIGPIPE 信号
(void)gspqsignal(SIGUSR1, CBMwriter_sigusr1_handler); // 执行自定义操作以响应 SIGUSR1 信号
(void)gspqsignal(SIGUSR2, SIG_IGN); /* not used */ // 忽略 SIGUSR2 信号
/*
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGCHLD, SIG_DFL);
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
(void)gspqsignal(SIGCONT, SIG_DFL);
(void)gspqsignal(SIGWINCH, SIG_DFL);
// 恢复各信号处理程序为系统默认行为
(void)gspqsignal(SIGCHLD, SIG_DFL); // SIGCHLD 信号在子进程终止或停止时发出
(void)gspqsignal(SIGTTIN, SIG_DFL); // SIGTTIN 信号在后台进程尝试从终端读取时发出
(void)gspqsignal(SIGTTOU, SIG_DFL); // SIGTTOU 信号在后台进程尝试向终端写入时发出
(void)gspqsignal(SIGCONT, SIG_DFL); // SIGCONT 信号用于继续已停止的进程
(void)gspqsignal(SIGWINCH, SIG_DFL); // SIGWINCH 信号在终端窗口大小变化时发出
/* We allow SIGQUIT (quickdie) at all times */
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许 SIGQUIT信号在任何时候都可以触发
/*
* Create a resource owner to keep track of our resources (not clear that
* we need this, but may as well have one).
*/
// 创建 ResourceOwner 用于跟踪管理资源
cbmwriter_resourceOwner = ResourceOwnerCreate(NULL, "CBM Writer",
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
t_thrd.utils_cxt.CurrentResourceOwner = cbmwriter_resourceOwner;
@ -109,6 +122,7 @@ void CBMWriterMain(void)
* that we can reset the context during error recovery and thereby avoid
* possible memory leaks.
*/
// 创建内存上下文,以便在错误恢复期间重置上下文,避免内存泄漏
t_thrd.cbm_cxt.cbmwriter_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
"CBM Writer",
ALLOCSET_DEFAULT_MINSIZE,
@ -124,24 +138,26 @@ void CBMWriterMain(void)
/*
* If an exception is encountered, processing resumes here.
*/
// 发生异常跳转到此处
int curTryCounter;
int* oldTryCounter = NULL;
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
/* Since not using PG_TRY, must reset error stack by hand */
// 手动重置错误栈
t_thrd.log_cxt.error_context_stack = NULL;
t_thrd.log_cxt.call_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
HOLD_INTERRUPTS(); // 防止在清理期间发生中断
/* Report the error to the server log */
EmitErrorReport();
EmitErrorReport(); // 报告错误到服务器日志
/* release resource held by lsc */
AtEOXact_SysDBCache(false);
AtEOXact_SysDBCache(false); // 释放由 lsc 持有的资源
/*
* These operations are really just a minimal subset of
* AbortTransaction(). We don't have very many resources to worry
@ -200,7 +216,7 @@ void CBMWriterMain(void)
int rc;
/* Clear any already-pending wakeups */
ResetLatch(&t_thrd.proc->procLatch);
ResetLatch(&t_thrd.proc->procLatch); // 清除挂起的唤醒请求
pgstat_report_activity(STATE_RUNNING, NULL);
@ -209,12 +225,12 @@ void CBMWriterMain(void)
*/
if (t_thrd.cbm_cxt.got_SIGHUP) {
t_thrd.cbm_cxt.got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
ProcessConfigFile(PGC_SIGHUP); // 配置文件
u_sess->attr.attr_storage.CheckPointTimeout = ENABLE_INCRE_CKPT
? u_sess->attr.attr_storage.incrCheckPointTimeout
: u_sess->attr.attr_storage.fullCheckPointTimeout;
}
// 如果收到关闭请求,则退出循环
if (t_thrd.cbm_cxt.shutdown_requested) {
g_instance.proc_base->cbmwriterLatch = NULL;
/* Normal exit from the walwriter is here */
@ -223,7 +239,8 @@ void CBMWriterMain(void)
CBMFollowXlog();
pgstat_report_activity(STATE_IDLE, NULL);
pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态
// 等待事件的发生包括信号量设置、超时、postmaster 死亡
rc = WaitLatch(&t_thrd.proc->procLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
(long)u_sess->attr.attr_storage.CheckPointTimeout * 1000);
@ -231,6 +248,7 @@ void CBMWriterMain(void)
/* Emergency bailout if postmaster has died. This is to avoid the
* necessity for manual cleanup of all postmaster children.
*/
// postmaster 退出时应急退出以避免手动清理所有 postmaster 子进程
if (rc & WL_POSTMASTER_DEATH) {
g_instance.proc_base->cbmwriterLatch = NULL;
gs_thread_exit(1);
@ -248,11 +266,21 @@ void CBMWriterMain(void)
* Some backend has bought the farm,
* so we need to stop what we're doing and exit.
*/
/*
* CBM Writer进程的紧急退出
*
* SIGNAL_ARGS
*
*/
static void CBM_quickdie(SIGNAL_ARGS)
{
g_instance.proc_base->cbmwriterLatch = NULL;
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
g_instance.proc_base->cbmwriterLatch = NULL; // 设置 cbmwriterLatch 为 NULL防止后续的唤醒
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); // 解除信号的阻塞状态
/*
cbmwriter进程会立即终止`proc_exit()`
*/
/*
* We DO NOT want to run proc_exit() callbacks -- we're here because
* shared memory may be corrupted, so we don't want to try to clean up our
@ -263,6 +291,10 @@ static void CBM_quickdie(SIGNAL_ARGS)
*/
on_exit_reset();
/*
exit(2)退 postmaster
cbmwriter进程
*/
/*
* Note we do exit(2) not exit(0). This is to force the postmaster into a
* system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
@ -275,37 +307,64 @@ static void CBM_quickdie(SIGNAL_ARGS)
}
/* SIGHUP: set flag to re-read config file at next convenient time */
/*
* SIGHUP 便
*
* :
* SIGNAL_ARGS:
*
* :
*
*/
static void CBMSigHupHandler(SIGNAL_ARGS)
{
int save_errno = errno;
int save_errno = errno; // 保存当前的错误码
t_thrd.cbm_cxt.got_SIGHUP = true;
t_thrd.cbm_cxt.got_SIGHUP = true; // 设置标志以指示需要在下一个方便的时间重新读取配置文件
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在则设置进程的Latch以唤醒进程
errno = save_errno;
errno = save_errno; // 恢复之前保存的错误码
}
/* SIGTERM: set flag to exit normally */
/*
* SIGTERM 退
*
* :
* SIGNAL_ARGS:
*
* :
*
*/
static void CBMShutdownHandler(SIGNAL_ARGS)
{
int save_errno = errno;
int save_errno = errno; // 保存当前的错误码
t_thrd.cbm_cxt.shutdown_requested = true;
t_thrd.cbm_cxt.shutdown_requested = true; // 设置标志以指示需要正常退出
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在则设置进程的Latch以唤醒进程
errno = save_errno;
errno = save_errno; // 恢复之前保存的错误码
}
/* SIGUSR1: used for latch wakeups */
/*
* SIGUSR1 Latch唤醒
*
* :
* SIGNAL_ARGS:
*
* :
*
*/
static void CBMwriter_sigusr1_handler(SIGNAL_ARGS)
{
int save_errno = errno;
int save_errno = errno; // 保存当前的错误码
latch_sigusr1_handler();
latch_sigusr1_handler(); // 处理SIGUSR1信号唤醒进程
errno = save_errno;
errno = save_errno; // 恢复之前保存的错误码
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,7 @@ pid_t fork_process(void)
* Presently stdout and stderr are the only stdio output channels used by
* the postmaster, so fflush'ing them should be sufficient.
*/
// 刷新标准输出和标准错误流,避免输出问题
fflush(stdout);
fflush(stderr);
@ -57,15 +58,16 @@ pid_t fork_process(void)
getitimer(ITIMER_PROF, &prof_itimer);
#endif
result = fork();
result = fork(); // 创建子进程
if (result == 0) {
if (result == 0) { // fork成功
/* fork succeeded, in child */
#ifdef LINUX_PROFILE
setitimer(ITIMER_PROF, &prof_itimer, NULL);
setitimer(ITIMER_PROF, &prof_itimer, NULL); // 设置性能分析计时器,以保证子进程也能进行性能分析
#endif
/*
* Linux系统中保护postmaster进程免受OOM杀死的问题
* By default, Linux tends to kill the postmaster in out-of-memory
* situations, because it blames the postmaster for the sum of child
* process sizes *including shared memory*. (This is unbelievably
@ -84,19 +86,19 @@ pid_t fork_process(void)
* Use open() not stdio, to ensure we control the open flags. Some
* Linux security environments reject anything but O_WRONLY.
*/
int fd = open("/proc/self/oom_score_adj", O_WRONLY, 0);
int fd = open("/proc/self/oom_score_adj", O_WRONLY, 0); // 打开OOM分数调整文件
/* We ignore all errors */
if (fd >= 0) {
char buf[16];
int rc;
char buf[16]; // 用于存储要写入文件的字符串
int rc; // 用于存储write()函数的返回值
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_SCORE_ADJ);
securec_check_intval(rcs, );
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_SCORE_ADJ); // 格式化OOM分数调整值为字符串
securec_check_intval(rcs, ); // 检查snprintf_s函数的返回值
rc = write(fd, buf, strlen(buf));
(void)rc;
close(fd);
rc = write(fd, buf, strlen(buf)); // 将OOM分数调整值写入文件
(void)rc; // 防止编译器警告
close(fd); // 关闭文件
}
}
#endif /* LINUX_OOM_SCORE_ADJ */
@ -113,25 +115,25 @@ pid_t fork_process(void)
* Use open() not stdio, to ensure we control the open flags. Some
* Linux security environments reject anything but O_WRONLY.
*/
int fd = open("/proc/self/oom_adj", O_WRONLY, 0);
int fd = open("/proc/self/oom_adj", O_WRONLY, 0); // 打开OOM调整文件
/* We ignore all errors */
if (fd >= 0) {
char buf[16];
int rc;
char buf[16];// 用于存储要写入文件的字符串
int rc; // 用于存储write()函数的返回值
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_ADJ);
securec_check_intval(rcs, );
securec_check_intval(rcs, ); // 检查snprintf_s函数的返回值
rc = write(fd, buf, strlen(buf));
(void)rc;
close(fd);
rc = write(fd, buf, strlen(buf)); // 将OOM调整值写入文件
(void)rc; // 防止编译器警告
close(fd); // 关闭文件
}
}
#endif /* LINUX_OOM_ADJ */
/* Binding static TLS variables for current thread */
EarlyBindingTLSVariables();
EarlyBindingTLSVariables(); // 绑定当前线程的静态TLS变量
}
return result;

View File

@ -60,52 +60,94 @@ static void GlobalstatsSigusr2Handler(SIGNAL_ARGS);
static void GlobalstatsSigtermHandler(SIGNAL_ARGS);
/* SIGHUP: set flag to re-read config file at next convenient time */
/*
* SIGHUP 便
*
* :
* SIGNAL_ARGS:
*
* :
*
*/
static void GlobalstatsSighupHandler(SIGNAL_ARGS)
{
int saveErrno = errno;
int saveErrno = errno; // 保存当前的错误码
t_thrd.gstat_cxt.got_SIGHUP = true;
t_thrd.gstat_cxt.got_SIGHUP = true; // 设置标志以指示需要在下一个方便的时间重新读取配置文件
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch);// 如果进程存在则设置进程的Latch以唤醒进程
errno = saveErrno;
errno = saveErrno;// 恢复之前保存的错误码
}
/* SIGUSR2: a worker is up and running, or just finished, or failed to fork */
/*
* SIGUSR2 fork
*
* :
* SIGNAL_ARGS:
*
* :
*
*/
static void GlobalstatsSigusr2Handler(SIGNAL_ARGS)
{
int saveErrno = errno;
int saveErrno = errno; // 保存当前的错误码
t_thrd.gstat_cxt.got_SIGUSR2 = true;
t_thrd.gstat_cxt.got_SIGUSR2 = true; // 设置标志以指示接收到SIGUSR2信号
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在则设置进程的Latch以唤醒进程
errno = saveErrno;
errno = saveErrno; // 恢复之前保存的错误码
}
/* SIGTERM: time to die */
/*
* SIGTERM 退
*
* :
* SIGNAL_ARGS:
*
* :
*
*/
static void GlobalstatsSigtermHandler(SIGNAL_ARGS)
{
int saveErrno = errno;
int saveErrno = errno;// 保存当前的错误码
t_thrd.gstat_cxt.got_SIGTERM = true;
t_thrd.gstat_cxt.got_SIGTERM = true; // 设置标志以指示需要正常退出
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在则设置进程的Latch以唤醒进程
errno = saveErrno;
errno = saveErrno;// 恢复之前保存的错误码
}
/*
*
*
* 线
* quiesce 线
* quiesce 0
*
* :
*
* :
*/
static void PrepareStatsHashForSwitch()
{
// 断言 quiesce 标志为 0确保没有其他线程正在切换
Assert(pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->quiesce) == 0);
// 将 quiesce 标志设置为 1表示正在切换
pg_atomic_exchange_u64(&g_instance.stat_cxt.tableStat->quiesce, 1);
// 获取当前读取哈希表的线程数
uint64 readers = pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->readers);
// 断言读取线程数小于总线程数
Assert(readers < (uint64) GLOBAL_ALL_PROCS);
/* Wait until all readers are done */
while (readers > 0) {
pg_usleep(10000L);
while (readers > 0) { // 等待所有读取线程完成
pg_usleep(10000L); // 等待一段时间,以免过于频繁地检查
// 重新获取读取线程数
readers = pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->readers);
}
}
@ -118,16 +160,31 @@ static void CompleteStatsHashSwitch()
pg_atomic_exchange_u64(&g_instance.stat_cxt.tableStat->quiesce, 0);
}
/*
*
*
* PgStat_StartBlockTableKey
* relidparentid dbid
*
* :
* - left:
* - right:
* - keysize:
*
* : 0 0
*/
static int MatchDictItem(const void* left, const void* right, Size keysize)
{
// 将左右两个键转换为 PgStat_StartBlockTableKey 类型
const PgStat_StartBlockTableKey* leftItem = (PgStat_StartBlockTableKey*)left;
const PgStat_StartBlockTableKey* rightItem = (PgStat_StartBlockTableKey*)right;
Assert(leftItem != NULL && rightItem != NULL);
Assert(leftItem != NULL && rightItem != NULL); // 断言左右键都不为空
/* we just care whether the result is 0 or not. */
// 如果 relid、parentid 和 dbid 都相等,则返回 0表示匹配
if (leftItem->relid != rightItem->relid || leftItem->parentid != rightItem->parentid ||
leftItem->dbid != rightItem->dbid) {
return 1;
return 1; // 不匹配,返回非 0 值
}
return 0;
@ -138,26 +195,39 @@ static uint32 HashDictItem(const void* key, Size keysize)
return DatumGetUInt32(hash_any((const unsigned char*)key, sizeof(PgStat_StartBlockTableKey)));
}
/*
*
*
* :
*
* :
*/
void GlobalStatsTrackerInit()
{
/* Setup a shared memory context that other backends can access.
* This will hold the Global Statistics Hash
*/
// 设置一个其他后端进程可以访问的共享内存上下文
g_instance.stat_cxt.tableStat->global_stats_cxt =
AllocSetContextCreate((MemoryContext)g_instance.instance_context, "GlobalStatisticsContext",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
// 初始化哈希表控制结构
HASHCTL hashCtrl;
errno_t rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl));
securec_check(rc, "", "");
// 设置哈希表的哈希函数和比较函数
hashCtrl.hash = (HashValueFunc)HashDictItem;
hashCtrl.match = (HashCompareFunc)MatchDictItem;
// 设置哈希表键的大小和条目的大小
hashCtrl.keysize = (Size)sizeof(PgStat_StartBlockTableKey);
hashCtrl.entrysize = (Size)sizeof(PgStat_StartBlockTableEntry);
// 设置哈希表的上下文为全局统计信息上下文
hashCtrl.hcxt = g_instance.stat_cxt.tableStat->global_stats_cxt;
// 设置哈希表的分区数
hashCtrl.num_partitions = NUM_STARTBLOCK_PARTITIONS;
int flags = (HASH_FUNCTION | HASH_COMPARE | HASH_ELEM | HASH_SHRCTX | HASH_PARTITION);
// 创建全局统计信息哈希表
g_instance.stat_cxt.tableStat->blocks_map =
hash_create("Candidate Blocks for Pruning Hash", NUM_STARTBLOCK_PARTITIONS, &hashCtrl, flags);
}
@ -167,9 +237,17 @@ bool IsGlobalStatsTrackerProcess()
return t_thrd.role == GLOBALSTATS_THREAD;
}
/*
*
*
*
* :
*
* :
*/
NON_EXEC_STATIC void GlobalStatsTrackerMain()
{
sigjmp_buf localSigjmpBuf;
sigjmp_buf localSigjmpBuf; // 用于保存异常跳转的上下文信息
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
@ -181,29 +259,31 @@ NON_EXEC_STATIC void GlobalStatsTrackerMain()
t_thrd.proc_cxt.MyProgName = "StatsTracker";
Assert(t_thrd.proc->pid == t_thrd.proc_cxt.MyProcPid);
init_ps_display("global stats process", "", "", "");
Assert(t_thrd.proc->pid == t_thrd.proc_cxt.MyProcPid); // 断言当前进程的 PID 与 t_thrd 中记录的一致
init_ps_display("global stats process", "", "", ""); // 初始化进程状态的显示
// 输出日志,表示全局统计信息收集器已启动
ereport(LOG, (errmsg("global stats collector started")));
SetProcessingMode(InitProcessing);
SetProcessingMode(InitProcessing); // 设置当前进程的处理模式为初始化模式,用于执行一些初始化操作
/*
* Set up signal handlers. We operate on databases much like a regular
* backend, so we use the same signal handling. See equivalent code in
* tcop/postgres.c.
*/
gspqsignal(SIGHUP, GlobalstatsSighupHandler);
gspqsignal(SIGINT, StatementCancelHandler);
gspqsignal(SIGTERM, GlobalstatsSigtermHandler);
// 设置信号处理程序
gspqsignal(SIGHUP, GlobalstatsSighupHandler); // 用于重新读取配置文件
gspqsignal(SIGINT, StatementCancelHandler); // 用于取消正在执行的语句
gspqsignal(SIGTERM, GlobalstatsSigtermHandler); // 用于终止进程
gspqsignal(SIGQUIT, quickdie);
gspqsignal(SIGALRM, handle_sig_alarm);
gspqsignal(SIGQUIT, quickdie); // 用于快速终止进程
gspqsignal(SIGALRM, handle_sig_alarm); // 用于处理定时器信号
gspqsignal(SIGPIPE, SIG_IGN);
gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
gspqsignal(SIGUSR2, GlobalstatsSigusr2Handler);
gspqsignal(SIGFPE, FloatExceptionHandler);
gspqsignal(SIGCHLD, SIG_DFL);
gspqsignal(SIGPIPE, SIG_IGN); // 用于避免进程在写入已关闭的管道时终止
gspqsignal(SIGUSR1, procsignal_sigusr1_handler); // 用于用户自定义的信号处理
gspqsignal(SIGUSR2, GlobalstatsSigusr2Handler); // 用于指示一个工作进程正在运行、刚刚完成或者在 fork 失败
gspqsignal(SIGFPE, FloatExceptionHandler); // 用于处理浮点异常
gspqsignal(SIGCHLD, SIG_DFL); // 用于处理子进程的状态变化
/* Early initialization */
BaseInit();
@ -215,74 +295,75 @@ NON_EXEC_STATIC void GlobalStatsTrackerMain()
* had to do some stuff with LWLocks).
*/
#ifndef EXEC_BACKEND
InitProcess();
InitProcess(); // 初始化进程上下文
#endif
SetProcessingMode(NormalProcessing);
SetProcessingMode(NormalProcessing); // 设置当前进程的处理模式为正常处理模式,用于正常的数据库操作
/* Unblock signals (they were blocked when the postmaster forked us) */
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 取消了屏蔽以允许信号被处理
(void)gs_signal_unblock_sigusr2(); // 取消对 SIGUSR2 信号[用于后台进程的状态更新]的屏蔽
/*
* If an exception is encountered, processing resumes here.
*
* This code is a stripped down version of PostgresMain error recovery.
*/
if (sigsetjmp(localSigjmpBuf, 1) != 0) {
// 错误恢复处理
if (sigsetjmp(localSigjmpBuf, 1) != 0) { // 如果在执行过程中出现错误,将会跳转到此处
/* since not using PG_TRY, must reset error stack by hand */
t_thrd.log_cxt.error_context_stack = NULL;
t_thrd.log_cxt.call_stack = NULL;
/* Prevents interrupts while cleaning up */
HOLD_INTERRUPTS();
HOLD_INTERRUPTS(); // 禁止中断,以确保在清理操作期间不会被中断
/* Report the error to the server log */
EmitErrorReport();
EmitErrorReport(); // 将错误信息记录到服务器日志中
/* release resource held by lsc */
AtEOXact_SysDBCache(false);
AtEOXact_SysDBCache(false); // 释放系统数据库缓存,以确保清理操作
FlushErrorState();
FlushErrorState(); // 刷新错误状态,以清除错误信息
/* Now we can allow interrupts again */
RESUME_INTERRUPTS();
RESUME_INTERRUPTS(); // 允许中断,以便继续正常处理
/* if in shutdown mode, no need for anything further; just go away */
if (t_thrd.gstat_cxt.got_SIGTERM)
if (t_thrd.gstat_cxt.got_SIGTERM) // 如果收到了 SIGTERM 信号,就跳转到关闭操作
goto shutdown;
/*
* Sleep at least 1 second after any error. We don't want to be
* filling the error logs as fast as we can.
*/
pg_usleep(1000000L);
pg_usleep(1000000L); // 如果出现错误等待至少1秒后再继续以避免日志填充过快
}
while (!t_thrd.gstat_cxt.got_SIGTERM) {
while (!t_thrd.gstat_cxt.got_SIGTERM) { // 循环,直到收到 SIGTERM 信号
/* backup the old one so we can delete it when nobody needs it anymore */
MemoryContext oldStatLocalContext = u_sess->stat_cxt.pgStatLocalContext;
MemoryContext oldStatLocalContext = u_sess->stat_cxt.pgStatLocalContext; // 保存旧的统计信息内存上下文,以便稍后进行清理操作
// 创建一个新的内存上下文用于存储全局统计信息的快照数据
u_sess->stat_cxt.pgStatLocalContext =
AllocSetContextCreate(u_sess->top_mem_cxt, "Global Statistics snapshot",
ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE);
pgstat_fetch_global();
pgstat_fetch_global(); // 从全局统计信息中抓取数据并存储在新的内存上下文中
/* switch global_stats_map to point to the newly loaded statistics */
PrepareStatsHashForSwitch();
g_instance.stat_cxt.tableStat->global_stats_map = u_sess->stat_cxt.pgStatDBHash;
CompleteStatsHashSwitch();
PrepareStatsHashForSwitch(); // 执行准备切换操作,确保全局统计信息的安全切换
g_instance.stat_cxt.tableStat->global_stats_map = u_sess->stat_cxt.pgStatDBHash; // 将全局统计信息的映射指针切换到新的数据
CompleteStatsHashSwitch(); // 完成切换操作,确保新的数据结构已生效
/* Now destroy the old one */
if (oldStatLocalContext) {
MemoryContextDelete(oldStatLocalContext);
if (oldStatLocalContext) { // 检查旧的统计信息内存上下文是否存在
MemoryContextDelete(oldStatLocalContext); // 存在则释放
}
u_sess->stat_cxt.pgStatDBHash = NULL;
u_sess->stat_cxt.pgStatDBHash = NULL; // 设为NULL确保不再引用旧的数据
pg_usleep(u_sess->attr.attr_storage.ustats_tracker_naptime * 1000000L);
pg_usleep(u_sess->attr.attr_storage.ustats_tracker_naptime * 1000000L); // 使进程休眠,等待下一次获取全局统计信息的时间
}
shutdown:
@ -290,11 +371,11 @@ shutdown:
* Before the thread exits, set global_stats_map to NULL to prevent core dump when the
* backend thread accesses the released memory during the prune operation.
*/
PrepareStatsHashForSwitch();
g_instance.stat_cxt.tableStat->global_stats_map = NULL;
CompleteStatsHashSwitch();
ereport(LOG, (errmsg("global stats shutting down")));
proc_exit(0);
PrepareStatsHashForSwitch(); // 准备切换操作,确保在关闭之前对全局统计信息进行最后的处理
g_instance.stat_cxt.tableStat->global_stats_map = NULL; // 确保在关闭时不再访问释放的内存
CompleteStatsHashSwitch(); // 完成切换操作,确保新的状态已生效
ereport(LOG, (errmsg("global stats shutting down"))); // 生成日志,表示全局统计信息追踪器正在关闭
proc_exit(0); // 正常退出当前进程
}
@ -303,24 +384,41 @@ shutdown:
* relid is the partition id for a Partitioned table, otherwise it's the Relation id.
* parentid is the actual Relation id for a Partitioned table, otherwise it's InvalidOid.
*/
/*
*
*
*
* Oid dbid
* Oid relid
* Oid parentid
* PgStat_StatTabEntry *tableentry
*
*
* bool类型 true
* false
*
*/
bool GetTableGstats(Oid dbid, Oid relid, Oid parentid, PgStat_StatTabEntry *tableentry)
{
/* return if stats is not ready */
// 检查全局统计信息的映射是否为 NULL以及是否处于 "quiesce" 状态。如果是,说明统计信息尚未准备好,返回 false
if (g_instance.stat_cxt.tableStat->global_stats_map == NULL ||
pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->quiesce) == 1) {
return false;
}
uint64 readers PG_USED_FOR_ASSERTS_ONLY = pg_atomic_add_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1);
Assert(readers < (uint64) GLOBAL_ALL_PROCS);
uint64 readers PG_USED_FOR_ASSERTS_ONLY = pg_atomic_add_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1); // 增加全局统计信息读取者的计数
Assert(readers < (uint64) GLOBAL_ALL_PROCS); // 断言检查 readers 是否小于 GLOBAL_ALL_PROCS用于限制并发访问全局统计信息的进程数量
/* recheck in case quiesce is updated between first check and increment readers */
// 再次检查 "quiesce" 状态,以确保在增加读取者计数期间未更改状态。如果状态已更改,则减少读取者计数并返回 false
if (pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->quiesce) == 1) {
pg_atomic_sub_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1);
return false;
}
Assert(g_instance.stat_cxt.tableStat->global_stats_map != NULL);
Assert(g_instance.stat_cxt.tableStat->global_stats_map != NULL); // 确保全局统计信息的映射不为 NULL
// 定义指向数据库和表统计信息条目的指针
PgStat_StatDBEntry *dbentry = NULL;
PgStat_StatTabEntry *tabentry = NULL;
errno_t rc = 0;
@ -329,87 +427,129 @@ bool GetTableGstats(Oid dbid, Oid relid, Oid parentid, PgStat_StatTabEntry *tabl
PgStat_StatTabKey tabkey;
tabkey.statFlag = parentid;
tabkey.tableid = relid;
// 通过数据库标识从全局统计信息映射中查找数据库统计信息条目。如果没有找到dbentry 将为 NULL
dbentry = (PgStat_StatDBEntry*)hash_search(g_instance.stat_cxt.tableStat->global_stats_map,
(void*)&dbid, HASH_FIND, NULL);
if (dbentry == NULL) {
goto done;
}
// 通过构建的键从数据库统计信息中查找表的统计信息条目。如果没有找到tabentry 将为 NULL
tabentry = (PgStat_StatTabEntry*)hash_search(dbentry->tables, (void*)(&tabkey), HASH_FIND, NULL);
if (tabentry == NULL) {
goto done;
}
// 如果找到了统计信息条目,函数将其复制到 tableentry 中,以便返回给调用者
rc = memcpy_s(tableentry, sizeof(PgStat_StatTabEntry),
tabentry, sizeof(PgStat_StatTabEntry));
securec_check(rc, "", "");
result = true;
result = true; // 表示成功获取了统计信息
done:
readers = pg_atomic_sub_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1);
readers = pg_atomic_sub_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1); // 减少读取者计数
Assert(readers < (uint64) GLOBAL_ALL_PROCS);
return result;
}
/*
*访 "StartBlock"
*
*
* PgStat_StartBlockTableKey *tabkey访
* LWLockMode modeSHAREDEXCLUSIVE
*
*
* LWLock *
*
*/
static LWLock *LockStartBlockHashTablePartition(PgStat_StartBlockTableKey *tabkey, LWLockMode mode)
{
uint32 hashValue = get_hash_value(g_instance.stat_cxt.tableStat->blocks_map, tabkey);
uint32 partition = hashValue % (NUM_STARTBLOCK_PARTITIONS);
uint32 lockid = (uint32)(FirstStartBlockMappingLock + partition);
LWLock* lock = &t_thrd.shemem_ptr_cxt.mainLWLockArray[lockid].lock;
uint32 hashValue = get_hash_value(g_instance.stat_cxt.tableStat->blocks_map, tabkey); // 计算哈希值
uint32 partition = hashValue % (NUM_STARTBLOCK_PARTITIONS); // 计算分区号,用于确定哈希表中的哪个分区将用于锁定
uint32 lockid = (uint32)(FirstStartBlockMappingLock + partition); // 计算锁标识,用于确定要锁定的锁
LWLock* lock = &t_thrd.shemem_ptr_cxt.mainLWLockArray[lockid].lock; // 使用的是哈希分区的锁来控制对哈希表分区的访问
LWLockAcquire(lock, mode);
LWLockAcquire(lock, mode); // 获取锁
return lock;
}
/*
* "StartBlock" PgStat_StartBlockTableKey PgStat_StartBlockTableEntry
*
*
* PgStat_StartBlockTableKey *tabkey
*
*
* PgStat_StartBlockTableEntry * NULL
*/
PgStat_StartBlockTableEntry *
StartBlockHashTableLookup(PgStat_StartBlockTableKey *tabkey)
{
PgStat_StartBlockTableEntry *result = NULL;
bool found = false;
PgStat_StartBlockTableEntry *result = NULL; // 用于存储查找结果
bool found = false; // 用于表示是否找到了匹配的条目
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_SHARED);
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_SHARED); // 获取共享锁
// 查找匹配的条目,如果找到了,就将它的地址赋给 result ,并将 found 设置为 true否则result 保持为 NULLfound 保持为 false。
result = (PgStat_StartBlockTableEntry *) hash_search(g_instance.stat_cxt.tableStat->blocks_map,
tabkey, HASH_FIND, &found);
LWLockRelease(lock);
LWLockRelease(lock); // 释放之前获取的共享锁
return result;
}
/*
* "StartBlock"
*
*
* PgStat_StartBlockTableKey *tabkey
*
*
* PgStat_StartBlockTableEntry *
*
*/
PgStat_StartBlockTableEntry *
StartBlockHashTableAdd(PgStat_StartBlockTableKey *tabkey)
{
bool found = true;
PgStat_StartBlockTableEntry *result = NULL;
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_EXCLUSIVE);
bool found = true; // 用于指示在添加条目时是否找到了已存在的匹配条目
PgStat_StartBlockTableEntry *result = NULL; // 用于存储添加的条目或者已存在的匹配条目
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_EXCLUSIVE); // 获取独占锁
// 查找匹配的条目,如果找到了,就将它的地址赋给 result ,并将 found 设置为 true否则result 保持为 NULLfound 保持为 false。
result = (PgStat_StartBlockTableEntry *)hash_search(g_instance.stat_cxt.tableStat->blocks_map,
tabkey, HASH_ENTER, &found);
if (!found) {
if (!found) { // 如果有找到匹配的条目
// 用于初始化 result 条目中的 starting_blocks 数组。它将数组中的元素设置为从 0 到 START_BLOCK_ARRAY_SIZE - 1 的连续整数值
for (int i = 0; i < START_BLOCK_ARRAY_SIZE; i++) {
result->starting_blocks[i] = i;
}
}
LWLockRelease(lock);
LWLockRelease(lock); // 释放获取的独占锁
return result;
}
/*
* "StartBlock"
*
*
* PgStat_StartBlockTableKey *tabkey
*
*
* PgStat_StartBlockTableEntry *
*
*/
PgStat_StartBlockTableEntry *
GetStartBlockHashEntry(PgStat_StartBlockTableKey *tabkey)
{
PgStat_StartBlockTableEntry *result = NULL;
result = StartBlockHashTableLookup(tabkey);
PgStat_StartBlockTableEntry *result = NULL; // 用于存储获取的条目
result = StartBlockHashTableLookup(tabkey); // 用于查找哈希表中是否存在与给定键匹配的条目
/* not found, add it */
if (result == NULL) {
result = StartBlockHashTableAdd(tabkey);
if (result == NULL) { // 如果没有找到匹配的条目
result = StartBlockHashTableAdd(tabkey); // 添加新的条目
}
Assert(result);
Assert(result); // 确保 result 变量不为 NULL
return result;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More