DataAction / AdoNetCore.AseClient

AdoNetCore.AseClient - a .NET Core DB Provider for SAP ASE
Apache License 2.0
106 stars 44 forks source link

Error passing blobs to a stored procedure #209

Open jeroen-vh opened 3 years ago

jeroen-vh commented 3 years ago

Hello, first of all, thanks for creating this great project. I've been experimenting with this new library and testing some code I use to execute stored procedures. I'm having trouble when passing an argument to the stored procedure containing more than 16384 characters or bytes.

Describe the bug When passing an argument of the ASE type image or text to the stored procedure I get the following error from the ASE server when the argument contains a string or byte array larger than 16384 characters/bytes:

The token datastream length was not correct. This is an internal protocol error.

This works with the Sybase.AdoNet4.AseClient library version 4.157.1300.

To Reproduce Create a stored procedure that accepts a text argument:

create or replace procedure [dbo].[sp_test_text_type]
  @input text,
  @output int output
as
begin
  set @output = len(@input)
end

C# code:

    using (var connection = new AseConnection(connectionString))
    {
        connection.Open();
        using (var command = connection.CreateCommand())
        {
            command.CommandText = "sp_test_text_type";
            command.CommandType = CommandType.StoredProcedure;

            var input = new string('a', 20000);

            var pIn = command.CreateParameter();
            pIn.ParameterName = "@input";
            pIn.Value = input;
            pIn.AseDbType = AseDbType.Text;
            command.Parameters.Add(pIn);

            var pOut = command.CreateParameter();
            pOut.ParameterName = "@output";
            pOut.Value = DBNull.Value;
            pOut.DbType = DbType.Int32;
            pOut.Direction = ParameterDirection.Output;
            command.Parameters.Add(pOut);

            command.ExecuteNonQuery();
        }
    }

Debug output:

InternalConnectionFactory.GetNewConnection start

----------  Send packet   ----------
Write TDS_BUF_LOGIN
Write TDS_CAPABILITY

---------- Receive Tokens ----------
TDS_ENVCHANGE: TDS_ENV_CHARSET - iso_1 -> iso_1
INFO  [10] [L:0]: Changed client character set setting to 'iso_1'.
TDS_ENVCHANGE: TDS_ENV_DB - master -> lisdb
INFO  [10] [L:0]: Changed database context to 'lisdb'.
TDS_ENVCHANGE: TDS_ENV_LANG -  -> us_english
INFO  [10] [L:0]: Changed language setting to 'us_english'.
TDS_ENVCHANGE: TDS_ENV_PACKSIZE -  -> 2048
<- TDS_LOGINACK: TDS 5.0.0.0, ASE 16.0.3
Login success
<- TDS_DONE: TDS_DONE_FINAL (263)

----------  Send packet   ----------
Write TDS_BUF_NORMAL
-> TDS_OPTIONCMD: TDS_OPT_SET, TDS_OPT_TEXTSIZE, 00 80 00 00
---------- Receive Tokens ----------
<- TDS_DONE: TDS_DONE_FINAL (263)
TDS_DONE: TDS_DONE_FINAL

========== ExecuteNonQuery==========
Transaction set: False

----------  Send packet   ----------
Write TDS_BUF_NORMAL
-> TDS_DBRPC: sp_test_text_type
-> TDS_PARAMFMT2: 2 parameters
  -> @input: TDS_BLOB (20000) (ut:0)
  -> @output: TDS_INTN (4) (ut:0)
-> TDS_PARAMS: 2 parameters

---------- Receive Tokens ----------
ERROR [16] [L:0]: The token datastream length was not correct. This is an internal protocol error.
<- TDS_DONE: TDS_DONE_ERROR (263)
TDS_DONE: TDS_DONE_ERROR
<- TDS_RETURNSTATUS: -6
<- TDS_DONE: TDS_DONE_FINAL (263)
TDS_DONE: TDS_DONE_FINAL

A solution to get the above sample working is changing pIn.AseDbType = AseDbType.Text; to: pIn.DbType = DbType.String;

This results in 40000 bytes sent to the server with UserType 36:

-> TDS_PARAMFMT2: 2 parameters
  -> @input: TDS_BLOB (40000) (ut:36)
  -> @output: TDS_INTN (4) (ut:0)

and no error. I'm unable to create a similar solution for the image type.

Expected behavior Using the old Sybase .Net Framework library this results in the output argument returning 20000.

Environment

Additional context If wanted, I can also provide a sample for the image type bug using a byte array.