Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-17253

Oracle compatibility: The REVERSE key word for FOR loop behaves incorrectly

Details

    • Bug
    • Status: Closed (View Workflow)
    • Critical
    • Resolution: Fixed
    • 10.3.9
    • 10.3.11
    • Parser
    • None
    • Centos 7

    Description

      Release tested: 10.3.9

      The following loop did not produce any output. It worked correctly without the REVERSE key word.

      SET SQL_MODE=ORACLE;
        delimiter //
        #
        DECLARE
           totalprice DECIMAL(12,2):=NULL;
           loop_start INTEGER := 1;
        BEGIN
           FOR idx IN REVERSE loop_start..10 LOOP
              SELECT idx;
           END LOOP;
        END;
        //
      

      With the REVERSE key word, switching the lower and upper bound values also worked:

      FOR idx IN REVERSE 10..loop_start LOOP

      However, this behavior is different from Oracle's documentation below.

      ---------
      If REVERSE is specified:

      If upper_bound is less than lower_bound, the statements do not run, and control transfers to the statement after the FOR LOOP statement.
      ---------

      Attachments

        Activity

          bar Alexander Barkov added a comment - - edited

          Sorry, can you clarify please?
          In the above script you have a REVERSE loop and direct bound order. It performs no iterations and produces no output.
          This looks correct.

          I just checked all four combinations and I think they give correct results:

          Direct loop, direct bound order (3 iterations as expected)

          SET SQL_MODE=ORACLE;
          delimiter //
          #
          DECLARE
            totalprice DECIMAL(12,2):=NULL;
            loop_start INTEGER := 1;
          BEGIN
            FOR idx IN loop_start..3 LOOP
              SELECT idx;
            END LOOP;
          END;
          //
          delimiter ;
          

          +-----+
          | idx |
          +-----+
          |   1 |
          +-----+
          1 row in set (0.01 sec)
           
          +-----+
          | idx |
          +-----+
          |   2 |
          +-----+
          1 row in set (0.01 sec)
           
          +-----+
          | idx |
          +-----+
          |   3 |
          +-----+
          1 row in set (0.01 sec)
           
          Query OK, 0 rows affected (0.00 sec)
          

          Direct loop, reverse bound order (no iterations as expected)

          SET SQL_MODE=ORACLE;
          delimiter //
          #
          DECLARE
            totalprice DECIMAL(12,2):=NULL;
            loop_start INTEGER := 1;
          BEGIN
            FOR idx IN 3..loop_start LOOP
              SELECT idx;
            END LOOP;
          END;
          //
          delimiter ;
          

          Query OK, 0 rows affected (0.00 sec)
          

          Reverse loop, reverse bound order (3 iterations as expected)

          SET SQL_MODE=ORACLE;
          delimiter //
          #
          DECLARE
            totalprice DECIMAL(12,2):=NULL;
            loop_start INTEGER := 1;
          BEGIN
            FOR idx IN REVERSE 3..loop_start LOOP
              SELECT idx;
            END LOOP;
          END;
          //
          delimiter ;
          

          +-----+
          | idx |
          +-----+
          |   3 |
          +-----+
          1 row in set (0.00 sec)
           
          +-----+
          | idx |
          +-----+
          |   2 |
          +-----+
          1 row in set (0.00 sec)
           
          +-----+
          | idx |
          +-----+
          |   1 |
          +-----+
          1 row in set (0.00 sec)
           
          Query OK, 0 rows affected (0.00 sec)
          

          Reverse loop, direct bound order (no iterations as expected)

          SET SQL_MODE=ORACLE;
          delimiter //
          #
          DECLARE
            totalprice DECIMAL(12,2):=NULL;
            loop_start INTEGER := 1;
          BEGIN
            FOR idx IN REVERSE loop_start..3 LOOP
              SELECT idx;
            END LOOP;
          END;
          //
          delimiter ;
          

          Query OK, 0 rows affected (0.00 sec)
          

          bar Alexander Barkov added a comment - - edited Sorry, can you clarify please? In the above script you have a REVERSE loop and direct bound order. It performs no iterations and produces no output. This looks correct. I just checked all four combinations and I think they give correct results: Direct loop, direct bound order (3 iterations as expected) SET SQL_MODE=ORACLE; delimiter // # DECLARE totalprice DECIMAL (12,2):= NULL ; loop_start INTEGER := 1; BEGIN FOR idx IN loop_start..3 LOOP SELECT idx; END LOOP; END ; // delimiter ; +-----+ | idx | +-----+ | 1 | +-----+ 1 row in set (0.01 sec)   +-----+ | idx | +-----+ | 2 | +-----+ 1 row in set (0.01 sec)   +-----+ | idx | +-----+ | 3 | +-----+ 1 row in set (0.01 sec)   Query OK, 0 rows affected (0.00 sec) Direct loop, reverse bound order (no iterations as expected) SET SQL_MODE=ORACLE; delimiter // # DECLARE totalprice DECIMAL(12,2):=NULL; loop_start INTEGER := 1; BEGIN FOR idx IN 3..loop_start LOOP SELECT idx; END LOOP; END; // delimiter ; Query OK, 0 rows affected (0.00 sec) Reverse loop, reverse bound order (3 iterations as expected) SET SQL_MODE=ORACLE; delimiter // # DECLARE totalprice DECIMAL (12,2):= NULL ; loop_start INTEGER := 1; BEGIN FOR idx IN REVERSE 3..loop_start LOOP SELECT idx; END LOOP; END ; // delimiter ; +-----+ | idx | +-----+ | 3 | +-----+ 1 row in set (0.00 sec)   +-----+ | idx | +-----+ | 2 | +-----+ 1 row in set (0.00 sec)   +-----+ | idx | +-----+ | 1 | +-----+ 1 row in set (0.00 sec)   Query OK, 0 rows affected (0.00 sec) Reverse loop, direct bound order (no iterations as expected) SET SQL_MODE=ORACLE; delimiter // # DECLARE totalprice DECIMAL (12,2):= NULL ; loop_start INTEGER := 1; BEGIN FOR idx IN REVERSE loop_start..3 LOOP SELECT idx; END LOOP; END ; // delimiter ; Query OK, 0 rows affected (0.00 sec)

          The following is from the oracle documentation. If I am reading it correctly, your example 3 above should not produce results, while example 4 should.

          Thx

          https://docs.oracle.com/database/121/LNPLS/for_loop_statement.htm#LNPLS1515

          [ REVERSE ] lower_bound .. upper_bound

          lower_bound and upper_bound must evaluate to numbers (see "Lower Bound and Upper Bound"). PL/SQL evaluates lower_bound and upper_bound once, when the FOR LOOP statement is entered, and stores them as temporary PLS_INTEGER values, rounding them to the nearest integer if necessary.

          If lower_bound equals upper_bound, the statements run only once.

          If lower_bound does not equal upper_bound when the FOR LOOP statement begins to run, then:

          If REVERSE is omitted:

          If lower_bound is greater than upper_bound, the statements do not run, and control transfers to the statement after the FOR LOOP statement.

          Otherwise, lower_bound is assigned to index, the statements run, and control returns to the top of the loop, where index is compared to upper_bound. If index is less than upper_bound, index is incremented by one, the statements run again, and control returns to the top of the loop. When index is greater than upper_bound, control transfers to the statement after the FOR LOOP statement.

          If REVERSE is specified:

          If upper_bound is less than lower_bound, the statements do not run, and control transfers to the statement after the FOR LOOP statement.

          Otherwise, upper_bound is assigned to index, the statements run, and control returns to the top of the loop, where index is compared to lower_bound. If index is greater than lower_bound, index is decremented by one, the statements run again, and control returns to the top of the loop. When index is less than lower_bound, control transfers to the statement after the FOR LOOP statement.

          dleeyh Daniel Lee (Inactive) added a comment - The following is from the oracle documentation. If I am reading it correctly, your example 3 above should not produce results, while example 4 should. Thx https://docs.oracle.com/database/121/LNPLS/for_loop_statement.htm#LNPLS1515 [ REVERSE ] lower_bound .. upper_bound lower_bound and upper_bound must evaluate to numbers (see "Lower Bound and Upper Bound"). PL/SQL evaluates lower_bound and upper_bound once, when the FOR LOOP statement is entered, and stores them as temporary PLS_INTEGER values, rounding them to the nearest integer if necessary. If lower_bound equals upper_bound, the statements run only once. If lower_bound does not equal upper_bound when the FOR LOOP statement begins to run, then: If REVERSE is omitted: If lower_bound is greater than upper_bound, the statements do not run, and control transfers to the statement after the FOR LOOP statement. Otherwise, lower_bound is assigned to index, the statements run, and control returns to the top of the loop, where index is compared to upper_bound. If index is less than upper_bound, index is incremented by one, the statements run again, and control returns to the top of the loop. When index is greater than upper_bound, control transfers to the statement after the FOR LOOP statement. If REVERSE is specified: If upper_bound is less than lower_bound, the statements do not run, and control transfers to the statement after the FOR LOOP statement. Otherwise, upper_bound is assigned to index, the statements run, and control returns to the top of the loop, where index is compared to lower_bound. If index is greater than lower_bound, index is decremented by one, the statements run again, and control returns to the top of the loop. When index is less than lower_bound, control transfers to the statement after the FOR LOOP statement.
          bar Alexander Barkov added a comment - - edited

          Right, Oracle returns the following output:

          SET SERVEROUTPUT ON;
          DROP PROCEDURE p1;
          CREATE PROCEDURE p1 AS
            loop_start INTEGER := 1;
          BEGIN
            FOR idx IN REVERSE 3..loop_start LOOP
              DBMS_OUTPUT.PUT_LINE('idx='||idx);
            END LOOP;
          END;
          /
          CALL p1();
          

          Call completed.
          

          SET SERVEROUTPUT ON;
          DROP PROCEDURE p1;
          CREATE PROCEDURE p1 AS
            loop_start INTEGER := 1;
          BEGIN
            FOR idx IN REVERSE loop_start..3 LOOP
              DBMS_OUTPUT.PUT_LINE('idx='||idx);
            END LOOP;
          END;
          /
          CALL p1();
          

          idx=3
          idx=2
          idx=1
          Call completed.
          

          Sorry, I overlooked this.

          Will fix MariaDB accordingly. Thanks for clarification.

          bar Alexander Barkov added a comment - - edited Right, Oracle returns the following output: SET SERVEROUTPUT ON ; DROP PROCEDURE p1; CREATE PROCEDURE p1 AS loop_start INTEGER := 1; BEGIN FOR idx IN REVERSE 3..loop_start LOOP DBMS_OUTPUT.PUT_LINE( 'idx=' ||idx); END LOOP; END ; / CALL p1(); Call completed. SET SERVEROUTPUT ON ; DROP PROCEDURE p1; CREATE PROCEDURE p1 AS loop_start INTEGER := 1; BEGIN FOR idx IN REVERSE loop_start..3 LOOP DBMS_OUTPUT.PUT_LINE( 'idx=' ||idx); END LOOP; END ; / CALL p1(); idx=3 idx=2 idx=1 Call completed. Sorry, I overlooked this. Will fix MariaDB accordingly. Thanks for clarification.

          Forgot to close. Was fixed in 10.3.11 on Nov 13.

          bar Alexander Barkov added a comment - Forgot to close. Was fixed in 10.3.11 on Nov 13.

          People

            bar Alexander Barkov
            dleeyh Daniel Lee (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.