132 lines
4.4 KiB
C#
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)] + "...";
|
|
}
|