[MDEV-14164] Unknown column error when adding aggregate to function in oracle style procedure FOR loop Created: 2017-10-27  Updated: 2020-08-25  Resolved: 2017-11-09

Status: Closed
Project: MariaDB Server
Component/s: Stored routines
Affects Version/s: 10.0, 10.1, 10.2, 10.3
Fix Version/s: 10.0.34, 10.1.29, 10.2.11, 10.3.3

Type: Bug Priority: Critical
Reporter: Hartmut Holzgraefe Assignee: Oleksandr Byelkin
Resolution: Fixed Votes: 0
Labels: None

Sprint: 10.2.11

 Description   

Using this simple setup ...

SET SQL_MODE='ORACLE';
 
CREATE TABLE t1(
  id int primary key,
  val int
);

... the following query works fine when used standalone ...

SELECT * FROM (
  SELECT id, SUM(val) as sum_val FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                     WHERE a.id = b.id
              ) 
;

... but not when used in a procedure / FOR loop context ...

delimiter //
 
CREATE PROCEDURE p1
AS
BEGIN
FOR rec in (
SELECT * FROM (
  SELECT id, SUM(val) as sum_val FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                     WHERE a.id = b.id
               ) 
)
LOOP
SELECT 1;
END LOOP;
END;
//
DELIMITER ;

... resulting in ...

mysql> CALL p1();
ERROR 1054 (42S22): Unknown column 'a.id' in 'where clause'

Removing just the SUM() aggregate part makes the query/procedure work:

delimiter //
 
CREATE PROCEDURE p2
AS
BEGIN
FOR rec in (
SELECT * FROM (
  SELECT id FROM t1) a 
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                     WHERE a.id = b.id
               ) 
)
LOOP
SELECT 2;
END LOOP;
END;
//
 
DELIMITER ;

mysql> CALL p2();
Query OK, 0 rows affected (0,00 sec)



 Comments   
Comment by Alexander Barkov [ 2017-11-07 ]

The same problem is repeatable on an attempt to open the same cursor two times, without a cursor FOR loop.

SET sql_mode=ORACLE;
DELIMITER //
CREATE OR REPLACE PROCEDURE p1
AS
  CURSOR cur1 IS SELECT * FROM (
  SELECT id, SUM(val) as sum_val FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                    WHERE a.id=b.id);
BEGIN
  OPEN cur1;
  CLOSE cur1;
  OPEN cur1;
  CLOSE cur1;
END;
//
DELIMITER ;
CALL p1();

The first sp_instr_copen::exec_core() works fine, but the second one fails.

Comment by Alexander Barkov [ 2017-11-07 ]

The same problem is repeatable with sql_mode=DEFAULT in 10.0, 10.1, 10.2, 10.3.

SET sql_mode=DEFAULT;
CREATE OR REPLACE TABLE t1(id INT, val INT);
DROP PROCEDURE IF EXISTS p1;
DELIMITER //
CREATE PROCEDURE p1()
BEGIN
  DECLARE cur1 CURSOR FOR SELECT * FROM (
  SELECT id, SUM(val) as sum_val FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                    WHERE a.id=b.id);
  OPEN cur1;
  CLOSE cur1;
  OPEN cur1;
  CLOSE cur1;
END;
//
DELIMITER ;
CALL p1();

Also repeatable with COUNT(val) and COUNT( * ) instead of SUM(val).

Note, if I remove the second pair of OPEN cur1 and CLOSE cur1, it works fine.

Comment by Alexander Barkov [ 2017-11-07 ]

Also repeatable if I remove SUM from the inner SELECT, but add DISTINCT to id instead:

SET sql_mode=DEFAULT;
CREATE OR REPLACE TABLE t1(id INT, val INT);
DROP PROCEDURE IF EXISTS p1;
DELIMITER //
CREATE PROCEDURE p1()
BEGIN
  DECLARE cur1 CURSOR FOR SELECT * FROM (
  SELECT DISTINCT id FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                    WHERE a.id=b.id);
  OPEN cur1;
  CLOSE cur1;
  OPEN cur1;
  CLOSE cur1;
END;
//
DELIMITER ;
CALL p1();

Comment by Alexander Barkov [ 2017-11-07 ]

The same problem is repeatable without a CURSOR, if I do a direct SELECT inside the SP and call the SP two times:

SET sql_mode=DEFAULT;
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(id INT, val INT);
DROP PROCEDURE IF EXISTS p1;
DELIMITER //
CREATE PROCEDURE p1()
BEGIN
  SELECT * FROM (
  SELECT DISTINCT id FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                    WHERE a.id=b.id);
