Add distributed_exec() function

This function allows users to execute a SQL query on a list of data
nodes. The purpose is to provide users a way to, e.g., create roles on
data nodes.

The current implementation is quite straightforward. Just execute any
provided query on a list of data nodes. The query will execute with
the current user role. The function does not return or print any
result values. In case of error, it will print the data node name and
a related error message.
This commit is contained in:
Dmitry Simonenko 2019-08-22 15:30:08 +03:00 committed by Erik Nordström
parent 334604edc7
commit c8563b2d46
13 changed files with 292 additions and 76 deletions

View File

@ -200,3 +200,8 @@ AS '@MODULE_PATHNAME@', 'ts_data_node_block_new_chunks' LANGUAGE C VOLATILE;
-- allow chunks for all distributed hypertables
CREATE OR REPLACE FUNCTION allow_new_chunks(data_node_name NAME, hypertable REGCLASS = NULL) RETURNS INTEGER
AS '@MODULE_PATHNAME@', 'ts_data_node_allow_new_chunks' LANGUAGE C VOLATILE;
-- Execute query on a specified list of data nodes. By default node_list is NULL, which means
-- to execute the query on every data node
CREATE OR REPLACE FUNCTION distributed_exec(query TEXT, node_list name[] = NULL) RETURNS VOID
AS '@MODULE_PATHNAME@', 'ts_distributed_exec' LANGUAGE C VOLATILE;

View File

@ -63,6 +63,7 @@ TS_FUNCTION_INFO_V1(ts_dist_remove_id);
TS_FUNCTION_INFO_V1(ts_dist_set_peer_id);
TS_FUNCTION_INFO_V1(ts_dist_remote_hypertable_info);
TS_FUNCTION_INFO_V1(ts_dist_validate_as_data_node);
TS_FUNCTION_INFO_V1(ts_distributed_exec);
Datum
ts_add_drop_chunks_policy(PG_FUNCTION_ARGS)
@ -234,6 +235,13 @@ ts_dist_validate_as_data_node(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
Datum
ts_distributed_exec(PG_FUNCTION_ARGS)
{
ts_cm_functions->distributed_exec(fcinfo);
PG_RETURN_VOID();
}
/*
* stub function to trigger aggregate util functions.
*/
@ -630,6 +638,7 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = {
.data_node_ping = error_no_default_fn_pg_community,
.detach_data_node = error_no_default_fn_pg_community,
.data_node_set_block_new_chunks = data_node_set_block_new_chunks_default,
.distributed_exec = error_no_default_fn_pg_community,
.set_chunk_default_data_node = error_no_default_fn_pg_community,
.show_chunk = error_no_default_fn_pg_community,
.create_chunk = error_no_default_fn_pg_community,

View File

@ -145,6 +145,7 @@ typedef struct CrossModuleFunctions
Oid newer_than_type, bool cascade,
bool cascades_to_materializations, bool verbose,
List *data_node_oids);
PGFunction distributed_exec;
} CrossModuleFunctions;
extern TSDLLEXPORT CrossModuleFunctions *ts_cm_functions;

View File

@ -35,6 +35,7 @@ ORDER BY proname;
detach_data_node
detach_tablespace
detach_tablespaces
distributed_exec
drop_chunks
first
get_telemetry_report
@ -65,5 +66,5 @@ ORDER BY proname;
timescaledb_fdw_validator
timescaledb_post_restore
timescaledb_pre_restore
(51 rows)
(52 rows)

View File

@ -58,7 +58,7 @@ if [[ ! -f ${TEST_OUTPUT_DIR}/.pg_init ]]; then
SET client_min_messages=ERROR;
ALTER USER ${TEST_ROLE_SUPERUSER} WITH SUPERUSER;
ALTER USER ${TEST_ROLE_CLUSTER_SUPERUSER} WITH SUPERUSER;
ALTER USER ${TEST_ROLE_1} WITH CREATEDB;
ALTER USER ${TEST_ROLE_1} WITH CREATEDB CREATEROLE;
ALTER USER ${TEST_ROLE_2} WITH CREATEDB;
ALTER USER ${TEST_ROLE_3} WITH CREATEDB PASSWORD '${TEST_ROLE_3_PASS}';
EOF

