uint64 my_processed = 0;
SPITupleTable *my_tuptable = NULL;
int res = 0;
+ bool allow_nonatomic;
bool pushed_active_snap = false;
ResourceOwner plan_owner = options->owner;
SPICallbackArg spicallbackarg;
CachedPlan *cplan = NULL;
ListCell *lc1;
+ /*
+ * We allow nonatomic behavior only if options->allow_nonatomic is set
+ * *and* the SPI_OPT_NONATOMIC flag was given when connecting.
+ */
+ allow_nonatomic = options->allow_nonatomic && !_SPI_current->atomic;
+
/*
* Setup error traceback support for ereport()
*/
* snapshot != InvalidSnapshot, read_only = false: use the given snapshot,
* modified by advancing its command ID before each querytree.
*
- * snapshot == InvalidSnapshot, read_only = true: use the entry-time
- * ActiveSnapshot, if any (if there isn't one, we run with no snapshot).
+ * snapshot == InvalidSnapshot, read_only = true: do nothing for queries
+ * that require no snapshot. For those that do, ensure that a Portal
+ * snapshot exists; then use that, or use the entry-time ActiveSnapshot if
+ * that exists and is different.
*
- * snapshot == InvalidSnapshot, read_only = false: take a full new
- * snapshot for each user command, and advance its command ID before each
- * querytree within the command.
+ * snapshot == InvalidSnapshot, read_only = false: do nothing for queries
+ * that require no snapshot. For those that do, ensure that a Portal
+ * snapshot exists; then, in atomic execution (!allow_nonatomic) take a
+ * full new snapshot for each user command, and advance its command ID
+ * before each querytree within the command. In allow_nonatomic mode we
+ * just use the Portal snapshot unmodified.
*
* In the first two cases, we can just push the snap onto the stack once
* for the whole plan list.
*/
if (snapshot != InvalidSnapshot)
{
+ /* this intentionally tests the options field not the derived value */
Assert(!options->allow_nonatomic);
if (options->read_only)
{
* Skip it when doing non-atomic execution, though (we rely
* entirely on the Portal snapshot in that case).
*/
- if (!options->read_only && !options->allow_nonatomic)
+ if (!options->read_only && !allow_nonatomic)
{
if (pushed_active_snap)
PopActiveSnapshot();
QueryCompletion qc;
/*
- * If the SPI context is atomic, or we were not told to allow
- * nonatomic operations, tell ProcessUtility this is an atomic
- * execution context.
+ * If we're not allowing nonatomic operations, tell
+ * ProcessUtility this is an atomic execution context.
*/
- if (_SPI_current->atomic || !options->allow_nonatomic)
- context = PROCESS_UTILITY_QUERY;
- else
+ if (allow_nonatomic)
context = PROCESS_UTILITY_QUERY_NONATOMIC;
+ else
+ context = PROCESS_UTILITY_QUERY;
InitializeQueryCompletion(&qc);
ProcessUtility(stmt,
(1 row)
+-- Check that stable functions in CALL see the correct snapshot
+CREATE TABLE t_test (x int);
+INSERT INTO t_test VALUES (0);
+CREATE FUNCTION f_get_x () RETURNS int
+AS $$
+DECLARE l_result int;
+BEGIN
+ SELECT x INTO l_result FROM t_test;
+ RETURN l_result;
+END
+$$ LANGUAGE plpgsql STABLE;
+CREATE PROCEDURE f_print_x (x int)
+AS $$
+BEGIN
+ RAISE NOTICE 'f_print_x(%)', x;
+END
+$$ LANGUAGE plpgsql;
+-- test in non-atomic context
+DO $$
+BEGIN
+ UPDATE t_test SET x = x + 1;
+ RAISE NOTICE 'f_get_x(%)', f_get_x();
+ CALL f_print_x(f_get_x());
+ UPDATE t_test SET x = x + 1;
+ RAISE NOTICE 'f_get_x(%)', f_get_x();
+ CALL f_print_x(f_get_x());
+ ROLLBACK;
+END
+$$;
+NOTICE: f_get_x(1)
+NOTICE: f_print_x(1)
+NOTICE: f_get_x(2)
+NOTICE: f_print_x(2)
+-- test in atomic context
+BEGIN;
+DO $$
+BEGIN
+ UPDATE t_test SET x = x + 1;
+ RAISE NOTICE 'f_get_x(%)', f_get_x();
+ CALL f_print_x(f_get_x());
+ UPDATE t_test SET x = x + 1;
+ RAISE NOTICE 'f_get_x(%)', f_get_x();
+ CALL f_print_x(f_get_x());
+END
+$$;
+NOTICE: f_get_x(1)
+NOTICE: f_print_x(1)
+NOTICE: f_get_x(2)
+NOTICE: f_print_x(2)
+ROLLBACK;
CALL outer_p(42);
SELECT outer_f(42);
+
+-- Check that stable functions in CALL see the correct snapshot
+
+CREATE TABLE t_test (x int);
+INSERT INTO t_test VALUES (0);
+
+CREATE FUNCTION f_get_x () RETURNS int
+AS $$
+DECLARE l_result int;
+BEGIN
+ SELECT x INTO l_result FROM t_test;
+ RETURN l_result;
+END
+$$ LANGUAGE plpgsql STABLE;
+
+CREATE PROCEDURE f_print_x (x int)
+AS $$
+BEGIN
+ RAISE NOTICE 'f_print_x(%)', x;
+END
+$$ LANGUAGE plpgsql;
+
+-- test in non-atomic context
+DO $$
+BEGIN
+ UPDATE t_test SET x = x + 1;
+ RAISE NOTICE 'f_get_x(%)', f_get_x();
+ CALL f_print_x(f_get_x());
+ UPDATE t_test SET x = x + 1;
+ RAISE NOTICE 'f_get_x(%)', f_get_x();
+ CALL f_print_x(f_get_x());
+ ROLLBACK;
+END
+$$;
+
+-- test in atomic context
+BEGIN;
+
+DO $$
+BEGIN
+ UPDATE t_test SET x = x + 1;
+ RAISE NOTICE 'f_get_x(%)', f_get_x();
+ CALL f_print_x(f_get_x());
+ UPDATE t_test SET x = x + 1;
+ RAISE NOTICE 'f_get_x(%)', f_get_x();
+ CALL f_print_x(f_get_x());
+END
+$$;
+
+ROLLBACK;