diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs b/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs index 41c014d5106..27becbfd17d 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs @@ -297,6 +297,14 @@ public virtual Stream GetStream(int ordinal) var blobDatabaseName = sqlite3_column_database_name(Handle, ordinal).utf8_to_string(); var blobTableName = sqlite3_column_table_name(Handle, ordinal).utf8_to_string(); + if (blobDatabaseName == null + || blobTableName == null) + { + // The value came from an expression, so there is no row to open a blob on. Looking for a rowid + // column would also hand these nulls to sqlite3_table_column_metadata, which is misuse + return new MemoryStream(GetCachedBlob(ordinal), false); + } + var rowidkey = $"{blobDatabaseName}_{blobTableName}"; if (!RowIds.TryGetValue(rowidkey, out var rowIdForOrdinal)) { diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs index 03f9cb81097..3e9ca184f53 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs @@ -413,6 +413,68 @@ public void GetStream_Blob_works(string createTableCmd, string selectCmd) Assert.Equal([0x01, 0x02, 0x03, 0x04], buffer); } + [Fact] + public void GetStream_works_when_expression_and_a_literal_in_the_same_query() + { + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + connection.ExecuteNonQuery( + "CREATE TABLE DataTable (Id INTEGER PRIMARY KEY, Data BLOB); INSERT INTO DataTable VALUES (5, X'01020304');"); + + var selectCommand = connection.CreateCommand(); + selectCommand.CommandText = "SELECT 42, substr(Data, 1, 3) FROM DataTable WHERE Id = 5"; + using var reader = selectCommand.ExecuteReader(); + Assert.True(reader.Read()); + + using var sourceStream = reader.GetStream(1); + Assert.IsType(sourceStream); + var buffer = new byte[3]; + var bytesRead = sourceStream.Read(buffer, 0, 3); + Assert.Equal(3, bytesRead); + Assert.Equal([0x01, 0x02, 0x03], buffer); + } + + [Fact] + public void GetTextReader_works_when_expression_and_a_literal_in_the_same_query() + { + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + connection.ExecuteNonQuery( + "CREATE TABLE DataTable (Id INTEGER PRIMARY KEY, Value TEXT); INSERT INTO DataTable VALUES (5, 'abcdef');"); + + var selectCommand = connection.CreateCommand(); + selectCommand.CommandText = "SELECT 42, substr(Value, 1, 3) FROM DataTable WHERE Id = 5"; + using var reader = selectCommand.ExecuteReader(); + Assert.True(reader.Read()); + + using var textReader = reader.GetTextReader(1); + Assert.Equal("abc", textReader.ReadToEnd()); + } + + [Fact] + public void GetStream_Blob_works_when_a_literal_is_in_the_same_query() + { + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + connection.ExecuteNonQuery( + "CREATE TABLE DataTable (Id INTEGER PRIMARY KEY, Data BLOB); INSERT INTO DataTable VALUES (5, X'01020304');"); + + var selectCommand = connection.CreateCommand(); + selectCommand.CommandText = "SELECT 42, Id, Data FROM DataTable WHERE Id = 5"; + using var reader = selectCommand.ExecuteReader(); + Assert.True(reader.Read()); + + using var sourceStream = reader.GetStream(2); + Assert.IsType(sourceStream); + var buffer = new byte[4]; + var bytesRead = sourceStream.Read(buffer, 0, 4); + Assert.Equal(4, bytesRead); + Assert.Equal([0x01, 0x02, 0x03, 0x04], buffer); + } + [Fact] public void GetStream_Blob_works_when_long_pk() {