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

SNOW-1045932: Can I use parameters with a begin/end block with .Net Client? #860

Closed
Coder3333 opened this issue Feb 7, 2024 · 6 comments
Assignees
Labels
status-triage_done Initial triage done, will be further handled by the driver team

Comments

@Coder3333
Copy link

I am using the .Net client to execute Snowflake queries. This works fine when I only try to execute a single sql statement with a parameter.

 SELECT TOP 1 Col1     
 FROM Table1 WHERE Col2 = :p1; 

If I remove the parameter and move the query into a begin/end block, that works, too.

 begin
  SELECT TOP 1 Col1     
 FROM Table1; 
 
 return 'value';
 end

However, if I use a begin/end block and keep the parameter, I get an error about the parameter being undefined.

begin

 SELECT TOP 1 Col1     
 FROM Table1 WHERE Col2 = :p1; 

  return 'value';
end;

Should I expect this scenario to work with a parameter in a begin/end block, or is there some reason why this should not work?

@Coder3333 Coder3333 added the bug label Feb 7, 2024
@github-actions github-actions bot changed the title Can I use parameters with a begin/end block with .Net Client? SNOW-1045932: Can I use parameters with a begin/end block with .Net Client? Feb 7, 2024
@sfc-gh-dszmolka sfc-gh-dszmolka added status-triage Issue is under initial triage and removed bug labels Feb 9, 2024
@sfc-gh-dszmolka sfc-gh-dszmolka self-assigned this Feb 9, 2024
@sfc-gh-dszmolka
Copy link
Contributor

hi, thank you for submitting this issue - we'll take a look

@sfc-gh-dszmolka
Copy link
Contributor

As a reproduction attempt, did the following

CREATE OR REPLACE PROCEDURE test(input INT)
  RETURNS FLOAT
  LANGUAGE SQL
  AS
    DECLARE
      result INT;
    BEGIN
      SELECT ID into :result FROM net_gh_860 WHERE ID >= :input;

      return result;
    END;

then

using System;
using System.Data;
using System.Data.Common;
using Snowflake.Data.Client;
namespace SnowflakeTestProgram
{
    class Program
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private static DataTable dbdataTable;
        
        static void Main(string[] args)
        {
            try
            {
                using (IDbConnection conn = new SnowflakeDbConnection())
                {
                    conn.ConnectionString = "account=myaccount.eu-central-1; user=admin; password=password; DB=TEST_DB; SCHEMA=DOTNET; warehouse=COMPUTE_WH";
                    conn.Open();
                    Console.WriteLine("Connection successful!");
                    
                    using (IDbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = "CALL TEST_DB.PUBLIC.TEST(:1);";
                        var p1 = cmd.CreateParameter();
                        p1.ParameterName = "1";
                        p1.Value = 2;
                        p1.DbType = DbType.Int32;
                        cmd.Parameters.Add(p1);
                        IDataReader reader = cmd.ExecuteReader();
                        while (reader.Read())
                        {
                                Console.WriteLine(reader.GetString(0));

                        }
                        conn.Close();
                    }
                }
            }
            catch (DbException exc)
            {
                Console.WriteLine("Error Message: {0}", exc.Message);
            }
        }
    }
}

properly returns 2 which is the only row in the underlying test table.

Perhaps you're trying to do something entirely different here. If so, could you please provide a detailed reproduction or a runnable script which leads to the issue ? I would like to see if it's a limitation in the Snowflake .NET driver, or the backend itself.

Thank you in advance !

@sfc-gh-dszmolka sfc-gh-dszmolka added the status-information_needed Additional information is required from the reporter label Feb 12, 2024
@Coder3333
Copy link
Author

@sfc-gh-dszmolka , modify your test in this way.

 using (IDbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = @"DECLARE
      result INT;
    BEGIN
      SELECT ID into :result FROM net_gh_860 WHERE ID >= :input;

      return result;
    END;";
                        var p1 = cmd.CreateParameter();
                        p1.ParameterName = "1";
                        p1.Value = 2;
                        p1.DbType = DbType.Int32;
                        cmd.Parameters.Add(p1);
                        IDataReader reader = cmd.ExecuteReader();
                        while (reader.Read())
                        {
                                Console.WriteLine(reader.GetString(0));

                        }
                        conn.Close();
                    }

Begin/End blocks run just fine as long as there are no parameters. How can I get this to run with a parameter? I am trying to avoid writing a full out procedure that has to be deployed.

@sfc-gh-dszmolka
Copy link
Contributor

okay, got it - thank you! Got to this point:

                        cmd.CommandText = @"
    DECLARE
      INPUT INT;
      RESULT INT;
    BEGIN
      SELECT ID into :RESULT FROM net_gh_860 WHERE ID >= :INPUT;

      return :RESULT;
    END;
    ";

                        var p1 = cmd.CreateParameter();
                        p1.ParameterName = "INPUT";
                        p1.Value = 1;
                        p1.DbType = DbType.Int32;
                        cmd.Parameters.Add(p1);
                        IDataReader reader = cmd.ExecuteReader();

Which does not error out, but does not return anything either. On the Snowflake side, the query is translated into
SELECT ID FROM net_gh_860 WHERE ID >= :INPUT::NUMBER(38,0);

Parameter doesn't seem to be passed. Similarly, this does not even seem to be possible with another Snowflake driver, the Python Connector. Also returns empty result.

You'll need to either create it as a procedure after all, or if thats possible, perhaps dynamically generate the whole anonymous function (+its parameter values) during runtime.

@sfc-gh-dszmolka sfc-gh-dszmolka added status-triage_done Initial triage done, will be further handled by the driver team and removed status-triage Issue is under initial triage status-information_needed Additional information is required from the reporter labels Feb 12, 2024
@Coder3333
Copy link
Author

@sfc-gh-dszmolka , right now I am just building the query dynamically as a work around, but I wish there was a way to ensure I am not susceptible to sql injection, which parameters would provide.

@sfc-gh-dszmolka
Copy link
Contributor

Good to know that at least you have a workaround here ! I'm now closing this issue since it is not related to the .NET driver as it looks like now but looks like a backend limitation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status-triage_done Initial triage done, will be further handled by the driver team
Projects
None yet
Development

No branches or pull requests

2 participants