END;
//
DELIMITER ;
CALL p1();
CALL p1();

Notice, the first CALL works fine, while the second CALL fails on the same error:

ERROR 1054 (42S22): Unknown column 'a.id' in 'where clause'

Note, if I change the equality a.id=b.id to some other comparison predicate, e.g. to:

  • a.id<=>b.id
  • a.id<>b.id
  • a.id<b.id
  • a.id>b.id

it starts to work fine.

Comment by Alexander Barkov [ 2017-11-07 ]

If I issue this command:

SET optimizer_switch='exists_to_in=off';

all problems disappear.

Comment by Oleksandr Byelkin [ 2017-11-07 ]

Test suite in format of test suite put here to avoid fixing it each time

CREATE TABLE t1(id INT, val INT);
DELIMITER //;
CREATE PROCEDURE p1()
BEGIN
  DECLARE cur1 CURSOR FOR SELECT * FROM (
  SELECT DISTINCT id FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                    WHERE a.id=b.id);
  OPEN cur1;
  CLOSE cur1;
  OPEN cur1;
  CLOSE cur1;
END;
//
DELIMITER ;//
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;

Comment by Oleksandr Byelkin [ 2017-11-07 ]

cause of the problem is <exists outer expr> which is not supposed to be reprepared but it is...

Comment by Oleksandr Byelkin [ 2017-11-07 ]

I was not right, the problem is in the field the <exists outer expr> refer

Comment by Oleksandr Byelkin [ 2017-11-07 ]

The problem is that name resolution context (made during pullout) refer on not opened table (alias b)

Comment by Oleksandr Byelkin [ 2017-11-07 ]

CREATE TABLE t1(id INT, val INT);
DELIMITER //;
CREATE PROCEDURE p1()
BEGIN
  SELECT * FROM (
  SELECT DISTINCT id FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM ( SELECT id FROM t1) b
                    WHERE a.id=b.id);
END;
//
DELIMITER ;//
CALL p1();
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;

Comment by Oleksandr Byelkin [ 2017-11-07 ]

It do not depend on derived tables:

CREATE TABLE t1(id INT, val INT);
DELIMITER //;
CREATE PROCEDURE p1()
BEGIN
  SELECT * FROM (
  SELECT DISTINCT id FROM t1) a
  WHERE NOT EXISTS (SELECT * FROM t1 b WHERE a.id=b.id);
END;
//
DELIMITER ;//
CALL p1();
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;

Comment by Oleksandr Byelkin [ 2017-11-07 ]

oops, above I removed not derived which causes problem

Actually distinct just make it non mergible, so problem is that there is materialazed derived and it is not processed

Comment by Oleksandr Byelkin [ 2017-11-07 ]

Converted to IN manually it works:

  SELECT * FROM (SELECT DISTINCT id FROM t1) a
  WHERE NOT a.id IN (SELECT b.id FROM t1 b);

Comment by Oleksandr Byelkin [ 2017-11-07 ]

Temporary table looks like created, but somehow Item points in other place

Comment by Oleksandr Byelkin [ 2017-11-08 ]

Name resolution context of a.id field refers to table b (which is OK, because it was in the inner query) but upper name resolution context set to NULL which prevent resolving.

Comment by Oleksandr Byelkin [ 2017-11-08 ]

Name resolution created during "pull-out". It advance next name resolution but uses original table list...

Comment by Oleksandr Byelkin [ 2017-11-08 ]

Try to use table list of the place where it was break other tests.

Comment by Oleksandr Byelkin [ 2017-11-08 ]

The problem is that fix_after_pullout() used for 2 purposes:
1) converting from semijoin and it should kind of "keep" context, but remove one level in previous context
2) pulling out of subquery (should keep context completely or move everything up)

Comment by Oleksandr Byelkin [ 2017-11-08 ]

github tree: bb-10.0-MDEV-14164

revision-id: cfb314a13b4187e8bb2d3b2ba0f48270e07c61be (mariadb-10.0.33-10-gcfb314a13b4)
parent(s): 6a524fcfdde539c6448aa4126ccb5ed79055b9ce
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2017-11-08 15:47:49 +0100
message:

MDEV-14164: Unknown column error when adding aggregate to function in oracle style procedure FOR loop

Make differentiation between pullout for merge and pulout of outer field during exists2in transformation.
In last case the field was outer and so we can safely starrt from name resolution context of the SELECT where it was pulled.
Old behaviour lead to inconsistence between list of tables and outer name resolution context (which skipps one SELECT for merge purposes) which cretes problem of name resolution.

Generated at Thu Feb 08 08:11:25 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.