View File

@ -555,7 +555,7 @@ data_node_add_internal(PG_FUNCTION_ARGS, bool set_distid)
bool database_created = false;
bool extension_created = false;
if (set_distid && dist_util_membership() == DIST_MEMBER_BACKEND)
if (set_distid && dist_util_membership() == DIST_MEMBER_DATA_NODE)
ereport(ERROR,
(errcode(ERRCODE_TS_DATA_NODE_ASSIGNMENT_ALREADY_EXISTS),
(errmsg("unable to assign data nodes from an existing distributed database"))));
@ -603,7 +603,7 @@ data_node_add_internal(PG_FUNCTION_ARGS, bool set_distid)
if (set_distid)
{
if (dist_util_membership() != DIST_MEMBER_FRONTEND)
if (dist_util_membership() != DIST_MEMBER_ACCESS_NODE)
dist_util_set_as_frontend();
add_distributed_id_to_data_node(node_name,

View File

@ -56,9 +56,9 @@ dist_util_membership(void)
if (isnull)
return DIST_MEMBER_NONE;
else if (uuid_matches(dist_id, ts_telemetry_metadata_get_uuid()))
return DIST_MEMBER_FRONTEND;
return DIST_MEMBER_ACCESS_NODE;
else
return DIST_MEMBER_BACKEND;
return DIST_MEMBER_DATA_NODE;
}
void

View File

@ -11,9 +11,9 @@
typedef enum DistUtilMembershipStatus
{
DIST_MEMBER_NONE, /* Database doesn't belong to a distributed database */
DIST_MEMBER_BACKEND, /* Database is a backend node */
DIST_MEMBER_FRONTEND /* Database is a frontend node */
DIST_MEMBER_NONE, /* Database doesn't belong to a distributed database */
DIST_MEMBER_DATA_NODE, /* Database is a backend node */
DIST_MEMBER_ACCESS_NODE /* Database is a frontend node */
} DistUtilMembershipStatus;
DistUtilMembershipStatus dist_util_membership(void);

View File

@ -53,6 +53,7 @@
#include "remote/txn_id.h"
#include "remote/txn_resolve.h"
#include "remote/dist_copy.h"
#include "remote/dist_commands.h"
#include "process_utility.h"
#include "dist_util.h"
#include "remote/connection.h"
@ -256,6 +257,7 @@ CrossModuleFunctions tsl_cm_functions = {
.remove_from_distributed_db = NULL,
.remote_hypertable_info = error_not_supported_default_fn,
.validate_as_data_node = NULL,
.distributed_exec = error_not_supported_default_fn,
#else
.add_data_node = data_node_add,
.delete_data_node = data_node_delete,
@ -287,6 +289,7 @@ CrossModuleFunctions tsl_cm_functions = {
.remote_hypertable_info = dist_util_remote_hypertable_info,
.validate_as_data_node = validate_data_node_settings,
.drop_chunks_on_data_nodes = chunk_drop_remote_chunks,
.distributed_exec = ts_dist_cmd_exec,
#endif
.cache_syscache_invalidate = cache_syscache_invalidate,
};

View File

@ -4,6 +4,9 @@
* LICENSE-TIMESCALE for a copy of the license.
*/
#include <postgres.h>
#include <utils/builtins.h>
#include <utils/guc.h>
#include <catalog/namespace.h>
#include <libpq-fe.h>
@ -11,6 +14,7 @@
#include "remote/dist_txn.h"
#include "remote/connection_cache.h"
#include "data_node.h"
#include "dist_util.h"
#include "miscadmin.h"
typedef struct DistPreparedStmt
@ -235,3 +239,32 @@ ts_dist_cmd_close_prepared_command(PreparedDistCmd *command)
list_free_deep(command);
}
Datum
ts_dist_cmd_exec(PG_FUNCTION_ARGS)
{
const char *query = TextDatumGetCString(PG_GETARG_DATUM(0));
ArrayType *data_nodes = PG_ARGISNULL(1) ? NULL : PG_GETARG_ARRAYTYPE_P(1);
DistCmdResult *result;
List *data_node_list;
const char *search_path;
if (dist_util_membership() != DIST_MEMBER_ACCESS_NODE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function must be run on the access node only")));
if (data_nodes == NULL)
data_node_list = data_node_get_node_name_list();
else
data_node_list = data_node_array_to_node_name_list(data_nodes);
search_path = GetConfigOption("search_path", false, false);
result = ts_dist_cmd_invoke_on_data_nodes_using_search_path(query, search_path, data_node_list);
if (result)
ts_dist_cmd_close_response(result);
list_free(data_node_list);
PG_RETURN_VOID();
}

View File

@ -35,4 +35,6 @@ extern DistCmdResult *ts_dist_cmd_invoke_prepared_command(PreparedDistCmd *comma
extern void ts_dist_cmd_close_prepared_command(PreparedDistCmd *command);
extern Datum ts_dist_cmd_exec(PG_FUNCTION_ARGS);
#endif

View File

@ -2,38 +2,42 @@
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
\c :TEST_DBNAME :ROLE_SUPERUSER
-- Support for execute_sql_and_filter_server_name_on_error()
\unset ECHO
psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping
psql:include/filter_exec.sql:5: NOTICE: schema "test" already exists, skipping
SET ROLE :ROLE_1;
SELECT * FROM add_data_node('data_node1',
database => 'data_node1',
SELECT * FROM add_data_node('data_node_1',
database => 'data_node_1',
bootstrap_user => :'ROLE_CLUSTER_SUPERUSER');
node_name | host | port | database | node_created | database_created | extension_created
------------+-----------+-------+------------+--------------+------------------+-------------------
data_node1 | localhost | 15432 | data_node1 | t | t | t
node_name | host | port | database | node_created | database_created | extension_created
-------------+-----------+-------+-------------+--------------+------------------+-------------------
data_node_1 | localhost | 15432 | data_node_1 | t | t | t
(1 row)
SELECT * FROM add_data_node('data_node2',
database => 'data_node2',
SELECT * FROM add_data_node('data_node_2',
database => 'data_node_2',
bootstrap_user => :'ROLE_CLUSTER_SUPERUSER');
node_name | host | port | database | node_created | database_created | extension_created
------------+-----------+-------+------------+--------------+------------------+-------------------
data_node2 | localhost | 15432 | data_node2 | t | t | t
node_name | host | port | database | node_created | database_created | extension_created
-------------+-----------+-------+-------------+--------------+------------------+-------------------
data_node_2 | localhost | 15432 | data_node_2 | t | t | t
(1 row)
SELECT * FROM add_data_node('data_node3',
database => 'data_node3',
SELECT * FROM add_data_node('data_node_3',
database => 'data_node_3',
bootstrap_user => :'ROLE_CLUSTER_SUPERUSER');
node_name | host | port | database | node_created | database_created | extension_created
------------+-----------+-------+------------+--------------+------------------+-------------------
data_node3 | localhost | 15432 | data_node3 | t | t | t
node_name | host | port | database | node_created | database_created | extension_created
-------------+-----------+-------+-------------+--------------+------------------+-------------------
data_node_3 | localhost | 15432 | data_node_3 | t | t | t
(1 row)
\des+
List of foreign servers
Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | FDW options | Description
------------+-------------+----------------------+-------------------+------+---------+-------------------------------------------------------+-------------
data_node1 | test_role_1 | timescaledb_fdw | | | | (host 'localhost', port '15432', dbname 'data_node1') |
data_node2 | test_role_1 | timescaledb_fdw | | | | (host 'localhost', port '15432', dbname 'data_node2') |
data_node3 | test_role_1 | timescaledb_fdw | | | | (host 'localhost', port '15432', dbname 'data_node3') |
List of foreign servers
Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | FDW options | Description
-------------+-------------+----------------------+-------------------+------+---------+--------------------------------------------------------+-------------
data_node_1 | test_role_1 | timescaledb_fdw | | | | (host 'localhost', port '15432', dbname 'data_node_1') |
data_node_2 | test_role_1 | timescaledb_fdw | | | | (host 'localhost', port '15432', dbname 'data_node_2') |
data_node_3 | test_role_1 | timescaledb_fdw | | | | (host 'localhost', port '15432', dbname 'data_node_3') |
(3 rows)
RESET ROLE;
@ -47,19 +51,19 @@ AS :TSL_MODULE_PATHNAME, 'tsl_invoke_faulty_distributed_command'
LANGUAGE C STRICT;
SET ROLE :ROLE_1;
SELECT _timescaledb_internal.invoke_distributed_commands();
INFO: data_node1 result: PGRES_COMMAND_OK
INFO: data_node2 result: PGRES_COMMAND_OK
INFO: data_node3 result: PGRES_COMMAND_OK
INFO: data_node1 result: PGRES_COMMAND_OK
INFO: data_node2 result: PGRES_COMMAND_OK
INFO: data_node1 result: PGRES_COMMAND_OK
INFO: data_node2 result: PGRES_COMMAND_OK
INFO: data_node_1 result: PGRES_COMMAND_OK
INFO: data_node_2 result: PGRES_COMMAND_OK
INFO: data_node_3 result: PGRES_COMMAND_OK
INFO: data_node_1 result: PGRES_COMMAND_OK
INFO: data_node_2 result: PGRES_COMMAND_OK
INFO: data_node_1 result: PGRES_COMMAND_OK
INFO: data_node_2 result: PGRES_COMMAND_OK
invoke_distributed_commands
-----------------------------
(1 row)
\c data_node1
\c data_node_1
\dt
List of relations
Schema | Name | Type | Owner
@ -74,7 +78,7 @@ SELECT * FROM disttable1;
Sat Sep 18 00:00:00 1976 PDT | 47 | 103.4
(1 row)
\c data_node2
\c data_node_2
\dt
List of relations
Schema | Name | Type | Owner
@ -89,7 +93,7 @@ SELECT * FROM disttable1;
Sat Sep 18 00:00:00 1976 PDT | 47 | 103.4
(1 row)
\c data_node3
\c data_node_3
\dt
List of relations
Schema | Name | Type | Owner
@ -107,15 +111,15 @@ SET ROLE :ROLE_1;
-- Verify failed insert command gets fully rolled back
\set ON_ERROR_STOP 0
SELECT _timescaledb_internal.invoke_faulty_distributed_command();
ERROR: [data_node3]: relation "public.disttable2" does not exist
ERROR: [data_node_3]: relation "public.disttable2" does not exist
\set ON_ERROR_STOP 1
\c data_node1
\c data_node_1
SELECT * from disttable2;
time | device | temp
------+--------+------
(0 rows)
\c data_node2
\c data_node_2
SELECT * from disttable2;
time | device | temp
------+--------+------
@ -131,7 +135,7 @@ psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping
CREATE OR REPLACE FUNCTION is_frontend_session()
RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'test_is_frontend_session' LANGUAGE C;
\c data_node1
\c data_node_1
CREATE OR REPLACE FUNCTION is_frontend_session()
RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'test_is_frontend_session' LANGUAGE C;
@ -141,7 +145,7 @@ SELECT is_frontend_session();
f
(1 row)
\c data_node2
\c data_node_2
CREATE OR REPLACE FUNCTION is_frontend_session()
RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'test_is_frontend_session' LANGUAGE C;
@ -151,7 +155,7 @@ SELECT is_frontend_session();
f
(1 row)
\c data_node3
\c data_node_3
CREATE OR REPLACE FUNCTION is_frontend_session()
RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'test_is_frontend_session' LANGUAGE C;
@ -171,31 +175,31 @@ SELECT is_frontend_session();
-- Ensure peer dist id is already set and can be set only once
\set ON_ERROR_STOP 0
SELECT * FROM test.remote_exec('{data_node1}', $$ SELECT * FROM _timescaledb_internal.set_peer_dist_id('77348176-09da-4a80-bc78-e31bdf5e63ec'); $$);
NOTICE: [data_node1]: SELECT * FROM _timescaledb_internal.set_peer_dist_id('77348176-09da-4a80-bc78-e31bdf5e63ec')
ERROR: [data_node1]: distributed peer ID already set
SELECT * FROM test.remote_exec('{data_node_1}', $$ SELECT * FROM _timescaledb_internal.set_peer_dist_id('77348176-09da-4a80-bc78-e31bdf5e63ec'); $$);
NOTICE: [data_node_1]: SELECT * FROM _timescaledb_internal.set_peer_dist_id('77348176-09da-4a80-bc78-e31bdf5e63ec')
ERROR: [data_node_1]: distributed peer ID already set
\set ON_ERROR_STOP 1
-- Repeat is_frontend_session() test again, but this time using connections openned from frontend
-- to backend nodes. Must return true.
SELECT * FROM test.remote_exec(NULL, $$ SELECT is_frontend_session(); $$);
NOTICE: [data_node1]: SELECT is_frontend_session()
NOTICE: [data_node1]:
NOTICE: [data_node_1]: SELECT is_frontend_session()
NOTICE: [data_node_1]:
is_frontend_session
-------------------
t
(1 row)
NOTICE: [data_node2]: SELECT is_frontend_session()
NOTICE: [data_node2]:
NOTICE: [data_node_2]: SELECT is_frontend_session()
NOTICE: [data_node_2]:
is_frontend_session
-------------------
t
(1 row)
NOTICE: [data_node3]: SELECT is_frontend_session()
NOTICE: [data_node3]:
NOTICE: [data_node_3]: SELECT is_frontend_session()
NOTICE: [data_node_3]:
is_frontend_session
-------------------
t
@ -207,7 +211,120 @@ t
(1 row)
-- Test distributed_exec()
-- Make sure dist session is properly set
SELECT * FROM distributed_exec('DO $$ BEGIN ASSERT(SELECT is_frontend_session()) = true; END; $$;');
distributed_exec
------------------
(1 row)
-- Test creating and dropping a table
SELECT * FROM distributed_exec('CREATE TABLE dist_test (id int)');
distributed_exec
------------------
(1 row)
SELECT * FROM distributed_exec('INSERT INTO dist_test values (7)');
distributed_exec
------------------
(1 row)
SELECT * FROM test.remote_exec(NULL, $$ SELECT * from dist_test; $$);
NOTICE: [data_node_1]: SELECT * from dist_test
NOTICE: [data_node_1]:
id
--
7
(1 row)
NOTICE: [data_node_2]: SELECT * from dist_test
NOTICE: [data_node_2]:
id
--
7
(1 row)
NOTICE: [data_node_3]: SELECT * from dist_test
NOTICE: [data_node_3]:
id
--
7
(1 row)
remote_exec
-------------
(1 row)
SELECT * FROM distributed_exec('DROP TABLE dist_test');
distributed_exec
------------------
(1 row)
\set ON_ERROR_STOP 0
SELECT * FROM distributed_exec('INSERT INTO dist_test VALUES (8)', '{data_node_1}');
ERROR: [data_node_1]: relation "dist_test" does not exist
\set ON_ERROR_STOP 1
-- Test creating and dropping a role
CREATE ROLE dist_test_role;
-- Expect this to be an error, since data nodes are created on the same instance
\set ON_ERROR_STOP 0
SELECT test.execute_sql_and_filter_data_node_name_on_error($$
SELECT * FROM distributed_exec('CREATE ROLE dist_test_role');
$$);
ERROR: [data_node_x]: role "dist_test_role" already exists
\set ON_ERROR_STOP 1
SELECT * FROM test.remote_exec(NULL, $$ SELECT true from pg_catalog.pg_roles WHERE rolname = 'dist_test_role'; $$);
NOTICE: [data_node_1]: SELECT true from pg_catalog.pg_roles WHERE rolname = 'dist_test_role'
NOTICE: [data_node_1]:
bool
----
t
(1 row)
NOTICE: [data_node_2]: SELECT true from pg_catalog.pg_roles WHERE rolname = 'dist_test_role'
NOTICE: [data_node_2]:
bool
----
t
(1 row)
NOTICE: [data_node_3]: SELECT true from pg_catalog.pg_roles WHERE rolname = 'dist_test_role'
NOTICE: [data_node_3]:
bool
----
t
(1 row)
remote_exec
-------------
(1 row)
DROP ROLE DIST_TEST_ROLE;
\set ON_ERROR_STOP 0
SELECT test.execute_sql_and_filter_data_node_name_on_error($$
SELECT * FROM distributed_exec('DROP ROLE dist_test_role');
$$);
ERROR: [data_node_x]: role "dist_test_role" does not exist
\set ON_ERROR_STOP 1
-- Do not allow to run distributed_exec() on a data nodes
\c data_node_1
\set ON_ERROR_STOP 0
SELECT * FROM distributed_exec('SELECT 1');
ERROR: function must be run on the access node only
\set ON_ERROR_STOP 1
\c :TEST_DBNAME :ROLE_SUPERUSER
DROP DATABASE data_node1;
DROP DATABASE data_node2;
DROP DATABASE data_node3;
DROP DATABASE data_node_1;
DROP DATABASE data_node_2;
DROP DATABASE data_node_3;

View File

@ -3,16 +3,25 @@
-- LICENSE-TIMESCALE for a copy of the license.
\c :TEST_DBNAME :ROLE_SUPERUSER
-- Support for execute_sql_and_filter_server_name_on_error()
\unset ECHO
\o /dev/null
\ir include/remote_exec.sql
\ir include/filter_exec.sql
\o
\set ECHO all
SET ROLE :ROLE_1;
SELECT * FROM add_data_node('data_node1',
database => 'data_node1',
SELECT * FROM add_data_node('data_node_1',
database => 'data_node_1',
bootstrap_user => :'ROLE_CLUSTER_SUPERUSER');
SELECT * FROM add_data_node('data_node2',
database => 'data_node2',
SELECT * FROM add_data_node('data_node_2',
database => 'data_node_2',
bootstrap_user => :'ROLE_CLUSTER_SUPERUSER');
SELECT * FROM add_data_node('data_node3',
database => 'data_node3',
SELECT * FROM add_data_node('data_node_3',
database => 'data_node_3',
bootstrap_user => :'ROLE_CLUSTER_SUPERUSER');
\des+
@ -31,13 +40,13 @@ SET ROLE :ROLE_1;
SELECT _timescaledb_internal.invoke_distributed_commands();
\c data_node1
\c data_node_1
\dt
SELECT * FROM disttable1;
\c data_node2
\c data_node_2
\dt
SELECT * FROM disttable1;
\c data_node3
\c data_node_3
\dt
SELECT * FROM disttable1;
\c :TEST_DBNAME :ROLE_SUPERUSER
@ -48,9 +57,9 @@ SET ROLE :ROLE_1;
SELECT _timescaledb_internal.invoke_faulty_distributed_command();
\set ON_ERROR_STOP 1
\c data_node1
\c data_node_1
SELECT * from disttable2;
\c data_node2
\c data_node_2
SELECT * from disttable2;
-- Test connection session identity
@ -68,19 +77,19 @@ CREATE OR REPLACE FUNCTION is_frontend_session()
RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'test_is_frontend_session' LANGUAGE C;
\c data_node1
\c data_node_1
CREATE OR REPLACE FUNCTION is_frontend_session()
RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'test_is_frontend_session' LANGUAGE C;
SELECT is_frontend_session();
\c data_node2
\c data_node_2
CREATE OR REPLACE FUNCTION is_frontend_session()
RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'test_is_frontend_session' LANGUAGE C;
SELECT is_frontend_session();
\c data_node3
\c data_node_3
CREATE OR REPLACE FUNCTION is_frontend_session()
RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'test_is_frontend_session' LANGUAGE C;
@ -92,14 +101,50 @@ SELECT is_frontend_session();
-- Ensure peer dist id is already set and can be set only once
\set ON_ERROR_STOP 0
SELECT * FROM test.remote_exec('{data_node1}', $$ SELECT * FROM _timescaledb_internal.set_peer_dist_id('77348176-09da-4a80-bc78-e31bdf5e63ec'); $$);
SELECT * FROM test.remote_exec('{data_node_1}', $$ SELECT * FROM _timescaledb_internal.set_peer_dist_id('77348176-09da-4a80-bc78-e31bdf5e63ec'); $$);
\set ON_ERROR_STOP 1
-- Repeat is_frontend_session() test again, but this time using connections openned from frontend
-- to backend nodes. Must return true.
SELECT * FROM test.remote_exec(NULL, $$ SELECT is_frontend_session(); $$);
-- Test distributed_exec()
-- Make sure dist session is properly set
SELECT * FROM distributed_exec('DO $$ BEGIN ASSERT(SELECT is_frontend_session()) = true; END; $$;');
-- Test creating and dropping a table
SELECT * FROM distributed_exec('CREATE TABLE dist_test (id int)');
SELECT * FROM distributed_exec('INSERT INTO dist_test values (7)');
SELECT * FROM test.remote_exec(NULL, $$ SELECT * from dist_test; $$);
SELECT * FROM distributed_exec('DROP TABLE dist_test');
\set ON_ERROR_STOP 0
SELECT * FROM distributed_exec('INSERT INTO dist_test VALUES (8)', '{data_node_1}');
\set ON_ERROR_STOP 1
-- Test creating and dropping a role
CREATE ROLE dist_test_role;
-- Expect this to be an error, since data nodes are created on the same instance
\set ON_ERROR_STOP 0
SELECT test.execute_sql_and_filter_data_node_name_on_error($$
SELECT * FROM distributed_exec('CREATE ROLE dist_test_role');
$$);
\set ON_ERROR_STOP 1
SELECT * FROM test.remote_exec(NULL, $$ SELECT true from pg_catalog.pg_roles WHERE rolname = 'dist_test_role'; $$);
DROP ROLE DIST_TEST_ROLE;
\set ON_ERROR_STOP 0
SELECT test.execute_sql_and_filter_data_node_name_on_error($$
SELECT * FROM distributed_exec('DROP ROLE dist_test_role');
$$);
\set ON_ERROR_STOP 1
-- Do not allow to run distributed_exec() on a data nodes
\c data_node_1
\set ON_ERROR_STOP 0
SELECT * FROM distributed_exec('SELECT 1');
\set ON_ERROR_STOP 1
\c :TEST_DBNAME :ROLE_SUPERUSER
DROP DATABASE data_node1;
DROP DATABASE data_node2;
DROP DATABASE data_node3;
DROP DATABASE data_node_1;
DROP DATABASE data_node_2;
DROP DATABASE data_node_3;