diff --git a/automation/sqlrepo/features/fdw_extension_tests/create_extension/expected/query01.ans b/automation/sqlrepo/features/fdw_extension_tests/create_extension/expected/query01.ans index dc264fa26..e5f264e04 100644 --- a/automation/sqlrepo/features/fdw_extension_tests/create_extension/expected/query01.ans +++ b/automation/sqlrepo/features/fdw_extension_tests/create_extension/expected/query01.ans @@ -17,13 +17,13 @@ SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; extversion ------------ - 2.0 + 2.1 (1 row) \dx+ pxf_fdw - Objects in extension "pxf_fdw" - Object Description ----------------------------------------- + Objects in extension "pxf_fdw" + Object Description +--------------------------------------------- foreign-data wrapper abfss_pxf_fdw foreign-data wrapper file_pxf_fdw foreign-data wrapper gs_pxf_fdw @@ -33,6 +33,12 @@ SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; foreign-data wrapper jdbc_pxf_fdw foreign-data wrapper s3_pxf_fdw foreign-data wrapper wasbs_pxf_fdw + function pxf_cancel_backend(integer) + function pxf_cancel_backend_raw(integer) function pxf_fdw_handler() function pxf_fdw_validator(text[],oid) -(11 rows) + function pxf_interrupt_backend(integer) + function pxf_interrupt_backend_raw(integer) + function pxf_stat_activity_raw() + view pxf_stat_activity +(17 rows) diff --git a/automation/sqlrepo/features/fdw_extension_tests/downgrade_then_upgrade/step_1_check_extension/expected/query01.ans b/automation/sqlrepo/features/fdw_extension_tests/downgrade_then_upgrade/step_1_check_extension/expected/query01.ans index c11dc91a1..97461cf03 100644 --- a/automation/sqlrepo/features/fdw_extension_tests/downgrade_then_upgrade/step_1_check_extension/expected/query01.ans +++ b/automation/sqlrepo/features/fdw_extension_tests/downgrade_then_upgrade/step_1_check_extension/expected/query01.ans @@ -17,13 +17,13 @@ SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; extversion ------------ - 2.0 + 2.1 (1 row) \dx+ pxf_fdw - Objects in extension "pxf_fdw" - Object Description ----------------------------------------- + Objects in extension "pxf_fdw" + Object Description +--------------------------------------------- foreign-data wrapper abfss_pxf_fdw foreign-data wrapper file_pxf_fdw foreign-data wrapper gs_pxf_fdw @@ -33,6 +33,12 @@ SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; foreign-data wrapper jdbc_pxf_fdw foreign-data wrapper s3_pxf_fdw foreign-data wrapper wasbs_pxf_fdw + function pxf_cancel_backend(integer) + function pxf_cancel_backend_raw(integer) function pxf_fdw_handler() function pxf_fdw_validator(text[],oid) -(11 rows) + function pxf_interrupt_backend(integer) + function pxf_interrupt_backend_raw(integer) + function pxf_stat_activity_raw() + view pxf_stat_activity +(17 rows) diff --git a/automation/sqlrepo/features/fdw_extension_tests/upgrade/step_2_after_alter_extension/expected/query01.ans b/automation/sqlrepo/features/fdw_extension_tests/upgrade/step_2_after_alter_extension/expected/query01.ans index 78cced15e..cf6ce291a 100644 --- a/automation/sqlrepo/features/fdw_extension_tests/upgrade/step_2_after_alter_extension/expected/query01.ans +++ b/automation/sqlrepo/features/fdw_extension_tests/upgrade/step_2_after_alter_extension/expected/query01.ans @@ -17,13 +17,13 @@ SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; extversion ------------ - 2.0 + 2.1 (1 row) \dx+ pxf_fdw - Objects in extension "pxf_fdw" - Object Description ----------------------------------------- + Objects in extension "pxf_fdw" + Object Description +--------------------------------------------- foreign-data wrapper abfss_pxf_fdw foreign-data wrapper file_pxf_fdw foreign-data wrapper gs_pxf_fdw @@ -33,9 +33,15 @@ SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; foreign-data wrapper jdbc_pxf_fdw foreign-data wrapper s3_pxf_fdw foreign-data wrapper wasbs_pxf_fdw + function pxf_cancel_backend(integer) + function pxf_cancel_backend_raw(integer) function pxf_fdw_handler() function pxf_fdw_validator(text[],oid) -(11 rows) + function pxf_interrupt_backend(integer) + function pxf_interrupt_backend_raw(integer) + function pxf_stat_activity_raw() + view pxf_stat_activity +(17 rows) SELECT fdw.fdwname FROM pg_catalog.pg_foreign_data_wrapper fdw where fdw.fdwname='adl_pxf_fdw'; fdwname diff --git a/automation/sqlrepo/features/fdw_extension_tests_2_1/create_extension/expected/query01.ans b/automation/sqlrepo/features/fdw_extension_tests_2_1/create_extension/expected/query01.ans new file mode 100644 index 000000000..8b18860c7 --- /dev/null +++ b/automation/sqlrepo/features/fdw_extension_tests_2_1/create_extension/expected/query01.ans @@ -0,0 +1,82 @@ +-- start_ignore +-- end_ignore +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- @description query01 for PXF FDW 2.1 install test (pxf_stat_activity + backend control) +-- start_matchsubs +-- +-- m{.*/usr/local/pxf-(dev|gp\d).*} +-- s{/usr/local/pxf-(dev|gp\d)}{\$PXF_HOME} +-- +-- m{.*\$libdir/pxf.*} +-- s{\$libdir}{\$PXF_HOME/gpextable} +-- +-- end_matchsubs +-- start_ignore +\c pxfautomation_extension +-- end_ignore + +SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; + extversion +------------ + 2.1 +(1 row) + +\dx+ pxf_fdw + Objects in extension "pxf_fdw" + Object Description +--------------------------------------------- + foreign-data wrapper abfss_pxf_fdw + foreign-data wrapper file_pxf_fdw + foreign-data wrapper gs_pxf_fdw + foreign-data wrapper hbase_pxf_fdw + foreign-data wrapper hdfs_pxf_fdw + foreign-data wrapper hive_pxf_fdw + foreign-data wrapper jdbc_pxf_fdw + foreign-data wrapper s3_pxf_fdw + foreign-data wrapper wasbs_pxf_fdw + function pxf_cancel_backend(integer) + function pxf_cancel_backend_raw(integer) + function pxf_fdw_handler() + function pxf_fdw_validator(text[],oid) + function pxf_interrupt_backend(integer) + function pxf_interrupt_backend_raw(integer) + function pxf_stat_activity_raw() + view pxf_stat_activity +(17 rows) + +-- backend-control functions are reachable end-to-end (C -> local PXF -> registry); +-- with no in-flight requests they are no-ops returning 0 +SELECT pxf_cancel_backend(-1); + pxf_cancel_backend +-------------------- + 0 +(1 row) + +SELECT pxf_interrupt_backend(-1); + pxf_interrupt_backend +----------------------- + 0 +(1 row) + +-- the activity view is empty while PXF is idle +SELECT count(*) FROM pxf_stat_activity; + count +------- + 0 +(1 row) diff --git a/automation/sqlrepo/features/fdw_extension_tests_2_1/create_extension/sql/query01.sql b/automation/sqlrepo/features/fdw_extension_tests_2_1/create_extension/sql/query01.sql new file mode 100644 index 000000000..9d293f2d7 --- /dev/null +++ b/automation/sqlrepo/features/fdw_extension_tests_2_1/create_extension/sql/query01.sql @@ -0,0 +1,42 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- @description query01 for PXF FDW 2.1 install test (pxf_stat_activity + backend control) +-- start_matchsubs +-- +-- m{.*/usr/local/pxf-(dev|gp\d).*} +-- s{/usr/local/pxf-(dev|gp\d)}{\$PXF_HOME} +-- +-- m{.*\$libdir/pxf.*} +-- s{\$libdir}{\$PXF_HOME/gpextable} +-- +-- end_matchsubs +-- start_ignore +\c pxfautomation_extension +-- end_ignore + +SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; + +\dx+ pxf_fdw + +-- backend-control functions are reachable end-to-end (C -> local PXF -> registry); +-- with no in-flight requests they are no-ops returning 0 +SELECT pxf_cancel_backend(-1); +SELECT pxf_interrupt_backend(-1); + +-- the activity view is empty while PXF is idle +SELECT count(*) FROM pxf_stat_activity; diff --git a/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_1_create_2_0/expected/query01.ans b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_1_create_2_0/expected/query01.ans new file mode 100644 index 000000000..b10ae349e --- /dev/null +++ b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_1_create_2_0/expected/query01.ans @@ -0,0 +1,55 @@ +-- start_ignore +-- end_ignore +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- @description query01 for PXF FDW 2.1 upgrade/downgrade test - step 1: create at 2.0 +-- start_matchsubs +-- +-- m{.*/usr/local/pxf-(dev|gp\d).*} +-- s{/usr/local/pxf-(dev|gp\d)}{\$PXF_HOME} +-- +-- m{.*\$libdir/pxf.*} +-- s{\$libdir}{\$PXF_HOME/gpextable} +-- +-- end_matchsubs +-- start_ignore +\c pxfautomation_extension +-- end_ignore + +SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; + extversion +------------ + 2.0 +(1 row) + +\dx+ pxf_fdw + Objects in extension "pxf_fdw" + Object Description +---------------------------------------- + foreign-data wrapper abfss_pxf_fdw + foreign-data wrapper file_pxf_fdw + foreign-data wrapper gs_pxf_fdw + foreign-data wrapper hbase_pxf_fdw + foreign-data wrapper hdfs_pxf_fdw + foreign-data wrapper hive_pxf_fdw + foreign-data wrapper jdbc_pxf_fdw + foreign-data wrapper s3_pxf_fdw + foreign-data wrapper wasbs_pxf_fdw + function pxf_fdw_handler() + function pxf_fdw_validator(text[],oid) +(11 rows) diff --git a/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_1_create_2_0/sql/query01.sql b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_1_create_2_0/sql/query01.sql new file mode 100644 index 000000000..74909d72a --- /dev/null +++ b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_1_create_2_0/sql/query01.sql @@ -0,0 +1,34 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- @description query01 for PXF FDW 2.1 upgrade/downgrade test - step 1: create at 2.0 +-- start_matchsubs +-- +-- m{.*/usr/local/pxf-(dev|gp\d).*} +-- s{/usr/local/pxf-(dev|gp\d)}{\$PXF_HOME} +-- +-- m{.*\$libdir/pxf.*} +-- s{\$libdir}{\$PXF_HOME/gpextable} +-- +-- end_matchsubs +-- start_ignore +\c pxfautomation_extension +-- end_ignore + +SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; + +\dx+ pxf_fdw diff --git a/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_2_after_upgrade_2_1/expected/query01.ans b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_2_after_upgrade_2_1/expected/query01.ans new file mode 100644 index 000000000..2bd7b522b --- /dev/null +++ b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_2_after_upgrade_2_1/expected/query01.ans @@ -0,0 +1,61 @@ +-- start_ignore +-- end_ignore +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- @description query01 for PXF FDW 2.1 upgrade/downgrade test - step 2: after upgrade to 2.1 +-- start_matchsubs +-- +-- m{.*/usr/local/pxf-(dev|gp\d).*} +-- s{/usr/local/pxf-(dev|gp\d)}{\$PXF_HOME} +-- +-- m{.*\$libdir/pxf.*} +-- s{\$libdir}{\$PXF_HOME/gpextable} +-- +-- end_matchsubs +-- start_ignore +\c pxfautomation_extension +-- end_ignore + +SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; + extversion +------------ + 2.1 +(1 row) + +\dx+ pxf_fdw + Objects in extension "pxf_fdw" + Object Description +--------------------------------------------- + foreign-data wrapper abfss_pxf_fdw + foreign-data wrapper file_pxf_fdw + foreign-data wrapper gs_pxf_fdw + foreign-data wrapper hbase_pxf_fdw + foreign-data wrapper hdfs_pxf_fdw + foreign-data wrapper hive_pxf_fdw + foreign-data wrapper jdbc_pxf_fdw + foreign-data wrapper s3_pxf_fdw + foreign-data wrapper wasbs_pxf_fdw + function pxf_cancel_backend(integer) + function pxf_cancel_backend_raw(integer) + function pxf_fdw_handler() + function pxf_fdw_validator(text[],oid) + function pxf_interrupt_backend(integer) + function pxf_interrupt_backend_raw(integer) + function pxf_stat_activity_raw() + view pxf_stat_activity +(17 rows) diff --git a/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_2_after_upgrade_2_1/sql/query01.sql b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_2_after_upgrade_2_1/sql/query01.sql new file mode 100644 index 000000000..19b1efc73 --- /dev/null +++ b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_2_after_upgrade_2_1/sql/query01.sql @@ -0,0 +1,34 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- @description query01 for PXF FDW 2.1 upgrade/downgrade test - step 2: after upgrade to 2.1 +-- start_matchsubs +-- +-- m{.*/usr/local/pxf-(dev|gp\d).*} +-- s{/usr/local/pxf-(dev|gp\d)}{\$PXF_HOME} +-- +-- m{.*\$libdir/pxf.*} +-- s{\$libdir}{\$PXF_HOME/gpextable} +-- +-- end_matchsubs +-- start_ignore +\c pxfautomation_extension +-- end_ignore + +SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; + +\dx+ pxf_fdw diff --git a/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_3_after_downgrade_2_0/expected/query01.ans b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_3_after_downgrade_2_0/expected/query01.ans new file mode 100644 index 000000000..f2d9f0e0e --- /dev/null +++ b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_3_after_downgrade_2_0/expected/query01.ans @@ -0,0 +1,55 @@ +-- start_ignore +-- end_ignore +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- @description query01 for PXF FDW 2.1 upgrade/downgrade test - step 3: after downgrade to 2.0 +-- start_matchsubs +-- +-- m{.*/usr/local/pxf-(dev|gp\d).*} +-- s{/usr/local/pxf-(dev|gp\d)}{\$PXF_HOME} +-- +-- m{.*\$libdir/pxf.*} +-- s{\$libdir}{\$PXF_HOME/gpextable} +-- +-- end_matchsubs +-- start_ignore +\c pxfautomation_extension +-- end_ignore + +SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; + extversion +------------ + 2.0 +(1 row) + +\dx+ pxf_fdw + Objects in extension "pxf_fdw" + Object Description +---------------------------------------- + foreign-data wrapper abfss_pxf_fdw + foreign-data wrapper file_pxf_fdw + foreign-data wrapper gs_pxf_fdw + foreign-data wrapper hbase_pxf_fdw + foreign-data wrapper hdfs_pxf_fdw + foreign-data wrapper hive_pxf_fdw + foreign-data wrapper jdbc_pxf_fdw + foreign-data wrapper s3_pxf_fdw + foreign-data wrapper wasbs_pxf_fdw + function pxf_fdw_handler() + function pxf_fdw_validator(text[],oid) +(11 rows) diff --git a/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_3_after_downgrade_2_0/sql/query01.sql b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_3_after_downgrade_2_0/sql/query01.sql new file mode 100644 index 000000000..5458329fb --- /dev/null +++ b/automation/sqlrepo/features/fdw_extension_tests_2_1/upgrade_downgrade/step_3_after_downgrade_2_0/sql/query01.sql @@ -0,0 +1,34 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- @description query01 for PXF FDW 2.1 upgrade/downgrade test - step 3: after downgrade to 2.0 +-- start_matchsubs +-- +-- m{.*/usr/local/pxf-(dev|gp\d).*} +-- s{/usr/local/pxf-(dev|gp\d)}{\$PXF_HOME} +-- +-- m{.*\$libdir/pxf.*} +-- s{\$libdir}{\$PXF_HOME/gpextable} +-- +-- end_matchsubs +-- start_ignore +\c pxfautomation_extension +-- end_ignore + +SELECT extversion FROM pg_extension WHERE extname = 'pxf_fdw'; + +\dx+ pxf_fdw diff --git a/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/extension/PxfFdwExtensionTest.java b/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/extension/PxfFdwExtensionTest.java index 27f075107..cc19d860d 100644 --- a/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/extension/PxfFdwExtensionTest.java +++ b/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/extension/PxfFdwExtensionTest.java @@ -52,7 +52,7 @@ protected void beforeMethod() throws Exception { @Test(groups = {"testcontainers", "pxf-fdw-extension"}) public void testPxfCreateExtension() throws Exception { - extensionDb.runQuery("CREATE EXTENSION pxf_fdw VERSION '2.0'"); + extensionDb.runQuery("CREATE EXTENSION pxf_fdw VERSION '2.1'"); regress.runSqlTest("features/fdw_extension_tests/create_extension"); } @@ -67,13 +67,13 @@ public void testPxfUpgrade() throws Exception { extensionDb.runQuery("CREATE EXTENSION pxf_fdw VERSION '1.0'"); regress.runSqlTest("features/fdw_extension_tests/upgrade/step_1_create_extension_with_older_pxf_version"); - extensionDb.runQuery("ALTER EXTENSION pxf_fdw UPDATE TO '2.0'"); + extensionDb.runQuery("ALTER EXTENSION pxf_fdw UPDATE TO '2.1'"); regress.runSqlTest("features/fdw_extension_tests/upgrade/step_2_after_alter_extension"); } @Test(groups = {"testcontainers", "pxf-fdw-extension"}) public void testPxfDowngradeThenUpgradeAgain() throws Exception { - extensionDb.runQuery("CREATE EXTENSION pxf_fdw VERSION '2.0'"); + extensionDb.runQuery("CREATE EXTENSION pxf_fdw VERSION '2.1'"); regress.runSqlTest("features/fdw_extension_tests/downgrade_then_upgrade/step_1_check_extension"); extensionDb.runQuery("ALTER EXTENSION pxf_fdw UPDATE TO '1.0'"); @@ -82,4 +82,22 @@ public void testPxfDowngradeThenUpgradeAgain() throws Exception { extensionDb.runQuery("ALTER EXTENSION pxf_fdw UPDATE TO '2.0'"); regress.runSqlTest("features/fdw_extension_tests/downgrade_then_upgrade/step_3_after_alter_extension_upgrade"); } + + @Test(groups = {"testcontainers", "pxf-fdw-extension"}) + public void testPxfCreateExtension21() throws Exception { + extensionDb.runQuery("CREATE EXTENSION pxf_fdw VERSION '2.1'"); + regress.runSqlTest("features/fdw_extension_tests_2_1/create_extension"); + } + + @Test(groups = {"testcontainers", "pxf-fdw-extension"}) + public void testPxfUpgradeDowngrade21() throws Exception { + extensionDb.runQuery("CREATE EXTENSION pxf_fdw VERSION '2.0'"); + regress.runSqlTest("features/fdw_extension_tests_2_1/upgrade_downgrade/step_1_create_2_0"); + + extensionDb.runQuery("ALTER EXTENSION pxf_fdw UPDATE TO '2.1'"); + regress.runSqlTest("features/fdw_extension_tests_2_1/upgrade_downgrade/step_2_after_upgrade_2_1"); + + extensionDb.runQuery("ALTER EXTENSION pxf_fdw UPDATE TO '2.0'"); + regress.runSqlTest("features/fdw_extension_tests_2_1/upgrade_downgrade/step_3_after_downgrade_2_0"); + } } diff --git a/fdw/Makefile b/fdw/Makefile index c195edbb6..ad6b06f1f 100644 --- a/fdw/Makefile +++ b/fdw/Makefile @@ -1,7 +1,7 @@ EXTENSION = pxf_fdw -DATA = pxf_fdw--2.0.sql pxf_fdw--1.0--2.0.sql pxf_fdw--2.0--1.0.sql pxf_fdw--1.0.sql +DATA = pxf_fdw--2.1.sql pxf_fdw--2.0--2.1.sql pxf_fdw--2.1--2.0.sql pxf_fdw--2.0.sql pxf_fdw--1.0--2.0.sql pxf_fdw--2.0--1.0.sql pxf_fdw--1.0.sql MODULE_big = pxf_fdw -OBJS = pxf_fdw.o pxf_bridge.o pxf_deparse.o pxf_filter.o pxf_header.o pxf_option.o libchurl.o +OBJS = pxf_fdw.o pxf_bridge.o pxf_deparse.o pxf_filter.o pxf_header.o pxf_option.o pxf_stat_activity.o pxf_cancel_activity.o libchurl.o REGRESS = pxf_fdw_wrapper pxf_fdw_server pxf_fdw_user_mapping pxf_fdw_foreign_table SHLIB_LINK += -lcurl diff --git a/fdw/pxf_cancel_activity.c b/fdw/pxf_cancel_activity.c new file mode 100644 index 000000000..eb28551c8 --- /dev/null +++ b/fdw/pxf_cancel_activity.c @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * pxf_cancel_activity.c + * + * Backs the pxf_cancel_backend and pxf_interrupt_backend SQL functions. Each is + * dispatched with EXECUTE ON ALL SEGMENTS: every segment asks its local PXF + * instance to terminate the in-flight requests of a given Greenplum session + * that originate from its own segment id (passed via X-GP-SEGMENT-ID). Because a + * single PXF instance serves every segment co-located on the host, this + * per-segment filter guarantees each running request is acted on exactly once, + * by its owning segment. + * + * Both functions are set-returning (one row per segment, carrying the PXF + * response verbatim as a JSON text value) because EXECUTE ON ALL SEGMENTS is + * only permitted for set-returning functions; the counts are summed by the SQL + * wrappers, keeping this layer schema-agnostic. + */ + +#include "libchurl.h" + +#include "fmgr.h" +#include "funcapi.h" +#include "lib/stringinfo.h" +#include "cdb/cdbvars.h" +#include "utils/builtins.h" + +/* PXF service defaults, mirror of pxf_stat_activity.c */ +#define PXF_CANCEL_DEFAULT_HOST "localhost" +#define PXF_CANCEL_DEFAULT_PORT "5888" +#define PXF_CANCEL_SERVICE_PREFIX "pxf" +#define PXF_CANCEL_READ_BUFFER_SIZE (64 * 1024) + +PG_FUNCTION_INFO_V1(pxf_cancel_backend_raw); +PG_FUNCTION_INFO_V1(pxf_interrupt_backend_raw); + +Datum pxf_cancel_backend_raw(PG_FUNCTION_ARGS); +Datum pxf_interrupt_backend_raw(PG_FUNCTION_ARGS); + +/* + * Returns the PXF service authority (host:port) for the local instance, + * honoring the PXF_HOST / PXF_PORT environment variables and falling back to + * localhost:5888, consistent with how segments reach their local PXF. + */ +static char * +get_pxf_cancel_authority(void) +{ + char *host = getenv("PXF_HOST"); + char *port = getenv("PXF_PORT"); + + return psprintf("%s:%s", + host ? host : PXF_CANCEL_DEFAULT_HOST, + port ? port : PXF_CANCEL_DEFAULT_PORT); +} + +/* + * Issues an HTTP GET to the given local PXF endpoint, scoped to this segment and + * the target Greenplum session, and returns the raw JSON response body as a + * palloc'd text value in the current memory context. + */ +static text * +fetch_backend_control_body(const char *endpoint, int32 session_id) +{ + CHURL_HEADERS headers; + CHURL_HANDLE handle; + StringInfoData uri; + StringInfoData response; + char readbuf[PXF_CANCEL_READ_BUFFER_SIZE]; + char segment_id[32]; + char session_buf[32]; + size_t n; + text *result; + + /* build the request URI for the local PXF instance */ + initStringInfo(&uri); + appendStringInfo(&uri, "http://%s/%s/%s", + get_pxf_cancel_authority(), PXF_CANCEL_SERVICE_PREFIX, endpoint); + + /* scope the request to this segment so co-located segments don't act twice */ + snprintf(segment_id, sizeof(segment_id), "%d", GpIdentity.segindex); + snprintf(session_buf, sizeof(session_buf), "%d", session_id); + + headers = churl_headers_init(); + churl_headers_append(headers, "X-GP-SEGMENT-ID", segment_id); + churl_headers_append(headers, "X-GP-SESSION-ID", session_buf); + churl_headers_append(headers, "Accept", "application/json"); + + elog(DEBUG2, "%s: segment %d requesting %s for session %d", + endpoint, GpIdentity.segindex, uri.data, session_id); + + handle = churl_init_download(uri.data, headers); + + /* read the full JSON body */ + initStringInfo(&response); + while ((n = churl_read(handle, readbuf, sizeof(readbuf))) != 0) + appendBinaryStringInfo(&response, readbuf, n); + + /* surface any error reported by the PXF service on the closed connection */ + churl_read_check_connectivity(handle); + + churl_cleanup(handle, false); + churl_headers_cleanup(headers); + + result = cstring_to_text(response.data); + + pfree(uri.data); + pfree(response.data); + + return result; +} + +/* + * Shared set-returning body: emit a single row carrying the raw JSON response + * from the local PXF instance for the given endpoint and session id. Dispatched + * with EXECUTE ON ALL SEGMENTS (which Cloudberry only permits for set-returning + * functions); the pxf_cancel_backend / pxf_interrupt_backend SQL wrappers sum + * the per-segment counts. + */ +static Datum +backend_control_srf(FunctionCallInfo fcinfo, const char *endpoint) +{ + FuncCallContext *funcctx; + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + int32 session_id = PG_GETARG_INT32(0); + + funcctx = SRF_FIRSTCALL_INIT(); + + /* the fetched body must outlive this call, so build it in the + * multi-call context and hand it back one row at a time */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + funcctx->user_fctx = fetch_backend_control_body(endpoint, session_id); + funcctx->max_calls = 1; + + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + + if (funcctx->call_cntr < funcctx->max_calls) + SRF_RETURN_NEXT(funcctx, PointerGetDatum((text *) funcctx->user_fctx)); + + SRF_RETURN_DONE(funcctx); +} + +/* + * pxf_cancel_backend_raw(session_id int) + * + * Asks the local PXF instance to gracefully cancel the in-flight requests of the + * given session (by ending their current bridge). Returns the raw JSON body, + * e.g. {"cancelled":N}. + */ +Datum +pxf_cancel_backend_raw(PG_FUNCTION_ARGS) +{ + return backend_control_srf(fcinfo, "cancel_backend"); +} + +/* + * pxf_interrupt_backend_raw(session_id int) + * + * Asks the local PXF instance to interrupt the worker thread(s) of the in-flight + * requests of the given session. Returns the raw JSON body, e.g. + * {"interrupted":N}. + */ +Datum +pxf_interrupt_backend_raw(PG_FUNCTION_ARGS) +{ + return backend_control_srf(fcinfo, "interrupt_backend"); +} diff --git a/fdw/pxf_fdw--2.0--2.1.sql b/fdw/pxf_fdw--2.0--2.1.sql new file mode 100644 index 000000000..aee9c9fa0 --- /dev/null +++ b/fdw/pxf_fdw--2.0--2.1.sql @@ -0,0 +1,87 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +/* fdw/pxf_fdw--2.0--2.1.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pxf_fdw UPDATE TO '2.1'" to load this file. \quit + +-- Raw per-segment accessor: each segment asks its local PXF instance for the +-- activity that originates from its own segment id and returns the JSON body +-- verbatim as a single row. Dispatched to every segment; the typed columns are +-- produced by the pxf_stat_activity view below. The function is set-returning +-- (one row per segment) because EXECUTE ON ALL SEGMENTS is only permitted for +-- set-returning functions. +CREATE FUNCTION pxf_stat_activity_raw() RETURNS SETOF text +AS 'MODULE_PATHNAME', 'pxf_stat_activity_raw' +LANGUAGE C VOLATILE EXECUTE ON ALL SEGMENTS; + +-- pg_stat_activity-like view of the queries currently running inside PXF, +-- aggregated across all segment hosts. DISTINCT is a safety net; PXF already +-- de-duplicates by filtering each response to the requesting segment id. +CREATE VIEW pxf_stat_activity AS +SELECT DISTINCT + (a->>'segmentId')::int AS segment_id, + (a->>'gpSessionId')::int AS session_id, + (a->>'gpCommandCount')::int AS command_count, + a->>'transactionId' AS xid, + a->>'requestType' AS operation, + a->>'user' AS usename, + a->>'serverName' AS server, + a->>'profile' AS profile, + a->>'schemaName' AS schema_name, + a->>'tableName' AS table_name, + a->>'dataSource' AS data_source, + to_timestamp((a->>'startTimeMs')::bigint / 1000.0) AS query_start, + a->>'host' AS pxf_host +FROM ( + SELECT json_array_elements(raw::json -> 'activities') AS a + FROM pxf_stat_activity_raw() AS raw +) s; + +-- Per-segment cancellation primitives backing pxf_cancel_backend / +-- pxf_interrupt_backend. Each segment asks its local PXF instance to terminate +-- the in-flight requests of the given Cloudberry session that originate from its +-- own segment id, returning the JSON body verbatim as a single row (e.g. +-- {"cancelled":N} / {"interrupted":N}). Dispatched to every segment; the counts +-- are summed by the SQL wrappers below. Set-returning because EXECUTE ON ALL +-- SEGMENTS is only permitted for set-returning functions. +CREATE FUNCTION pxf_cancel_backend_raw(session_id int) RETURNS SETOF text +AS 'MODULE_PATHNAME', 'pxf_cancel_backend_raw' +LANGUAGE C VOLATILE STRICT EXECUTE ON ALL SEGMENTS; + +CREATE FUNCTION pxf_interrupt_backend_raw(session_id int) RETURNS SETOF text +AS 'MODULE_PATHNAME', 'pxf_interrupt_backend_raw' +LANGUAGE C VOLATILE STRICT EXECUTE ON ALL SEGMENTS; + +-- Gracefully cancels the in-flight PXF requests of a Cloudberry session across +-- the whole cluster by ending their current bridge. Returns the number of +-- requests that were signalled. Analogous to pg_cancel_backend, but keyed by +-- the Cloudberry session id (as reported in pxf_stat_activity.session_id). +CREATE FUNCTION pxf_cancel_backend(session_id int) RETURNS int AS $$ + SELECT coalesce(sum((raw::json ->> 'cancelled')::int), 0)::int + FROM pxf_cancel_backend_raw(session_id) AS raw +$$ LANGUAGE sql VOLATILE; + +-- Interrupts the worker thread(s) of the in-flight PXF requests of a Cloudberry +-- session across the whole cluster. Returns the number of requests that were +-- interrupted. A forceful complement to pxf_cancel_backend for requests that do +-- not observe cancellation (e.g. blocked in a non-interruptible read). +CREATE FUNCTION pxf_interrupt_backend(session_id int) RETURNS int AS $$ + SELECT coalesce(sum((raw::json ->> 'interrupted')::int), 0)::int + FROM pxf_interrupt_backend_raw(session_id) AS raw +$$ LANGUAGE sql VOLATILE; diff --git a/fdw/pxf_fdw--2.1--2.0.sql b/fdw/pxf_fdw--2.1--2.0.sql new file mode 100644 index 000000000..a4e2c2b5b --- /dev/null +++ b/fdw/pxf_fdw--2.1--2.0.sql @@ -0,0 +1,28 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +/* fdw/pxf_fdw--2.1--2.0.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pxf_fdw UPDATE TO '2.0'" to load this file. \quit + +DROP FUNCTION pxf_interrupt_backend(int); +DROP FUNCTION pxf_cancel_backend(int); +DROP FUNCTION pxf_interrupt_backend_raw(int); +DROP FUNCTION pxf_cancel_backend_raw(int); +DROP VIEW pxf_stat_activity; +DROP FUNCTION pxf_stat_activity_raw(); diff --git a/fdw/pxf_fdw--2.1.sql b/fdw/pxf_fdw--2.1.sql new file mode 100644 index 000000000..496d39d2d --- /dev/null +++ b/fdw/pxf_fdw--2.1.sql @@ -0,0 +1,142 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +/* fdw/pxf_fdw--2.1.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION pxf_fdw" to load this file. \quit + +CREATE FUNCTION pxf_fdw_handler() +RETURNS fdw_handler +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +CREATE FUNCTION pxf_fdw_validator(text[], oid) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +CREATE FOREIGN DATA WRAPPER jdbc_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 'jdbc', mpp_execute 'all segments' ); + +CREATE FOREIGN DATA WRAPPER hdfs_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 'hdfs', mpp_execute 'all segments' ); + +CREATE FOREIGN DATA WRAPPER hive_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 'hive', mpp_execute 'all segments' ); + +CREATE FOREIGN DATA WRAPPER hbase_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 'hbase', mpp_execute 'all segments' ); + +CREATE FOREIGN DATA WRAPPER s3_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 's3', mpp_execute 'all segments' ); + +CREATE FOREIGN DATA WRAPPER gs_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 'gs', mpp_execute 'all segments' ); + +CREATE FOREIGN DATA WRAPPER abfss_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 'abfss', mpp_execute 'all segments' ); + +CREATE FOREIGN DATA WRAPPER wasbs_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 'wasbs', mpp_execute 'all segments' ); + +CREATE FOREIGN DATA WRAPPER file_pxf_fdw + HANDLER pxf_fdw_handler + VALIDATOR pxf_fdw_validator + OPTIONS ( protocol 'file', mpp_execute 'all segments' ); + +-- Raw per-segment accessor: each segment asks its local PXF instance for the +-- activity that originates from its own segment id and returns the JSON body +-- verbatim as a single row. Dispatched to every segment; the typed columns are +-- produced by the pxf_stat_activity view below. The function is set-returning +-- (one row per segment) because EXECUTE ON ALL SEGMENTS is only permitted for +-- set-returning functions. +CREATE FUNCTION pxf_stat_activity_raw() RETURNS SETOF text +AS 'MODULE_PATHNAME', 'pxf_stat_activity_raw' +LANGUAGE C VOLATILE EXECUTE ON ALL SEGMENTS; + +-- pg_stat_activity-like view of the queries currently running inside PXF, +-- aggregated across all segment hosts. DISTINCT is a safety net; PXF already +-- de-duplicates by filtering each response to the requesting segment id. +CREATE VIEW pxf_stat_activity AS +SELECT DISTINCT + (a->>'segmentId')::int AS segment_id, + (a->>'gpSessionId')::int AS session_id, + (a->>'gpCommandCount')::int AS command_count, + a->>'transactionId' AS xid, + a->>'requestType' AS operation, + a->>'user' AS usename, + a->>'serverName' AS server, + a->>'profile' AS profile, + a->>'schemaName' AS schema_name, + a->>'tableName' AS table_name, + a->>'dataSource' AS data_source, + to_timestamp((a->>'startTimeMs')::bigint / 1000.0) AS query_start, + a->>'host' AS pxf_host +FROM ( + SELECT json_array_elements(raw::json -> 'activities') AS a + FROM pxf_stat_activity_raw() AS raw +) s; + +-- Per-segment cancellation primitives backing pxf_cancel_backend / +-- pxf_interrupt_backend. Each segment asks its local PXF instance to terminate +-- the in-flight requests of the given Cloudberry session that originate from its +-- own segment id, returning the JSON body verbatim as a single row (e.g. +-- {"cancelled":N} / {"interrupted":N}). Dispatched to every segment; the counts +-- are summed by the SQL wrappers below. Set-returning because EXECUTE ON ALL +-- SEGMENTS is only permitted for set-returning functions. +CREATE FUNCTION pxf_cancel_backend_raw(session_id int) RETURNS SETOF text +AS 'MODULE_PATHNAME', 'pxf_cancel_backend_raw' +LANGUAGE C VOLATILE STRICT EXECUTE ON ALL SEGMENTS; + +CREATE FUNCTION pxf_interrupt_backend_raw(session_id int) RETURNS SETOF text +AS 'MODULE_PATHNAME', 'pxf_interrupt_backend_raw' +LANGUAGE C VOLATILE STRICT EXECUTE ON ALL SEGMENTS; + +-- Gracefully cancels the in-flight PXF requests of a Cloudberry session across +-- the whole cluster by ending their current bridge. Returns the number of +-- requests that were signalled. Analogous to pg_cancel_backend, but keyed by +-- the Cloudberry session id (as reported in pxf_stat_activity.session_id). +CREATE FUNCTION pxf_cancel_backend(session_id int) RETURNS int AS $$ + SELECT coalesce(sum((raw::json ->> 'cancelled')::int), 0)::int + FROM pxf_cancel_backend_raw(session_id) AS raw +$$ LANGUAGE sql VOLATILE; + +-- Interrupts the worker thread(s) of the in-flight PXF requests of a Cloudberry +-- session across the whole cluster. Returns the number of requests that were +-- interrupted. A forceful complement to pxf_cancel_backend for requests that do +-- not observe cancellation (e.g. blocked in a non-interruptible read). +CREATE FUNCTION pxf_interrupt_backend(session_id int) RETURNS int AS $$ + SELECT coalesce(sum((raw::json ->> 'interrupted')::int), 0)::int + FROM pxf_interrupt_backend_raw(session_id) AS raw +$$ LANGUAGE sql VOLATILE; diff --git a/fdw/pxf_fdw.control b/fdw/pxf_fdw.control index 9df860a49..4eeee3437 100644 --- a/fdw/pxf_fdw.control +++ b/fdw/pxf_fdw.control @@ -1,6 +1,6 @@ # pxf_fdw extension directory = 'extension' comment = 'PXF Foreign Data Wrapper for Apache Cloudberry' -default_version = '2.0' +default_version = '2.1' module_pathname = '$libdir/pxf_fdw' relocatable = true diff --git a/fdw/pxf_stat_activity.c b/fdw/pxf_stat_activity.c new file mode 100644 index 000000000..3b21b0774 --- /dev/null +++ b/fdw/pxf_stat_activity.c @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * pxf_stat_activity.c + * + * Backs the pxf_stat_activity SQL view. The function pxf_stat_activity_raw() + * is dispatched with EXECUTE ON ALL SEGMENTS: each segment asks its local PXF + * instance for the requests that originate from its own segment id (passed via + * the X-GP-SEGMENT-ID header). Because a single PXF instance serves every + * segment co-located on the host, this per-segment filter guarantees each + * running query is reported exactly once, by its owning segment. + * + * The function is set-returning (one row per segment, carrying the PXF response + * verbatim as a JSON text value) because EXECUTE ON ALL SEGMENTS is only + * permitted for set-returning functions; the typed columns are produced by the + * SQL view, keeping this layer schema-agnostic. + */ + +#include "libchurl.h" + +#include "fmgr.h" +#include "funcapi.h" +#include "lib/stringinfo.h" +#include "cdb/cdbvars.h" +#include "utils/builtins.h" + +/* PXF service defaults, mirror of pxf_option.h / external-table pxfutils.h */ +#define PXF_STAT_DEFAULT_HOST "localhost" +#define PXF_STAT_DEFAULT_PORT "5888" +#define PXF_STAT_SERVICE_PREFIX "pxf" +#define PXF_STAT_READ_BUFFER_SIZE (64 * 1024) + +PG_FUNCTION_INFO_V1(pxf_stat_activity_raw); + +Datum pxf_stat_activity_raw(PG_FUNCTION_ARGS); + +/* + * Returns the PXF service authority (host:port) for the local instance, + * honoring the PXF_HOST / PXF_PORT environment variables and falling back to + * localhost:5888, consistent with how segments reach their local PXF. + */ +static char * +get_pxf_stat_authority(void) +{ + char *host = getenv("PXF_HOST"); + char *port = getenv("PXF_PORT"); + + return psprintf("%s:%s", + host ? host : PXF_STAT_DEFAULT_HOST, + port ? port : PXF_STAT_DEFAULT_PORT); +} + +/* + * Issues an HTTP GET to the local PXF /pxf/stat_activity endpoint, scoped to + * this segment, and returns the raw JSON response body as a palloc'd text value + * in the current memory context. + */ +static text * +fetch_stat_activity_body(void) +{ + CHURL_HEADERS headers; + CHURL_HANDLE handle; + StringInfoData uri; + StringInfoData response; + char readbuf[PXF_STAT_READ_BUFFER_SIZE]; + char segment_id[32]; + size_t n; + text *result; + + /* build the request URI for the local PXF instance */ + initStringInfo(&uri); + appendStringInfo(&uri, "http://%s/%s/stat_activity", + get_pxf_stat_authority(), PXF_STAT_SERVICE_PREFIX); + + /* scope the request to this segment so co-located segments don't duplicate */ + snprintf(segment_id, sizeof(segment_id), "%d", GpIdentity.segindex); + + headers = churl_headers_init(); + churl_headers_append(headers, "X-GP-SEGMENT-ID", segment_id); + churl_headers_append(headers, "Accept", "application/json"); + + elog(DEBUG2, "pxf_stat_activity: segment %d requesting %s", + GpIdentity.segindex, uri.data); + + handle = churl_init_download(uri.data, headers); + + /* read the full JSON body */ + initStringInfo(&response); + while ((n = churl_read(handle, readbuf, sizeof(readbuf))) != 0) + appendBinaryStringInfo(&response, readbuf, n); + + /* surface any error reported by the PXF service on the closed connection */ + churl_read_check_connectivity(handle); + + churl_cleanup(handle, false); + churl_headers_cleanup(headers); + + result = cstring_to_text(response.data); + + pfree(uri.data); + pfree(response.data); + + return result; +} + +/* + * pxf_stat_activity_raw + * + * Set-returning function dispatched with EXECUTE ON ALL SEGMENTS (which + * Cloudberry only permits for set-returning functions). Each segment emits a + * single row: the raw JSON response body from its local PXF instance, scoped to + * that segment. The pxf_stat_activity view unions these rows and expands them + * into typed columns. + */ +Datum +pxf_stat_activity_raw(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + + funcctx = SRF_FIRSTCALL_INIT(); + + /* the fetched body must outlive this call, so build it in the + * multi-call context and hand it back one row at a time */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + funcctx->user_fctx = fetch_stat_activity_body(); + funcctx->max_calls = 1; + + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + + if (funcctx->call_cntr < funcctx->max_calls) + SRF_RETURN_NEXT(funcctx, PointerGetDatum((text *) funcctx->user_fctx)); + + SRF_RETURN_DONE(funcctx); +} diff --git a/server/pxf-service/src/main/java/org/apache/cloudberry/pxf/service/activity/ActiveRequest.java b/server/pxf-service/src/main/java/org/apache/cloudberry/pxf/service/activity/ActiveRequest.java new file mode 100644 index 000000000..c1d0bf40f --- /dev/null +++ b/server/pxf-service/src/main/java/org/apache/cloudberry/pxf/service/activity/ActiveRequest.java @@ -0,0 +1,167 @@ +package org.apache.cloudberry.pxf.service.activity; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import lombok.extern.slf4j.Slf4j; +import org.apache.cloudberry.pxf.service.bridge.Bridge; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * A single in-flight PXF request tracked by {@link ActiveRequestRegistry}. + *
+ * In addition to the immutable {@link ActiveRequestInfo} snapshot (which defines + * the `pxf_stat_activity` JSON wire contract) it holds the two live + * handles used to terminate the request from another thread: + * - the worker `thread` that is processing it (for `pxf_interrupt_backend`) + * - the current `bridge` (for `pxf_cancel_backend`) + */ +@Slf4j +class ActiveRequest { + + /* + * Concurrency: PXF processes a request synchronously on a servlet + * thread that is returned to a pool afterwards, so a naive + * `thread.interrupt()` could land on an unrelated request the thread has + * since picked up. To prevent that, every mutation that races with request + * completion is serialized on `lock` and gated by the `finished` + * flag: + *
+ * The getters are serialized to JSON by Jackson.
+ */
+@Getter
+public class ActiveRequestInfo {
+
+ /** The Cloudberry segment id that originated the request. */
+ private final int segmentId;
+
+ /** The Cloudberry session id (aka ssid). */
+ private final int gpSessionId;
+
+ /** The Cloudberry command count within the session (aka ccnt). */
+ private final int gpCommandCount;
+
+ /** The Cloudberry transaction id (XID) of the originating query. */
+ private final String transactionId;
+
+ /** The kind of operation: READ_BRIDGE or WRITE_BRIDGE. */
+ private final String requestType;
+
+ /** The end-user identity that issued the request. */
+ private final String user;
+
+ /** The name of the PXF server configuration used by the request. */
+ private final String serverName;
+
+ /** The profile associated with the request (e.g. hdfs:text). */
+ private final String profile;
+
+ /** The originating Cloudberry schema name. */
+ private final String schemaName;
+
+ /** The originating Cloudberry table name. */
+ private final String tableName;
+
+ /** The data source (file path or external resource identifier). */
+ private final String dataSource;
+
+ /** Epoch milliseconds when the request started being processed. */
+ private final long startTimeMs;
+
+ /** The hostname of the PXF instance that is serving the request. */
+ private final String host;
+
+ /**
+ * Captures a snapshot of the relevant fields from the request context.
+ *
+ * @param context the request context of the in-flight request
+ * @param startTimeMs epoch milliseconds when processing started
+ * @param host hostname of the PXF instance serving the request
+ */
+ public ActiveRequestInfo(RequestContext context, long startTimeMs, String host) {
+ this.segmentId = context.getSegmentId();
+ this.gpSessionId = context.getGpSessionId();
+ this.gpCommandCount = context.getGpCommandCount();
+ this.transactionId = context.getTransactionId();
+ this.requestType = context.getRequestType() == null ? null : context.getRequestType().name();
+ this.user = context.getUser();
+ this.serverName = context.getServerName();
+ this.profile = context.getProfile();
+ this.schemaName = context.getSchemaName();
+ this.tableName = context.getTableName();
+ this.dataSource = context.getDataSource();
+ this.startTimeMs = startTimeMs;
+ this.host = host;
+ }
+}
diff --git a/server/pxf-service/src/main/java/org/apache/cloudberry/pxf/service/activity/ActiveRequestRegistry.java b/server/pxf-service/src/main/java/org/apache/cloudberry/pxf/service/activity/ActiveRequestRegistry.java
new file mode 100644
index 000000000..22f7d2f72
--- /dev/null
+++ b/server/pxf-service/src/main/java/org/apache/cloudberry/pxf/service/activity/ActiveRequestRegistry.java
@@ -0,0 +1,191 @@
+package org.apache.cloudberry.pxf.service.activity;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.cloudberry.pxf.api.model.RequestContext;
+import org.apache.cloudberry.pxf.service.bridge.Bridge;
+import org.springframework.stereotype.Component;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Registry of the requests that are currently being processed by this PXF instance.
+ * A request is registered when it starts being processed by
+ * `BaseServiceImpl.processData()` and unregistered when it ends.
+ */
+@Component
+@Slf4j
+public class ActiveRequestRegistry {
+
+ /** Sentinel meaning "all segments" for the segment-id filter. */
+ public static final int ALL_SEGMENTS = -1;
+
+ /** All in-flight requests on this instance. */
+ private final Set