mirror of https://github.com/citusdata/citus.git
Address the issues/comments from the original PR# 6315
1) Regular users fail to use clock UDF with permission issue. 2) Clock functions were declared as STABLE, whereas by definition they are VOLATILE. By design, any clock/time functions will return different results for each call even within a single SQL statement. Note: UDF citus_get_transaction_clock() is a misnomer as it internally calls the clock tick which always returns different results for every invocation in the same transaction.pull/6489/merge
parent
65f256eec4
commit
e14dc5d45d
|
@ -44,10 +44,15 @@
|
||||||
|
|
||||||
#define SAVE_AND_PERSIST(c) \
|
#define SAVE_AND_PERSIST(c) \
|
||||||
do { \
|
do { \
|
||||||
|
Oid savedUserId = InvalidOid; \
|
||||||
|
int savedSecurityContext = 0; \
|
||||||
LogicalClockShmem->clusterClockValue = *(c); \
|
LogicalClockShmem->clusterClockValue = *(c); \
|
||||||
|
GetUserIdAndSecContext(&savedUserId, &savedSecurityContext); \
|
||||||
|
SetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE); \
|
||||||
DirectFunctionCall2(setval_oid, \
|
DirectFunctionCall2(setval_oid, \
|
||||||
ObjectIdGetDatum(DistClockLogicalSequenceId()), \
|
ObjectIdGetDatum(DistClockLogicalSequenceId()), \
|
||||||
Int64GetDatum((c)->logical)); \
|
Int64GetDatum((c)->logical)); \
|
||||||
|
SetUserIdAndSecContext(savedUserId, savedSecurityContext); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(citus_get_node_clock);
|
PG_FUNCTION_INFO_V1(citus_get_node_clock);
|
||||||
|
@ -61,7 +66,7 @@ PG_FUNCTION_INFO_V1(citus_get_transaction_clock);
|
||||||
typedef enum ClockState
|
typedef enum ClockState
|
||||||
{
|
{
|
||||||
CLOCKSTATE_INITIALIZED,
|
CLOCKSTATE_INITIALIZED,
|
||||||
CLOCKSTATE_UNINITIALIZED,
|
CLOCKSTATE_UNINITIALIZED
|
||||||
} ClockState;
|
} ClockState;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -504,9 +509,17 @@ InitClockAtFirstUse(void)
|
||||||
* a higher rate than once every 32 seconds.
|
* a higher rate than once every 32 seconds.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
Oid saveUserId = InvalidOid;
|
||||||
|
int savedSecurityCtx = 0;
|
||||||
|
|
||||||
|
GetUserIdAndSecContext(&saveUserId, &savedSecurityCtx);
|
||||||
|
SetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);
|
||||||
|
|
||||||
persistedMaxClock.logical =
|
persistedMaxClock.logical =
|
||||||
DirectFunctionCall1(nextval_oid, ObjectIdGetDatum(DistClockLogicalSequenceId()));
|
DirectFunctionCall1(nextval_oid, ObjectIdGetDatum(DistClockLogicalSequenceId()));
|
||||||
|
|
||||||
|
SetUserIdAndSecContext(saveUserId, savedSecurityCtx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sequence 1 indicates no prior clock timestamps on this server, retain
|
* Sequence 1 indicates no prior clock timestamps on this server, retain
|
||||||
* the wall clock i.e. no adjustment needed.
|
* the wall clock i.e. no adjustment needed.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
CREATE OR REPLACE FUNCTION pg_catalog.citus_get_node_clock()
|
CREATE OR REPLACE FUNCTION pg_catalog.citus_get_node_clock()
|
||||||
RETURNS pg_catalog.cluster_clock
|
RETURNS pg_catalog.cluster_clock
|
||||||
LANGUAGE C STABLE PARALLEL SAFE STRICT
|
LANGUAGE C VOLATILE PARALLEL UNSAFE STRICT
|
||||||
AS 'MODULE_PATHNAME',$$citus_get_node_clock$$;
|
AS 'MODULE_PATHNAME',$$citus_get_node_clock$$;
|
||||||
COMMENT ON FUNCTION pg_catalog.citus_get_node_clock()
|
COMMENT ON FUNCTION pg_catalog.citus_get_node_clock()
|
||||||
IS 'Returns monotonically increasing timestamp with logical clock value as close to epoch value (in milli seconds) as possible, and a counter for ticks(maximum of 4 million) within the logical clock';
|
IS 'Returns monotonically increasing timestamp with logical clock value as close to epoch value (in milli seconds) as possible, and a counter for ticks(maximum of 4 million) within the logical clock';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
CREATE OR REPLACE FUNCTION pg_catalog.citus_get_node_clock()
|
CREATE OR REPLACE FUNCTION pg_catalog.citus_get_node_clock()
|
||||||
RETURNS pg_catalog.cluster_clock
|
RETURNS pg_catalog.cluster_clock
|
||||||
LANGUAGE C STABLE PARALLEL SAFE STRICT
|
LANGUAGE C VOLATILE PARALLEL UNSAFE STRICT
|
||||||
AS 'MODULE_PATHNAME',$$citus_get_node_clock$$;
|
AS 'MODULE_PATHNAME',$$citus_get_node_clock$$;
|
||||||
COMMENT ON FUNCTION pg_catalog.citus_get_node_clock()
|
COMMENT ON FUNCTION pg_catalog.citus_get_node_clock()
|
||||||
IS 'Returns monotonically increasing timestamp with logical clock value as close to epoch value (in milli seconds) as possible, and a counter for ticks(maximum of 4 million) within the logical clock';
|
IS 'Returns monotonically increasing timestamp with logical clock value as close to epoch value (in milli seconds) as possible, and a counter for ticks(maximum of 4 million) within the logical clock';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
CREATE OR REPLACE FUNCTION pg_catalog.citus_get_transaction_clock()
|
CREATE OR REPLACE FUNCTION pg_catalog.citus_get_transaction_clock()
|
||||||
RETURNS pg_catalog.cluster_clock
|
RETURNS pg_catalog.cluster_clock
|
||||||
LANGUAGE C STABLE PARALLEL SAFE STRICT
|
LANGUAGE C VOLATILE PARALLEL UNSAFE STRICT
|
||||||
AS 'MODULE_PATHNAME',$$citus_get_transaction_clock$$;
|
AS 'MODULE_PATHNAME',$$citus_get_transaction_clock$$;
|
||||||
COMMENT ON FUNCTION pg_catalog.citus_get_transaction_clock()
|
COMMENT ON FUNCTION pg_catalog.citus_get_transaction_clock()
|
||||||
IS 'Returns a transaction timestamp logical clock';
|
IS 'Returns a transaction timestamp logical clock';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
CREATE OR REPLACE FUNCTION pg_catalog.citus_get_transaction_clock()
|
CREATE OR REPLACE FUNCTION pg_catalog.citus_get_transaction_clock()
|
||||||
RETURNS pg_catalog.cluster_clock
|
RETURNS pg_catalog.cluster_clock
|
||||||
LANGUAGE C STABLE PARALLEL SAFE STRICT
|
LANGUAGE C VOLATILE PARALLEL UNSAFE STRICT
|
||||||
AS 'MODULE_PATHNAME',$$citus_get_transaction_clock$$;
|
AS 'MODULE_PATHNAME',$$citus_get_transaction_clock$$;
|
||||||
COMMENT ON FUNCTION pg_catalog.citus_get_transaction_clock()
|
COMMENT ON FUNCTION pg_catalog.citus_get_transaction_clock()
|
||||||
IS 'Returns a transaction timestamp logical clock';
|
IS 'Returns a transaction timestamp logical clock';
|
||||||
|
|
|
@ -328,9 +328,51 @@ WARNING: GUC enable_cluster_clock is off
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
SET citus.enable_cluster_clock to ON;
|
||||||
|
-- Test if the clock UDFs are volatile, result should never be the same
|
||||||
|
SELECT citus_get_node_clock() = citus_get_node_clock();
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select citus_get_transaction_clock() = citus_get_transaction_clock();
|
||||||
|
DEBUG: node xxxx transaction clock xxxxxx
|
||||||
|
DEBUG: final global transaction clock xxxxxx
|
||||||
|
DEBUG: node xxxx transaction clock xxxxxx
|
||||||
|
DEBUG: final global transaction clock xxxxxx
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Test if the clock UDFs are usable by non-superusers
|
||||||
|
CREATE ROLE non_super_user_clock;
|
||||||
|
SET ROLE non_super_user_clock;
|
||||||
|
SELECT citus_get_node_clock();
|
||||||
|
citus_get_node_clock
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
(xxxxxxxxxxxxx,x)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
SELECT citus_get_transaction_clock();
|
||||||
|
DEBUG: node xxxx transaction clock xxxxxx
|
||||||
|
DEBUG: final global transaction clock xxxxxx
|
||||||
|
citus_get_transaction_clock
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
(xxxxxxxxxxxxx,x)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
-- Test setting the persisted clock value (it must fail)
|
||||||
|
SELECT setval('pg_dist_clock_logical_seq', 100, true);
|
||||||
|
ERROR: permission denied for sequence pg_dist_clock_logical_seq
|
||||||
|
\c
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
RESET citus.enable_cluster_clock;
|
RESET citus.enable_cluster_clock;
|
||||||
|
DROP ROLE non_super_user_clock;
|
||||||
DROP SCHEMA clock CASCADE;
|
DROP SCHEMA clock CASCADE;
|
||||||
NOTICE: drop cascades to 2 other objects
|
NOTICE: drop cascades to 2 other objects
|
||||||
DETAIL: drop cascades to table clock_test
|
DETAIL: drop cascades to table clock.clock_test
|
||||||
drop cascades to table cluster_clock_type
|
drop cascades to table clock.cluster_clock_type
|
||||||
|
|
|
@ -141,6 +141,25 @@ BEGIN;
|
||||||
SELECT citus_get_transaction_clock();
|
SELECT citus_get_transaction_clock();
|
||||||
END;
|
END;
|
||||||
|
|
||||||
|
SET citus.enable_cluster_clock to ON;
|
||||||
|
|
||||||
|
-- Test if the clock UDFs are volatile, result should never be the same
|
||||||
|
SELECT citus_get_node_clock() = citus_get_node_clock();
|
||||||
|
select citus_get_transaction_clock() = citus_get_transaction_clock();
|
||||||
|
|
||||||
|
-- Test if the clock UDFs are usable by non-superusers
|
||||||
|
CREATE ROLE non_super_user_clock;
|
||||||
|
SET ROLE non_super_user_clock;
|
||||||
|
SELECT citus_get_node_clock();
|
||||||
|
BEGIN;
|
||||||
|
SELECT citus_get_transaction_clock();
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- Test setting the persisted clock value (it must fail)
|
||||||
|
SELECT setval('pg_dist_clock_logical_seq', 100, true);
|
||||||
|
|
||||||
|
\c
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
RESET citus.enable_cluster_clock;
|
RESET citus.enable_cluster_clock;
|
||||||
|
DROP ROLE non_super_user_clock;
|
||||||
DROP SCHEMA clock CASCADE;
|
DROP SCHEMA clock CASCADE;
|
||||||
|
|
Loading…
Reference in New Issue