statement ok
CREATE TABLE xy (x INT, y INT);
INSERT INTO xy VALUES (1, 2), (3, 4);

subtest default

statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT 1 INTO i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
1

statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO i FROM xy ORDER BY x DESC LIMIT 1;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
3

# If the INTO query returns more than one row, only the first is used.
statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO i FROM xy ORDER BY x DESC;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
3

# If the INTO query returns no rows, the target variables are set to NULL.
statement ok
DELETE FROM xy WHERE true;

statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO i FROM xy ORDER BY x DESC;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
NULL

statement ok
INSERT INTO xy VALUES (1, 2), (3, 4);

query I
SELECT f();
----
3

statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    INSERT INTO xy VALUES (100, 100) RETURNING x INTO i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
100

query II rowsort
SELECT * FROM xy;
----
1    2
3    4
100  100

statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    UPDATE xy SET y = y * 2 WHERE x = 100 RETURNING x INTO i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
100

query II rowsort
SELECT * FROM xy;
----
1    2
3    4
100  200

statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    DELETE FROM xy WHERE x = 100 RETURNING x INTO i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
100

query II rowsort
SELECT * FROM xy;
----
1  2
3  4

statement ok
CREATE OR REPLACE FUNCTION f(n INT) RETURNS INT AS $$
  DECLARE
    foo INT;
    bar INT;
    i INT := 0;
  BEGIN
    LOOP IF i >= n THEN EXIT; END IF;
      INSERT INTO xy VALUES (100+i, 200+i) RETURNING y INTO bar;
      SELECT x INTO foo FROM xy ORDER BY x DESC LIMIT 1;
      RAISE NOTICE 'foo: %, bar: %', foo, bar;
      i := i + 1;
    END LOOP;
    RETURN 0;
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
SELECT f(5);
----
NOTICE: foo: 100, bar: 200
NOTICE: foo: 101, bar: 201
NOTICE: foo: 102, bar: 202
NOTICE: foo: 103, bar: 203
NOTICE: foo: 104, bar: 204

query II rowsort
SELECT * FROM xy;
----
1    2
3    4
100  200
101  201
102  202
103  203
104  204

# If there are more INTO targets than output columns, the left over variables
# are set to NULL.
statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
    j INT;
  BEGIN
    SELECT x INTO i, j FROM xy ORDER BY x DESC;
    RAISE NOTICE 'i = %', i;
    IF j IS NULL THEN
      RAISE NOTICE 'j is null!';
    END IF;
    RETURN 0;
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
SELECT f();
----
NOTICE: i = 104
NOTICE: j is null!

# If there are less INTO targets than output columns, the left over columns
# are ignored.
statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x, y INTO i FROM xy ORDER BY x DESC;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
104

query II rowsort
SELECT * FROM xy;
----
1    2
3    4
100  200
101  201
102  202
103  203
104  204

# It is possible to SELECT INTO multiple targets.
statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
    j INT;
  BEGIN
    SELECT x, y INTO i, j FROM xy ORDER BY x DESC;
    RETURN i + j;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
308

# It is possible to reference a previous value of a target variable in a
# SELECT INTO statement.
statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO i FROM xy ORDER BY x DESC;
    RAISE NOTICE 'i = %', i;
    SELECT x INTO i FROM xy WHERE x < i ORDER BY x DESC;
    RAISE NOTICE 'i = %', i;
    RETURN 0;
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
SELECT f();
----
NOTICE: i = 104
NOTICE: i = 103

# When the SQL statement returns zero rows, the target variables are all set
# to NULL.
statement ok
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT := 0;
  BEGIN
    RAISE NOTICE 'i = %', i;
    SELECT x INTO i FROM xy WHERE False;
    RAISE NOTICE 'i = %', i;
    i := 1;
    RAISE NOTICE 'i = %', i;
    INSERT INTO xy (SELECT 1, 2 FROM generate_series(1, 0)) RETURNING x INTO i;
    RAISE NOTICE 'i = %', i;
    RETURN 0;
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
SELECT f();
----
NOTICE: i = 0
NOTICE: i = <NULL>
NOTICE: i = 1
NOTICE: i = <NULL>

statement error pgcode 0A000 pq: unimplemented: duplicate INTO target
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x, y INTO i, i FROM xy ORDER BY x DESC;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

subtest strict

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO STRICT i FROM xy ORDER BY x DESC LIMIT 1;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
104

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO STRICT i FROM xy WHERE False;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode P0002 pq: query returned no rows
SELECT f();

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO STRICT i FROM xy ORDER BY x DESC;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode P0003 pq: query returned more than one row
SELECT f();

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    INSERT INTO xy VALUES (105, 105) RETURNING x INTO STRICT i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
105

query I
SELECT count(*) FROM xy;
----
8

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    INSERT INTO xy (SELECT * FROM xy WHERE False) RETURNING x INTO STRICT i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode P0002 pq: query returned no rows
SELECT f();

query I
SELECT count(*) FROM xy;
----
8

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    INSERT INTO xy VALUES (106, 106), (107, 107) RETURNING x INTO STRICT i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode P0003 pq: query returned more than one row
SELECT f();

query I
SELECT count(*) FROM xy;
----
8

subtest setting

# The plpgsql_use_strict_into setting causes INTO to behave as if strict was
# specified.
statement ok
SET plpgsql_use_strict_into = true;

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO i FROM xy ORDER BY x DESC LIMIT 1;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
105

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO i FROM xy WHERE False;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode P0002 pq: query returned no rows
SELECT f();

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    SELECT x INTO i FROM xy ORDER BY x DESC;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode P0003 pq: query returned more than one row
SELECT f();

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    INSERT INTO xy VALUES (106, 106) RETURNING x INTO i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
106

query I
SELECT count(*) FROM xy;
----
9

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    INSERT INTO xy (SELECT * FROM xy WHERE False) RETURNING x INTO i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode P0002 pq: query returned no rows
SELECT f();

query I
SELECT count(*) FROM xy;
----
9

statement ok
DROP FUNCTION f();
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    i INT;
  BEGIN
    INSERT INTO xy VALUES (107, 107), (108, 108) RETURNING x INTO i;
    RETURN i;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode P0003 pq: query returned more than one row
SELECT f();

query I
SELECT count(*) FROM xy;
----
9

statement ok
RESET plpgsql_use_strict_into;

subtest end
