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(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 [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)] + "..."; }