PL/SQL parser (MDEV-10142)

[MDEV-10581] sql_mode=ORACLE: Explicit cursor FOR LOOP Created: 2016-08-17  Updated: 2023-10-04  Resolved: 2017-03-10

Status: Closed
Project: MariaDB Server
Component/s: Parser, Stored routines
Affects Version/s: None
Fix Version/s: 10.3.0

Type: Technical task Priority: Major
Reporter: Alexander Barkov Assignee: Alexander Barkov
Resolution: Fixed Votes: 0
Labels: Compatibility

Issue Links:
Blocks
blocks MDEV-12098 sql_mode=ORACLE: Implicit cursor FOR ... Closed
is blocked by MDEV-10914 ROW data type for stored routine vari... Closed
is blocked by MDEV-12007 Allow ROW variables as a cursor FETCH... Closed
is blocked by MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in va... Closed
Problem/Incident
causes MDEV-32275 getting error 'Illegal parameter data... Closed
Relates
relates to MDEV-14415 Add Oracle-style FOR loop to sql_mode... Closed
relates to MDEV-16674 Document FOR loop Open
Sprint: 10.2.2-3, 10.2.2-1, 10.2.2-2, 10.2.2-4, 10.1.18

 Description   

Add support for explicit cursor FOR loops when running in sql_mode=ORACLE:

FOR rec IN cur
LOOP
  -- statements
END LOOP;

The benefits of this syntax is:

  • No declaration of the FOR variable rec is needed. The cursor FOR statement implicitly declares a record variable (i.e. a ROW type variable}} that has the same structure with the data returned by the cursor.
  • No needs to do OPEN or CLOSE for the cursor. It's automatically opened before the loop, and is closed after the loop.
  • No needs to do CLOSE from inside the FOR loop if one uses a GOTO statement from inside the loop. The cursor is closed automatically.
  • No needs to do an explicit FETCH. The next row is automatically fetched in the beginning of every iteration.
  • No needs to do an explicit EXIT to leave the loop when FETCH returned no data.

Example:

DECLARE
  CURSOR c1 IS
    SELECT last_name, job_id FROM employees
    WHERE job_id LIKE '%CLERK%' AND manager_id > 120
    ORDER BY last_name;
BEGIN
  FOR item IN c1
  LOOP
    DBMS_OUTPUT.PUT_LINE
      ('Name = ' || item.last_name || ', Job = ' || item.job_id);
  END LOOP;
END;
/

The fields of the implicitly declared record variable must be updatable:

DECLARE
  CURSOR cur IS
    SELECT 'Black' AS last_name, 10 AS job_id FROM DUAL UNION
    SELECT 'White' AS last_name, 20 AS job_id FROM DUAL;
BEGIN
  FOR rec IN cur
  LOOP
    rec.job_id:= rec.job_id + 100;
    DBMS_OUTPUT.PUT_LINE ('Name=' || rec.last_name || ', Job=' || rec.job_id);
  END LOOP;
END;
/

Internally, the explicit cursor FOR LOOP will be transparently translated to:

  • OPEN
  • Declaration of a %ROWTYPE variable
  • Simple LOOP with EXIT
  • CLOSE

So the block in the previous example will be effectively equal to:

DECLARE
  CURSOR cur IS
    SELECT 'Black' AS last_name, 10 AS job_id FROM DUAL UNION
    SELECT 'White' AS last_name, 20 AS job_id FROM DUAL;
BEGIN
  OPEN cur;
  DECLARE
    rec cur%ROWTYPE;
  BEGIN
    LOOP
      FETCH cur INTO rec;
      EXIT WHEN cur%NOTFOUND;
      rec.job_id:= rec.job_id + 100;
      DBMS_OUTPUT.PUT_LINE ('Name=' || rec.last_name || ', Job=' || rec.job_id);
    END LOOP;
    CLOSE cur;
  END;
END;
/



 Comments   
Comment by Alexander Barkov [ 2017-01-24 ]

See also:
WL#3309: Stored Procedures: FOR statement
https://dev.mysql.com/worklog/task/?id=3309

Comment by Alexander Barkov [ 2017-03-10 ]

Pushed to bb-10.2-compatibility

Comment by Robert Dyas [ 2018-06-30 ]

Is there any way we can use this particular syntax WITHOUT sql_mode=ORACLE ?

FOR rec IN cur
LOOP
  -- statements
END LOOP;

It would be very, very helpful if we could. Even if we had to set a special variable like sql_mode=ALLOW_FOR_IN_LOOP or something.

Comment by Alexander Barkov [ 2018-07-03 ]

Yes, FOR loops are possible without sql_mode=ORACLE, but using slightly a different syntax. See MDEV-14415 for details.

Generated at Thu Feb 08 07:43:19 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.