在 C# 的 SQLite 数据库中使用 FTS5 我得到:
Unhandled exception. Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 11: 'database disk image is malformed'.
at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
at Microsoft.Data.Sqlite.SqliteDataReader.NextResult()
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync()
at DatabaseManager.Search(String query) in E:\Documents\projects\sqlite-in-csharp\SqliteExample\DatabaseManager.cs:line 106
at Program.Program.Main(String[] args) in E:\Documents\projects\sqlite-in-csharp\SqliteExample\Program.cs:line 42
at Program.Program.<Main>(String[] args)
在完整性检查中,
meta
表变得干净,但即使在重建后search
仍然无效。
程序.cs:
using Microsoft.Data.Sqlite;
using static DatabaseManager;
namespace Program
{
public struct Track
{
public string Path;
public string Artist;
public string Title;
public Track(string path, string artist, string title)
{
Path = path;
Artist = artist;
Title = title;
}
}
class Program
{
static async Task Main(string[] args)
{
// Get database connection
DatabaseManager manager = await DatabaseManager.Build();
// Insert some data
Track[] tracks = [
new Track("E:/Music/King Gizzard & the Lizard Wizard/Flying Microtonal Banana (2017-02-24)/1.1 - Rattlesnake.flac", "King Gizzard & the Lizard Wizard", "Rattlesnake"),
new Track("E:/Music/Tame Impala/Lonerism (2012-10-08)/1.8 - Keep On Lying.flac", "Tame Impala", "Keep On Lying"),
new Track("E:/Music/Tame Impala/Lonerism (2012-10-08)/1.4 - Mind Mischief.flac", "Tame Impala", "Mind Mischief"),
new Track("E:/Music/Tame Impala/Lonerism (2012-10-08)/1.3 - Apocalypse Dreams.flac", "Tame Impala", "Apocalypse Dreams"),
new Track("E:/Music/LCD Soundsystem/This Is Happening (2010-12-03)/1.1 - Dance Yrself Clean.flac", "LCD Soundsystem", "Dance Yrself Clean"),
new Track("E:/Music/LCD Soundsystem/LCD Soundsystem (2005-01-24)/1.1 - Daft Punk Is Playing at My House.flac", "LCD Soundsystem", "Daft Punk Is Playing at My House"),
new Track("E:/Music/The Silents/Things to Learn (2008-03-28)/1.2 - Ophelia.flac", "The Silents", "Ophelia"),
new Track("E:/Music/The Silents/Things to Learn (2008-03-28)/1.4 - Tune for a Nymph.flac", "The Silents", "Tune for a Nymph"),
new Track("E:/Music/The Silents/Things to Learn (2008-03-28)/1.6 - Nightcrawl.flac", "The Silents", "Nightcrawl"),
new Track("E:/Music/The Silents/Things to Learn (2008-03-28)/1.9 - See the Future.flac", "The Silents", "See the Future"),
];
await manager.InsertData(tracks);
await manager.Search("\"Tame\"");
}
}
}
DatabaseManager.cs:
using Microsoft.Data.Sqlite;
using Program;
public class DatabaseManager
{
private SqliteConnection Connection { get; set; }
public static async Task<DatabaseManager> Build()
{
SqliteConnection connection = new SqliteConnection("Data Source=meta.db");
await connection.OpenAsync();
DatabaseManager manager = new DatabaseManager(connection);
await manager.InitTables();
return manager;
}
private DatabaseManager(SqliteConnection connection)
{
Connection = connection;
}
private async Task InitTables()
{
using (SqliteCommand command = this.Connection.CreateCommand())
{
command.CommandText = """
CREATE TABLE IF NOT EXISTS meta (
path TEXT PRIMARY KEY NOT NULL,
artist TEXT,
title TEXT
) WITHOUT ROWID;
""";
await command.ExecuteNonQueryAsync();
command.CommandText = """
CREATE VIRTUAL TABLE IF NOT EXISTS search USING fts5(
path UNINDEXED,
artist,
title,
content=meta,
content_rowid=path
);
""";
await command.ExecuteNonQueryAsync();
command.CommandText = """
CREATE TRIGGER IF NOT EXISTS meta_ai AFTER INSERT ON meta BEGIN
INSERT INTO search(path, artist, title) VALUES (new.path, new.artist, new.title);
END;
CREATE TRIGGER IF NOT EXISTS meta_ad AFTER DELETE ON meta BEGIN
INSERT INTO search(search, path, artist, title) VALUES ('delete', old.path, old.artist, old.title);
END;
CREATE TRIGGER IF NOT EXISTS meta_au AFTER UPDATE ON meta BEGIN
INSERT INTO search(search, path, artist, title) VALUES ('delete', old.path, old.artist, old.title);
INSERT INTO search(path, artist, title) VALUES (new.path, new.artist, new.title);
END;
""";
await command.ExecuteNonQueryAsync();
}
}
public async Task InsertData(Track data)
{
using (SqliteCommand command = this.Connection.CreateCommand())
{
command.CommandText = "INSERT INTO meta VALUES (?1, ?2, ?3);";
command.Parameters.AddWithValue("?1", data.Path);
command.Parameters.AddWithValue("?2", data.Artist);
command.Parameters.AddWithValue("?3", data.Title);
await command.ExecuteNonQueryAsync();
}
}
public async Task InsertData(Track[] data)
{
using (SqliteCommand command = this.Connection.CreateCommand())
{
command.CommandText = "INSERT INTO meta VALUES (?1, ?2, ?3);";
var pathParameter = command.Parameters.Add("?1", SqliteType.Text);
var artistParameter = command.Parameters.Add("?2", SqliteType.Text);
var titleParameter = command.Parameters.Add("?3", SqliteType.Text);
foreach (Track track in data)
{
pathParameter.Value = track.Path;
artistParameter.Value = track.Artist;
titleParameter.Value = track.Title;
await command.ExecuteNonQueryAsync();
}
}
}
public async Task Search(string query)
{
using (SqliteCommand command = this.Connection.CreateCommand())
{
command.CommandText = "SELECT * FROM search WHERE search MATCH ?1;";
command.Parameters.AddWithValue("?1", query);
var data = await command.ExecuteReaderAsync();
while (await data.ReadAsync())
{
Console.WriteLine("Test 1");
Console.WriteLine($"{data.GetValue(0)}, {data.GetValue(1)}, {data.GetValue(2)}");
}
Console.WriteLine("Test 2");
}
}
public async Task ResetSanity()
{
using (SqliteCommand command = this.Connection.CreateCommand())
{
command.CommandText = """
INSERT INTO search(search) VALUES ('integrity-check');
INSERT INTO search(search) VALUES ('rebuild');
INSERT INTO search(search) VALUES ('integrity-check');
""";
await command.ExecuteNonQueryAsync();
}
}
}
我的语法错误吗?在匹配之前我需要做些什么,或者我必须在虚拟表中配置一些东西吗?
SQLite 论坛的 Richard Hipp 告诉我:
您不能使用WITHOUT ROWID 表作为外部内容表。外部内容表必须是 rowid 表。 FTS5 的“content_rowid=”参数必须引用 INTEGER PRIMARY KEY 类型的列。如果您使“content_rowid=”引用 TEXT PRIMARY KEY 列,它将不起作用。
删除
WITHOUT ROWID
和 content_rowid=path
可以解决问题。