Skip to content

Commit

Permalink
Microsoft.Data.Sqlite: Don't assume nullabilty or uniqueness of proje…
Browse files Browse the repository at this point in the history
…cted columns

Fixes #29744
Fixes #30765
  • Loading branch information
ajcvickers committed Dec 19, 2023
1 parent 209865e commit b3f736f
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 3 deletions.
7 changes: 4 additions & 3 deletions src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,8 @@ public override DataTable GetSchemaTable()
schemaRow[dataTypeColumn] = GetFieldType(i);
var dataTypeName = GetDataTypeName(i);
schemaRow[dataTypeNameColumn] = dataTypeName;
schemaRow[isAliasedColumn] = columnName != GetName(i);
var isAliased = columnName != GetName(i);
schemaRow[isAliasedColumn] = isAliased;
schemaRow[isExpressionColumn] = columnName == null;
schemaRow[isLongColumn] = DBNull.Value;

Expand All @@ -706,7 +707,7 @@ public override DataTable GetSchemaTable()
command.Parameters.AddWithValue("$column", columnName);

var cnt = (long)command.ExecuteScalar()!;
schemaRow[isUniqueColumn] = cnt != 0;
schemaRow[isUniqueColumn] = !isAliased && cnt != 0;

command.Parameters.Clear();
var columnType = "typeof(\"" + columnName.Replace("\"", "\"\"") + "\")";
Expand Down Expand Up @@ -740,7 +741,7 @@ public override DataTable GetSchemaTable()
SqliteException.ThrowExceptionForRC(rc, _command.Connection.Handle);

schemaRow[isKeyColumn] = primaryKey != 0;
schemaRow[allowDBNullColumn] = notNull == 0;
schemaRow[allowDBNullColumn] = isAliased || notNull == 0;
schemaRow[isAutoIncrementColumn] = autoInc != 0;
}
}
Expand Down
106 changes: 106 additions & 0 deletions test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using Microsoft.Data.Sqlite.Properties;
Expand Down Expand Up @@ -2223,6 +2224,111 @@ public void Dispose_doesnt_throw_but_stops_on_error()
}
}

[Fact] // Issue #29744
public void DataTable_load_handles_nulls()
{
using (var connection = new SqliteConnection("Data Source=:memory:"))
{
connection.Open();

connection.ExecuteNonQuery(
"""
CREATE TABLE Member (
ID INTEGER,
Lastname TEXT NOT NULL,
Firstname TEXT NOT NULL,
Type INTEGER,
Hidden INTEGER,
PRIMARY KEY (ID AUTOINCREMENT)
);
CREATE TABLE Types (
ID INTEGER,
Description TEXT NOT NULL,
Hidden INTEGER,
PRIMARY KEY (ID AUTOINCREMENT)
);
INSERT INTO Types (Description) VALUES ('Administrator');
INSERT INTO Types (Description) VALUES ('User');
INSERT INTO Member (Lastname, Firstname, Type, Hidden) VALUES ('Mustermann', 'Max', 1, 0);
INSERT INTO Member (Lastname, Firstname, Type, Hidden) VALUES ('Weber', 'Max', 2, 0);
INSERT INTO Member (Lastname, Firstname, Type, Hidden) VALUES ('Müller', 'Willhelm', NULL, 0);
""");

string sql =
"""
SELECT
Member.ID AS ID,
Member.Lastname,
Member.Firstname,
Types.ID AS TypeID,
Types.Description AS Type,
Member.Hidden
FROM Member
LEFT OUTER JOIN Types ON Types.ID = Member.Type;
""";

var table = new DataTable();
using (var command = new SqliteCommand(sql, connection))
{
using (var dataReader = command.ExecuteReader())
{
table.Load(dataReader);
}
}
}
}

[Fact] // Issue #30765
public void DataTable_load_handles_unique_columns()
{
using (var connection = new SqliteConnection("Data Source=:memory:"))
{
connection.Open();

connection.ExecuteNonQuery(
"""
CREATE TABLE "characters" (
"id" INTEGER,
"name" TEXT UNIQUE,
"guild" INTEGER
);
CREATE TABLE "guilds" (
"id" INTEGER NOT NULL UNIQUE,
"name" TEXT UNIQUE,
UNIQUE("name"),
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE UNIQUE INDEX guildname
ON guilds(name);
INSERT INTO characters (id, name, guild) VALUES (1, 'John', 1);
INSERT INTO characters (id, name, guild) VALUES (2, 'Jeanette', 1);
INSERT INTO guilds (id, name) VALUES (1, 'Testers');
""");

string sql =
"""
SELECT guilds.name as guildName, characters.name as charName FROM guilds
LEFT JOIN characters
ON guilds.id = characters.guild
""";

var table = new DataTable();
using (var command = new SqliteCommand(sql, connection))
{
using (var dataReader = command.ExecuteReader())
{
table.Load(dataReader);
}
}
}
}

private static void GetX_works<T>(string sql, Func<DbDataReader, T> action, T expected)
{
using (var connection = new SqliteConnection("Data Source=:memory:"))
Expand Down

0 comments on commit b3f736f

Please sign in to comment.