Files
2026-01-06 16:57:58 -05:00

132 lines
4.4 KiB
C#

using System.Text.Json;
using DbExporter;
if (args.Length < 1 || args.Contains("--help") || args.Contains("-h"))
{
PrintUsage();
return args.Contains("--help") || args.Contains("-h") ? 0 : 1;
}
var definitionPath = args[0];
var verify = args.Contains("--verify");
var verifyFull = args.Contains("--verify-full");
if (!File.Exists(definitionPath))
{
Console.WriteLine($"Error: Definition file not found: {definitionPath}");
return 1;
}
try
{
var json = await File.ReadAllTextAsync(definitionPath);
var definition = JsonSerializer.Deserialize<ExportDefinition>(json);
if (definition is null)
{
Console.WriteLine("Error: Failed to parse definition file.");
return 1;
}
// Validate required fields
if (string.IsNullOrWhiteSpace(definition.ProviderType))
{
Console.WriteLine("Error: providerType is required.");
return 1;
}
if (string.IsNullOrWhiteSpace(definition.ConnectionString))
{
Console.WriteLine("Error: connectionString is required.");
return 1;
}
if (string.IsNullOrWhiteSpace(definition.Query))
{
Console.WriteLine("Error: query is required.");
return 1;
}
if (string.IsNullOrWhiteSpace(definition.OutputPath))
{
Console.WriteLine("Error: outputPath is required.");
return 1;
}
var exporter = new DatabaseExporter();
var verifier = new Verifier();
Console.WriteLine($"Exporting from {definition.ProviderType}...");
Console.WriteLine($"Query: {Truncate(definition.Query, 80)}");
var result = await exporter.ExportAsync(definition);
// Always do a quick verify to get row count
var quickVerify = verifier.Verify(definition.OutputPath, computeHash: false);
var ratio = result.CompressedSize > 0 && quickVerify.RowCount > 0
? $" ({(double)result.CompressedSize / result.UncompressedSize * 100:F1}%)"
: "";
Console.WriteLine($"✓ Exported: {quickVerify.RowCount:N0} rows, {result.UncompressedSize:N0} → {result.CompressedSize:N0} bytes{ratio}");
if (verify || verifyFull)
{
Console.WriteLine();
Console.WriteLine("Verifying...");
var verifyResult = verifier.Verify(definition.OutputPath, computeHash: verifyFull);
Console.WriteLine($"✓ Verified: {verifyResult.RowCount:N0} rows");
Console.WriteLine($"Schema: {verifier.FormatSchema(verifyResult.Schema)}");
if (verifyFull && verifyResult.HashMatch.HasValue)
{
if (verifyResult.HashMatch.Value)
{
Console.WriteLine($"✓ Checksum: SHA256 match ({verifyResult.ComputedHash})");
}
else
{
Console.WriteLine($"✗ Checksum: SHA256 MISMATCH");
Console.WriteLine($" Expected: {verifyResult.ExpectedHash}");
Console.WriteLine($" Computed: {verifyResult.ComputedHash}");
return 1;
}
}
}
return 0;
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
return 1;
}
static void PrintUsage()
{
Console.WriteLine("Usage: DbExporter <definition-file> [options]");
Console.WriteLine();
Console.WriteLine("Arguments:");
Console.WriteLine(" definition-file Path to JSON definition file");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine(" --verify Verify output (row count + schema)");
Console.WriteLine(" --verify-full Verify output with SHA256 checksum");
Console.WriteLine(" --help Show this help");
Console.WriteLine();
Console.WriteLine("Definition file format:");
Console.WriteLine(" {");
Console.WriteLine(" \"providerType\": \"SqlServer\",");
Console.WriteLine(" \"connectionString\": \"Server=...;Database=...;\",");
Console.WriteLine(" \"query\": \"SELECT * FROM MyTable\",");
Console.WriteLine(" \"outputPath\": \"./output/mytable.pb.zstd\",");
Console.WriteLine(" \"compressionLevel\": 10");
Console.WriteLine(" }");
}
static string Truncate(string value, int maxLength)
{
if (string.IsNullOrEmpty(value)) return value;
var singleLine = value.Replace("\r", "").Replace("\n", " ");
return singleLine.Length <= maxLength ? singleLine : singleLine[..(maxLength - 3)] + "...";
}