Skip to content

Commit

Permalink
Support using a view as the sqlar table
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkagamine committed Jun 14, 2024
1 parent 4b9a388 commit fbdca09
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 11 deletions.
2 changes: 2 additions & 0 deletions SqliteArchive.Server/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"CaseInsensitive": false,
"StaticSite": false,
"Charset": "utf-8",
"BlobTable": "sqlar",
"BlobColumn": "data",
"EnableFtp": false,
"FtpPasvPorts": "10000-10009",
"FtpPasvAddress": "127.0.0.1"
Expand Down
10 changes: 7 additions & 3 deletions SqliteArchive.Tests/SqlarServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public sealed class SqlarServiceTests : IDisposable

private readonly SqliteConnection connection;

private SqlarOptions options = new();
private SqlarOptions options = new() { BlobTable = "sqlar", BlobColumn = "data" };
private ILogger<SqlarService> logger = NullLogger<SqlarService>.Instance;

public SqlarServiceTests()
Expand Down Expand Up @@ -505,7 +505,7 @@ INSERT INTO files(filename, content)
}

// Create a view
// Note: This must contain an additional "rowid" column to support the blob API
// Note: This must contain an additional "rowid" column to enable direct blob access
using var createView = connection.CreateCommand();
createView.CommandText = """
CREATE VIEW sqlar(rowid, name, mode, mtime, sz, data) AS
Expand All @@ -514,7 +514,11 @@ CREATE VIEW sqlar(rowid, name, mode, mtime, sz, data) AS
createView.ExecuteNonQuery();

// Instantiate service
var service = new SqlarService(connection, Options.Create(options), logger);
var service = new SqlarService(connection, Options.Create(options with
{
BlobTable = "files",
BlobColumn = "content"
}), logger);

// Attempt to list files and retrieve blob
var root = service.FindPath("/") as DirectoryNode;
Expand Down
12 changes: 11 additions & 1 deletion SqliteArchive/SqlarOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public record SqlarOptions
Environment variables:
TZ Timezone for displaying date modified (default: UTC)
See [3m]8;;https://w.wiki/4Jx\List of tz database time zones]8;;\[m
[3mSee ]8;;https://w.wiki/4Jx\List of tz database time zones]8;;\[m
LANG Locale used for formatting (default: en_US)
Expand All @@ -28,6 +28,12 @@ StaticSite Disable directory listing and serve index.html files where
Charset Sets the charset in the Content-Type header of file streams.
Empty string to disable. (default: utf-8)
BlobTable Name of the table holding the blob (default: sqlar)
See the tip in ]8;;https://github.com/maxkagamine/sqlarserver#readme\the readme]8;;\ on using a view for the sqlar table
BlobColumn Name of the column holding the blob (default: data)
See the tip in ]8;;https://github.com/maxkagamine/sqlarserver#readme\the readme]8;;\ on using a view for the sqlar table
EnableFtp Start the FTP server (default: false)
FtpPasvPorts Port range used for passive mode. Host and container ports
Expand All @@ -47,6 +53,10 @@ docker slow. (default: 10000-10009)

public string Charset { get; init; } = "";

public string BlobTable { get; init; } = "";

public string BlobColumn { get; init; } = "";

public bool EnableFtp { get; init; }

public string FtpPasvPorts { get; init; } = "";
Expand Down
25 changes: 18 additions & 7 deletions SqliteArchive/SqlarService.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
using System.Diagnostics.CodeAnalysis;
using System.IO.Compression;
using Microsoft.Data.Sqlite;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SqliteArchive.Helpers;
using SqliteArchive.Nodes;
using System.Diagnostics.CodeAnalysis;
using System.IO.Compression;

namespace SqliteArchive;

public class SqlarService : ISqlarService
{
const string SqlarViewExceptionMessage =
"When using a view for the sqlar table, it must include the rowid column from the underlying table, and " +
$"{nameof(SqlarOptions.BlobTable)} and {nameof(SqlarOptions.BlobColumn)} must be set to the name of the " +
"underlying table and its data column, respectively.";

private readonly SqliteConnection connection;
private readonly SqlarOptions options;
private readonly ILogger<SqlarService> logger;
Expand All @@ -25,7 +30,14 @@ public SqlarService(SqliteConnection connection, IOptions<SqlarOptions> options,
root = new DirectoryNode(options.Value.CaseInsensitive ?
StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal);

InitializeFileTree();
try
{
InitializeFileTree();
}
catch (SqliteException ex) when (ex.Message.Contains("no such column: rowid"))
{
throw new Exception(SqlarViewExceptionMessage, ex);
}
}

public Node? FindPath(string path, bool dereference = false) => FindPath(new Path(path), dereference);
Expand Down Expand Up @@ -89,14 +101,13 @@ private Stream GetStream(long rowId, long size)
// [2]: https://www.sqlite.org/sqlar.html#managing_sqlite_archives_from_application_code
try
{
var blob = new SqliteBlob(connection, "sqlar", "data", rowId, readOnly: true);
var blob = new SqliteBlob(connection, options.BlobTable, options.BlobColumn, rowId, readOnly: true);
return size < 0 || blob.Length == size ? blob :
new ZLibStream(blob, CompressionMode.Decompress, leaveOpen: false);
}
catch (SqliteException ex) when (ex.Message.Contains("cannot open view"))
{
// TODO: Show a more helpful exception that the table name option needs to be set
throw;
throw new Exception(SqlarViewExceptionMessage, ex);
}
}

Expand Down

0 comments on commit fbdca09

Please sign in to comment.