diff --git a/web/pgacloud/providers/azure.py b/web/pgacloud/providers/azure.py index 8539a35c5..ba1a109e6 100644 --- a/web/pgacloud/providers/azure.py +++ b/web/pgacloud/providers/azure.py @@ -162,13 +162,17 @@ class AzureProvider(AbsProvider): _credential = InteractiveBrowserCredential( tenant_id=self._tenant_id, timeout=180, - cache_persistence_options=TokenCachePersistenceOptions(), + cache_persistence_options=TokenCachePersistenceOptions( + allow_unencrypted_storage=True + ), authentication_record=deserialized_auth_record) else: _credential = InteractiveBrowserCredential( tenant_id=self._tenant_id, timeout=180, - cache_persistence_options=TokenCachePersistenceOptions()) + cache_persistence_options=TokenCachePersistenceOptions( + allow_unencrypted_storage=True) + ) return _credential def _get_azure_client(self, type): diff --git a/web/pgadmin/feature_tests/file_manager_test.py b/web/pgadmin/feature_tests/file_manager_test.py index 63eaa699d..cdefeed56 100644 --- a/web/pgadmin/feature_tests/file_manager_test.py +++ b/web/pgadmin/feature_tests/file_manager_test.py @@ -100,8 +100,8 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): self.wait.until(EC.visibility_of_element_located( (By.CSS_SELECTOR, QueryToolLocators.select_file_content_css))) - table = self.page.driver.find_element_by_css_selector( - QueryToolLocators.select_file_content_css) + table = self.page.driver.find_element( + By.CSS_SELECTOR, QueryToolLocators.select_file_content_css) retry_count = 0 while retry_count < 5: diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py index 0cb2c1c0d..0047ed1f7 100644 --- a/web/pgadmin/feature_tests/query_tool_journey_test.py +++ b/web/pgadmin/feature_tests/query_tool_journey_test.py @@ -18,6 +18,7 @@ from regression.python_test_utils import test_utils from regression.feature_utils.base_feature_test import BaseFeatureTest from regression.feature_utils.locators import QueryToolLocators import time +from selenium.webdriver.support import expected_conditions as EC class QueryToolJourneyTest(BaseFeatureTest): @@ -98,7 +99,7 @@ class QueryToolJourneyTest(BaseFeatureTest): def _test_copies_rows(self): self.page.driver.switch_to.default_content() self.page.driver.switch_to.frame( - self.page.driver.find_element_by_tag_name("iframe")) + self.page.driver.find_element(By.TAG_NAME, "iframe")) # row index starts with 2 select_row = self.page.find_by_css_selector( @@ -112,7 +113,7 @@ class QueryToolJourneyTest(BaseFeatureTest): self.page.driver.switch_to.default_content() self.page.driver.switch_to.frame( - self.page.driver.find_element_by_tag_name("iframe")) + self.page.driver.find_element(By.TAG_NAME, "iframe")) scratch_pad_ele = self.page.find_by_css_selector( QueryToolLocators.scratch_pad_css) @@ -127,7 +128,7 @@ class QueryToolJourneyTest(BaseFeatureTest): def _test_copies_columns(self): self.page.driver.switch_to.default_content() self.page.driver.switch_to.frame( - self.page.driver.find_element_by_tag_name("iframe")) + self.page.driver.find_element(By.TAG_NAME, "iframe")) column_header = self.page.find_by_css_selector( QueryToolLocators.output_column_header_css.format('some_column')) @@ -151,7 +152,7 @@ class QueryToolJourneyTest(BaseFeatureTest): def _test_history_tab(self): self.page.driver.switch_to.default_content() self.page.driver.switch_to.frame( - self.page.driver.find_element_by_tag_name("iframe")) + self.page.driver.find_element(By.TAG_NAME, "iframe")) self.page.clear_query_tool() editor_input = self.page.find_by_css_selector( @@ -400,8 +401,11 @@ class QueryToolJourneyTest(BaseFeatureTest): ActionChains(self.driver).double_click(cell_el).perform() ActionChains(self.driver).send_keys(value). \ send_keys(Keys.ENTER).perform() - self.page.find_by_css_selector( - QueryToolLocators.btn_save_data).click() + + save_btn = WebDriverWait(self.driver, 5).until( + EC.element_to_be_clickable( + (By.CSS_SELECTOR, QueryToolLocators.btn_save_data))) + save_btn.click() def _insert_data_into_test_editable_table(self): self.page.click_tab(self.query_editor_tab_id, rc_dock=True) @@ -472,7 +476,7 @@ class QueryToolJourneyTest(BaseFeatureTest): ActionChains(self.driver).double_click(cell_el).perform() ActionChains(self.driver).send_keys(new_value). \ send_keys(Keys.ENTER).perform() - + time.sleep(0.5) # Check if the value was updated # Finding element again to avoid stale element reference exception cell_el = self.page.\ diff --git a/web/pgadmin/feature_tests/query_tool_tests.py b/web/pgadmin/feature_tests/query_tool_tests.py index 6c0c305a4..c8f9a3c0a 100644 --- a/web/pgadmin/feature_tests/query_tool_tests.py +++ b/web/pgadmin/feature_tests/query_tool_tests.py @@ -41,10 +41,10 @@ class QueryToolFeatureTest(BaseFeatureTest): self.test_db) self.page.open_query_tool() self.page.wait_for_spinner_to_disappear() - self._reset_options() self.wait = WebDriverWait(self.page.driver, 10) def runTest(self): + self._reset_options() skip_warning = "Skipped." # on demand result set on scrolling. print("\nOn demand query result... ", @@ -260,10 +260,11 @@ SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format( SELECT generate_series(1, 1000) as id order by id desc""" self.page.fill_codemirror_area_with(query) - - self.page.retry_click( + time.sleep(0.5) + explain_op_btn_click = self.page.retry_click( (By.CSS_SELECTOR, QueryToolLocators.btn_explain_options_dropdown), (By.CSS_SELECTOR, QueryToolLocators.btn_explain_verbose)) + self.assertTrue(explain_op_btn_click, 'Explain Op button click failed') # disable Explain options and auto rollback only if they are enabled. for op in (QueryToolLocators.btn_explain_verbose, @@ -297,9 +298,10 @@ SELECT generate_series(1, 1000) as id order by id desc""" self.page.fill_codemirror_area_with(query) - self.page.retry_click( + explain_op_btn_click = self.page.retry_click( (By.CSS_SELECTOR, QueryToolLocators.btn_explain_options_dropdown), (By.CSS_SELECTOR, QueryToolLocators.btn_explain_verbose)) + self.assertTrue(explain_op_btn_click, 'Explain Op button click failed') # disable Explain options and auto rollback only if they are enabled. for op in (QueryToolLocators.btn_explain_buffers, diff --git a/web/pgadmin/feature_tests/test_copy_sql_to_query_tool.py b/web/pgadmin/feature_tests/test_copy_sql_to_query_tool.py index 83bf0e93f..77560af4d 100644 --- a/web/pgadmin/feature_tests/test_copy_sql_to_query_tool.py +++ b/web/pgadmin/feature_tests/test_copy_sql_to_query_tool.py @@ -128,7 +128,6 @@ class CopySQLFeatureTest(BaseFeatureTest): sql_editor = self.page.find_by_xpath( NavMenuLocators.specified_preference_tree_node.format( specified_preference_tree_node_name)) - print(sql_editor) sql_editor.click() if self.page.find_by_xpath( NavMenuLocators.specified_pref_node_exp_status. @@ -139,7 +138,6 @@ class CopySQLFeatureTest(BaseFeatureTest): option_node = self.page.find_by_xpath( "//*[@id='treeContainer']//div//span[text()=" "'Results grid']//preceding::span[text()='Options']") - print(option_node) # self.page.check_if_element_exists_with_scroll(option_node) self.page.driver.execute_script("arguments[0].scrollIntoView(false)", option_node) diff --git a/web/pgadmin/feature_tests/view_data_dml_queries.py b/web/pgadmin/feature_tests/view_data_dml_queries.py index 554e376e8..7c9b54f76 100644 --- a/web/pgadmin/feature_tests/view_data_dml_queries.py +++ b/web/pgadmin/feature_tests/view_data_dml_queries.py @@ -231,8 +231,10 @@ CREATE TABLE public.nonintpkey ActionChains(self.driver).send_keys(value). \ send_keys(Keys.ENTER).perform() elif cell_type in ['text', 'text[]', 'boolean[]']: - text_area_ele = self.page.find_by_css_selector( - QueryToolLocators.row_editor_text_area_css) + text_area_ele = WebDriverWait(self.driver, 5).until( + EC.visibility_of_element_located( + (By.CSS_SELECTOR, + QueryToolLocators.row_editor_text_area_css))) text_area_ele.clear() text_area_ele.click() text_area_ele.send_keys(value) @@ -273,12 +275,12 @@ CREATE TABLE public.nonintpkey ActionChains(self.driver).click(checkbox_el).perform() def _view_data_grid(self, table_name): - self.page.driver.find_element_by_link_text("Object").click() + self.page.driver.find_element(By.LINK_TEXT, "Object").click() ActionChains( self.page.driver ).move_to_element( - self.page.driver.find_element_by_link_text( - NavMenuLocators.view_data_link_text) + self.page.driver.find_element( + By.LINK_TEXT, NavMenuLocators.view_data_link_text) ).perform() self.page.find_by_partial_link_text("All Rows").click() @@ -327,7 +329,7 @@ CREATE TABLE public.nonintpkey # rowindex starts with 2 and 1st colindex is rownum cell_xpath = CheckForViewDataTest\ ._get_cell_xpath(str(idx + 1), row + 1) - time.sleep(0.2) + time.sleep(0.5) self._update_cell(cell_xpath, data[str(idx)]) self.page.find_by_css_selector( QueryToolLocators.btn_save_data).click() diff --git a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py index 818bdf3b4..23975ac94 100644 --- a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py +++ b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py @@ -66,13 +66,13 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest): function_node.click() def _debug_function(self): - self.page.driver.find_element_by_link_text("Object").click() + self.page.driver.find_element(By.LINK_TEXT, "Object").click() ActionChains( self.page.driver ).move_to_element( - self.page.driver.find_element_by_link_text("Debugging") + self.page.driver.find_element(By.LINK_TEXT, "Debugging") ).perform() - self.page.driver.find_element_by_link_text("Debug").click() + self.page.driver.find_element(By.LINK_TEXT, "Debug").click() # We need to check if debugger plugin is installed or not try: @@ -103,14 +103,14 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest): ) else: self.page.driver.switch_to.frame( - self.page.driver.find_element_by_tag_name('iframe') + self.page.driver.find_element(By.TAG_NAME, 'iframe') ) wait.until(EC.presence_of_element_located( (By.XPATH, "//span[contains(.,'Hello, pgAdmin4')]")) ) self.page.click_element( - self.page.driver.find_elements_by_xpath("//button")[2] + self.page.driver.find_elements(By.XPATH, "//button")[2] ) wait.until(EC.presence_of_element_located( diff --git a/web/pgadmin/feature_tests/xss_checks_roles_control_test.py b/web/pgadmin/feature_tests/xss_checks_roles_control_test.py index 4a7fefdf4..948062966 100644 --- a/web/pgadmin/feature_tests/xss_checks_roles_control_test.py +++ b/web/pgadmin/feature_tests/xss_checks_roles_control_test.py @@ -69,8 +69,8 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest): role_node.click() def _check_role_membership_control(self): - self.page.driver.find_element_by_link_text( - NavMenuLocators.object_menu_link_text).click() + self.page.driver.find_element( + By.LINK_TEXT, NavMenuLocators.object_menu_link_text).click() property_object = self.wait.until(EC.visibility_of_element_located( (By.CSS_SELECTOR, NavMenuLocators.properties_obj_css))) property_object.click() diff --git a/web/pgadmin/misc/cloud/azure/__init__.py b/web/pgadmin/misc/cloud/azure/__init__.py index 525b6a100..693caa48c 100644 --- a/web/pgadmin/misc/cloud/azure/__init__.py +++ b/web/pgadmin/misc/cloud/azure/__init__.py @@ -288,13 +288,17 @@ class Azure: _credential = InteractiveBrowserCredential( tenant_id=self._tenant_id, timeout=180, - cache_persistence_options=TokenCachePersistenceOptions(), + cache_persistence_options=TokenCachePersistenceOptions( + allow_unencrypted_storage=True + ), authentication_record=deserialized_auth_record) else: _credential = InteractiveBrowserCredential( tenant_id=self._tenant_id, timeout=180, - cache_persistence_options=TokenCachePersistenceOptions()) + cache_persistence_options=TokenCachePersistenceOptions( + allow_unencrypted_storage=True) + ) return _credential def _get_azure_client(self, type): diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py index 139af3c86..1b0a0873f 100644 --- a/web/regression/feature_utils/pgadmin_page.py +++ b/web/regression/feature_utils/pgadmin_page.py @@ -42,12 +42,12 @@ class PgadminPage: self.driver.switch_to.default_content() if not (self.check_if_element_exist_by_xpath( '//a[@id="navbar-user"]', 1)): - user_edt_box_el = self.driver.find_element_by_name('email') + user_edt_box_el = self.driver.find_element(By.NAME, 'email') user_edt_box_el.send_keys(user_detail['login_username']) - password_edt_box_el = self.driver.find_element_by_name('password') + password_edt_box_el = self.driver.find_element(By.NAME, 'password') password_edt_box_el.send_keys(user_detail['login_password']) - submit_btn = self.driver.find_element_by_xpath( - '//button[@value="Login"]') + submit_btn = self.driver.find_element( + By.XPATH, '//button[@value="Login"]') submit_btn.click() self.wait_for_spinner_to_disappear() @@ -155,11 +155,11 @@ class PgadminPage: self.driver, "//div[@id='btn-conn-status']", 5) def open_view_data(self, table_name): - self.driver.find_element_by_link_text("Object").click() + self.driver.find_element(By.LINK_TEXT, "Object").click() ActionChains( self.driver ).move_to_element( - self.driver.find_element_by_link_text("View/Edit Data") + self.driver.find_element(By.LINK_TEXT, "View/Edit Data") ).perform() self.find_by_partial_link_text("All Rows").click() time.sleep(1) @@ -173,7 +173,7 @@ class PgadminPage: ), "Timed out waiting for div element to appear" ) self.driver.switch_to.frame( - self.driver.find_element_by_tag_name('iframe') + self.driver.find_element(By.TAG_NAME, 'iframe') ) def enable_menu_item(self, menu_item, wait_time): @@ -243,15 +243,15 @@ class PgadminPage: def check_execute_option(self, option): """"This function will check auto commit or auto roll back based on user input. If button is already checked, no action will be taken""" - menu_btn = self.driver.find_element_by_css_selector( - QueryToolLocators.btn_query_dropdown) + menu_btn = self.driver.find_element( + By.CSS_SELECTOR, QueryToolLocators.btn_query_dropdown) if menu_btn.get_attribute('data-state') == "closed": menu_btn.click() def update_execute_option_setting(css_selector_of_option): retry = 3 - menu_option = self.driver.find_element_by_css_selector( - css_selector_of_option) + menu_option = self.driver.find_element(By.CSS_SELECTOR, + css_selector_of_option) if menu_option.get_attribute('data-checked') == 'false': while retry > 0: try: @@ -277,17 +277,18 @@ class PgadminPage: def uncheck_execute_option(self, option): """"This function will uncheck auto commit or auto roll back based on user input. If button is already unchecked, no action will be taken""" - menu = self.driver.find_element_by_css_selector( + menu = self.driver.find_element( + By.CSS_SELECTOR, QueryToolLocators.query_tool_menu.format('Execute Options Menu')) if menu.get_attribute('data-state') == "closed": - self.driver.find_element_by_css_selector( - QueryToolLocators.btn_query_dropdown).click() + self.driver.find_element( + By.CSS_SELECTOR, QueryToolLocators.btn_query_dropdown).click() def update_execute_option_setting(css_selector_of_option): retry = 3 - menu_option = self.driver.find_element_by_css_selector( - css_selector_of_option) + menu_option = self.driver.find_element( + By.CSS_SELECTOR, css_selector_of_option) if menu_option.get_attribute('data-checked') == 'true': while retry > 0: menu_option.click() @@ -305,8 +306,8 @@ class PgadminPage: QueryToolLocators.btn_auto_rollback) if menu.get_attribute('data-state') == "open": - self.driver.find_element_by_css_selector( - QueryToolLocators.btn_query_dropdown).click() + self.driver.find_element( + By.CSS_SELECTOR, QueryToolLocators.btn_query_dropdown).click() def close_data_grid(self): self.driver.switch_to.default_content() @@ -538,8 +539,8 @@ class PgadminPage: self.fill_input(field, password) self.find_by_xpath(ConnectToServerDiv.ok_button).click() self.wait_for_element_to_disappear( - lambda driver: driver.find_element_by_xpath( - ConnectToServerDiv.ok_button)) + lambda driver: driver.find_element( + By.XPATH, ConnectToServerDiv.ok_button)) if self.check_if_element_exist_by_xpath( ConnectToServerDiv.error_message, 2): print( @@ -593,6 +594,16 @@ class PgadminPage: else: child_node_ele = self.check_if_element_exists_with_scroll( server_child_node_xpath) + if not child_node_ele: + databases_node = self.driver.find_element( + By.XPATH, + TreeAreaLocators.server_child_node( + server_name, 'Databases')) + webdriver.ActionChains(self.driver).double_click( + databases_node).perform() + child_node_ele = self.check_if_element_exists_with_scroll( + server_child_node_xpath) + server_child_expanded = self.click_to_expand_tree_node( child_node_ele, server_child_node_exp_status_xpath) if not server_child_expanded: @@ -803,140 +814,6 @@ class PgadminPage: print("The schema/previous nodes not expanded", file=sys.stderr) return schema_child_expanded - # TODO Not used any where to be removed - def toggle_open_function_node(self): - """The function will be used for opening Functions node only""" - node_expanded = False - attempts = 3 - - xpath_for_functions_node = \ - "//span[@class='aciTreeText' and starts-with(text()," \ - "'Functions')]" - xpath_for_exp = "//div[div[div[div[div[div[div[div[span[span[" \ - "(@class='aciTreeText') and starts-with(text()," \ - "'Functions')]]]]]]]]]]" - xpath_for_button = "//div[span[span[(@class='aciTreeText') " \ - "and starts-with(text(),'Functions')]]]" \ - "/span[@class='aciTreeButton']" - - while node_expanded is not True and attempts > 0: - # get the element which contains 'aria-expanded' info - - xpath_for_refresh_btn = "//li[@class='context-menu-item']" \ - "/span[text()='Refresh']" - - # add code to refresh button, sometime the collapsing button - # is not visible even if there is sub node. - functions_node_ele = self.find_by_xpath(xpath_for_functions_node) - - webdriver.ActionChains(self.driver).move_to_element( - functions_node_ele).context_click().perform() - refresh_btn = self.find_by_xpath(xpath_for_refresh_btn) - refresh_btn.click() - time.sleep(.5) - - # get the expansion status - function_expansion_ele = self.find_by_xpath(xpath_for_exp) - - # look into the attribute and check if it is already expanded or - # not - if function_expansion_ele.get_attribute('aria-expanded') \ - == 'false': - # button element of the Function node to open it - item_button = self.find_by_xpath(xpath_for_button) - ActionChains(self.driver).click(item_button).perform() - # Expansion of element on GUI takes sometime, so put small - # sleep - time.sleep(.5) - function_expansion_ele = self.find_by_xpath( - xpath_for_exp) - if function_expansion_ele.get_attribute('aria-expanded') \ - == 'true': - break - else: - attempts -= 1 - else: - node_expanded = True - - # TODO Not used any where to be removed - def get_expansion_status_of_node(self, xpath_node): - """get the expansion status for a node through xpath""" - node_is_expanded = False - element = self.find_by_xpath(xpath_node) - if element.get_attribute("aria-expanded") == 'true': - node_is_expanded = True - return node_is_expanded - - # TODO Not used any where to be removed - def get_expansion_status_of_node_element(self, element): - """get the expansion status for an element""" - node_is_expanded = False - try: - if element.get_attribute("aria-expanded") == 'true': - node_is_expanded = True - except Exception as e: - print( - "There is some exception thrown in the function " - "get_expansion_status_of_node_element and is: " + str( - e), file=sys.stderr) - return node_is_expanded - - # TODO Not used any where to be removed - def toggle_open_tree_item(self, tree_item_text): - # 'sleep' here helps in cases where underlying nodes are auto opened. - # Otherwise, encountered situations where False value is returned - # even if the underlying node to be clicked was Opened. - time.sleep(.6) - item_with_text = self.find_by_xpath( - TreeAreaLocators.specified_tree_node.format(tree_item_text)) - - self.driver.execute_script(self.js_executor_scrollintoview_arg, - item_with_text) - - if item_with_text.find_element_by_xpath( - ".//ancestor::*[@class='aciTreeLine']").get_attribute( - "aria-expanded") == 'false': - item = item_with_text.find_element_by_xpath( - ".//parent::*[@class='aciTreeItem']") - ActionChains(self.driver).double_click(item).perform() - retry = 3 - while retry > 0: - try: - WebDriverWait(self.driver, 5).until((lambda item_with_text: ( - item_with_text.find_element_by_xpath( - ".//ancestor::*[@class='aciTreeLine']"). - get_attribute("aria-expanded") == 'true'))) - break - except TimeoutException: - retry -= 1 - - # TODO Not used any where to be removed - def toggle_open_server(self, tree_item_text): - def check_for_password_dialog_or_tree_open(driver): - try: - dialog = driver.find_element_by_id("frmPassword") - except WebDriverException: - dialog = None - try: - database_node = driver.find_element_by_xpath( - "//*[@id='tree']//*[.='Databases']" - "/../*[@class='aciTreeButton']") - except WebDriverException: - database_node = None - - return dialog is not None or database_node is not None - - self.toggle_open_tree_item(tree_item_text) - self._wait_for("Waiting for password dialog or tree to open", - check_for_password_dialog_or_tree_open) - - try: - self.driver.find_element_by_id("frmPassword") - # Enter password here if needed - self.click_modal('OK') - except WebDriverException: - return - def find_by_xpath(self, xpath): return self.wait_for_element( lambda driver: driver.find_element(By.XPATH, xpath) @@ -1049,7 +926,7 @@ class PgadminPage: try: driver.switch_to.default_content() driver.switch_to.frame( - driver.find_element_by_tag_name("iframe")) + driver.find_element(By.TAG_NAME, "iframe")) element = driver.find_element( By.CSS_SELECTOR, "#sqleditor-container .CodeMirror") if element.is_displayed() and element.is_enabled(): @@ -1123,8 +1000,8 @@ class PgadminPage: def wait_for_input_field_content(self, field_name, content, wait=1): def input_field_has_content(driver): - element = driver.find_element_by_xpath( - "//input[@name='" + field_name + "']") + element = driver.find_element( + By.XPATH, "//input[@name='" + field_name + "']") return str(content) == element.get_attribute('value') @@ -1173,7 +1050,7 @@ class PgadminPage: def wait_for_reloading_indicator_to_disappear(self): def reloading_indicator_has_disappeared(driver): try: - driver.find_element_by_id("reloading-indicator") + driver.find_element(By.ID, "reloading-indicator") return False except NoSuchElementException: return True @@ -1394,7 +1271,7 @@ class PgadminPage: WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located(verify_locator)) click_status = True - except Exception: + except Exception as e: attempt += 1 return click_status diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py index 1e14219bf..5cbdd3685 100644 --- a/web/regression/python_test_utils/test_utils.py +++ b/web/regression/python_test_utils/test_utils.py @@ -357,8 +357,11 @@ def create_debug_function(server, db_name, function_name="test_func"): old_isolation_level = connection.isolation_level connection.set_isolation_level(0) pg_cursor = connection.cursor() + try: + pg_cursor.execute('''CREATE EXTENSION pldbgapi;''') + except Exception as e: + pass pg_cursor.execute(''' - CREATE EXTENSION pldbgapi; CREATE OR REPLACE FUNCTION public."%s"() RETURNS text LANGUAGE 'plpgsql'