diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs b/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs index bbeb7ff3724..49dc290f878 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs @@ -320,7 +320,7 @@ private IEnumerable GetStatements() ? PrepareAndEnumerateStatements() : _preparedStatements) { - var boundParams = _parameters?.Bind(stmt) ?? 0; + var boundParams = _parameters?.Bind(stmt, Connection!.Handle!) ?? 0; if (expectedParams != boundParams) { diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteParameter.cs b/src/Microsoft.Data.Sqlite.Core/SqliteParameter.cs index e71073b8300..113d490ef6c 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteParameter.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteParameter.cs @@ -203,7 +203,7 @@ public virtual void ResetSqliteType() _sqliteType = null; } - internal bool Bind(sqlite3_stmt stmt) + internal bool Bind(sqlite3_stmt stmt, sqlite3 handle) { if (string.IsNullOrEmpty(ParameterName)) { @@ -222,7 +222,7 @@ internal bool Bind(sqlite3_stmt stmt) throw new InvalidOperationException(Resources.RequiresSet(nameof(Value))); } - new SqliteParameterBinder(stmt, index, _value, _size, _sqliteType).Bind(); + new SqliteParameterBinder(stmt, handle, index, _value, _size, _sqliteType).Bind(); return true; } diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteParameterBinder.cs b/src/Microsoft.Data.Sqlite.Core/SqliteParameterBinder.cs index ffbaefafcef..214bdb3e3cc 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteParameterBinder.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteParameterBinder.cs @@ -10,13 +10,15 @@ namespace Microsoft.Data.Sqlite internal class SqliteParameterBinder : SqliteValueBinder { private readonly sqlite3_stmt _stmt; + private readonly sqlite3 _handle; private readonly int _index; private readonly int? _size; - public SqliteParameterBinder(sqlite3_stmt stmt, int index, object value, int? size, SqliteType? sqliteType) + public SqliteParameterBinder(sqlite3_stmt stmt, sqlite3 handle, int index, object value, int? size, SqliteType? sqliteType) : base(value, sqliteType) { _stmt = stmt; + _handle = handle; _index = index; _size = size; } @@ -30,26 +32,43 @@ protected override void BindBlob(byte[] value) Array.Copy(value, blob, _size.Value); } - sqlite3_bind_blob(_stmt, _index, blob); + var rc = sqlite3_bind_blob(_stmt, _index, blob); + SqliteException.ThrowExceptionForRC(rc, _handle); } protected override void BindDoubleCore(double value) - => sqlite3_bind_double(_stmt, _index, value); + { + var rc = sqlite3_bind_double(_stmt, _index, value); + + SqliteException.ThrowExceptionForRC(rc, _handle); + } protected override void BindInt64(long value) - => sqlite3_bind_int64(_stmt, _index, value); + { + var rc = sqlite3_bind_int64(_stmt, _index, value); + + SqliteException.ThrowExceptionForRC(rc, _handle); + } protected override void BindNull() - => sqlite3_bind_null(_stmt, _index); + { + var rc = sqlite3_bind_null(_stmt, _index); + + SqliteException.ThrowExceptionForRC(rc, _handle); + } protected override void BindText(string value) - => sqlite3_bind_text( + { + var rc = sqlite3_bind_text( _stmt, _index, ShouldTruncate(value.Length) ? value.Substring(0, _size!.Value) : value); + SqliteException.ThrowExceptionForRC(rc, _handle); + } + private bool ShouldTruncate(int length) => _size.HasValue && length > _size.Value diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteParameterCollection.cs b/src/Microsoft.Data.Sqlite.Core/SqliteParameterCollection.cs index 6ec9439a7a3..5688af4f6d0 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteParameterCollection.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteParameterCollection.cs @@ -325,12 +325,12 @@ protected override void SetParameter(int index, DbParameter value) protected override void SetParameter(string parameterName, DbParameter value) => SetParameter(IndexOfChecked(parameterName), value); - internal int Bind(sqlite3_stmt stmt) + internal int Bind(sqlite3_stmt stmt, sqlite3 handle) { var bound = 0; foreach (var parameter in _parameters) { - if (parameter.Bind(stmt)) + if (parameter.Bind(stmt, handle)) { bound++; } diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs index 02a7951defe..aab6d37a187 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs @@ -15,6 +15,62 @@ namespace Microsoft.Data.Sqlite; public class SqliteCommandTest { + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Correct_error_code_is_returned_when_parameter_is_too_long(bool async) // Issue #27597 + { + using var connection = new SqliteConnection("Data Source=:memory:"); + + if (async) + { + await connection.OpenAsync(); + } + else + { + connection.Open(); + } + + using var command = connection.CreateCommand(); + command.CommandText = """ +CREATE TABLE "Products" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Products" PRIMARY KEY AUTOINCREMENT, + "Name" TEXT NOT NULL + ); +"""; + _ = async ? await command.ExecuteNonQueryAsync() : command.ExecuteNonQuery(); + + sqlite3_limit(connection.Handle!, 0, 10); + + command.CommandText = @"INSERT INTO ""Products"" (""Name"") VALUES (@p0);"; + command.Parameters.Add("@p0", SqliteType.Text); + command.Parameters[0].Value = new string('A', 15); + + try + { + _ = async ? await command.ExecuteReaderAsync() : command.ExecuteReader(); + } + catch (SqliteException ex) + { + Assert.Equal(18, ex.SqliteErrorCode); + } + finally + { +#if NET5_0_OR_GREATER + if (async) + { + await connection.CloseAsync(); + } + else + { + connection.Close(); + } +#else + connection.Close(); +#endif + } + } + [Fact] public void Ctor_sets_values() {