Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Declaration in Stored Procedure is used to prepend further statements => Error #44

Open
windiana42 opened this issue Dec 7, 2022 · 4 comments

Comments

@windiana42
Copy link

windiana42 commented Dec 7, 2022

Declarations in stored procedures can be defined using stored procedure arguments. Pytsql currently still copies them to prefix batches with isolate_top_level_statements=True which causes errors.
TSQL:

CREATE PROCEDURE CREATEALLDATES
    (
        @StartDate AS DATE, @EndDate AS DATE
    ) AS
    DECLARE @Current AS DATE = DATEADD(DD, 0, @StartDate); DROP TABLE IF EXISTS ##alldates CREATE TABLE ##alldates (
        dt DATE PRIMARY KEY
    ) WHILE @Current <= @EndDate BEGIN
    INSERT INTO ##alldates
    VALUES (@Current);
    SET @Current = DATEADD(DD, 1, @Current) -- add 1 to current day
END
GO
IF OBJECT_ID ( N'dbo.get_db_sampling_factor' , N'FN' ) IS NOT NULL DROP FUNCTION get_db_sampling_factor ;

Error:

sqlalchemy.exc.ProgrammingError: (pyodbc.ProgrammingError) ('42000', '[42000] [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Must declare the scalar variable "@StartDate". (137) (SQLExecDirectW)')
[SQL: DECLARE @Current AS DATE = DATEADD ( DD , 0 , @StartDate ) ; IF OBJECT_ID ( N'dbo.get_db_sampling_factor' , N'FN' ) IS NOT NULL DROP FUNCTION get_db_sampling_factor ;]
@windiana42
Copy link
Author

I already located the problem near tsql.py:115:

        if isinstance(node, tsqlParser.Declare_statementContext):
            self.dynamics.extend(result)

windiana42 added a commit that referenced this issue Dec 7, 2022
@windiana42
Copy link
Author

Here is a failing testcase: 58a61a7

@windiana42
Copy link
Author

windiana42 commented Dec 8, 2022

I am trying to reliably detect whether DECLARE is a top level statement. My current hypothesis is: a DECLARE statement is top level if the chain of parentCtx to the root (BatchContext) only includes Sql_clauseContext or Sql_clausesContext objects.

    @staticmethod
    def is_top_level_statement(node: ParserRuleContext):
        """Check wether node is a top level SQL statement."""
        cur = node.parentCtx
        while isinstance(cur, tsqlParser.Sql_clauseContext) or isinstance(cur, tsqlParser.Sql_clausesContext):
            cur = cur.parentCtx
        return isinstance(cur, tsqlParser.BatchContext)

@windiana42
Copy link
Author

Why is the expectation that this SQL code should result in @declare statement to be unconditionally copied?

def test_declaration_in_control_flow():
        seed = """
        IF 1 = 1
            DECLARE @A INT = 5
        SELECT * FROM x

If it is, we might have to parse the IF statement outcome:

        IF 1 = 0
            DECLARE @A INT = 5
        GO
        SELECT @A

The second with GO actually does not work in TSQL either. But this does:

DECLARE @A INT = 5
GO
SELECT @A

windiana42 added a commit that referenced this issue Dec 8, 2022
In case of isolate_top_level_statements=True, copy DECLARE statements to subsequent statements only if they are top level.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant