-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
116 lines (97 loc) · 4.31 KB
/
Program.cs
File metadata and controls
116 lines (97 loc) · 4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using System.CommandLine;
using System.Security;
using pg_extract_schema;
var hostOption = new Option<string>("--host", description: "PostgreSQL server hostname") { IsRequired = true };
hostOption.AddAlias("-h");
var portOption = new Option<int>("--port", getDefaultValue: () => 5432, description: "PostgreSQL server port");
portOption.AddAlias("-p");
var databaseOption = new Option<string>("--database", description: "Database name") { IsRequired = true };
databaseOption.AddAlias("-d");
var schemaOption = new Option<string?>("--schema", description: "Schema name (default: all non-system schemas)");
schemaOption.AddAlias("-s");
var outputOption = new Option<string>("--output", getDefaultValue: () => "output", description: "Output directory");
outputOption.AddAlias("-o");
var userOption = new Option<string>("--username", getDefaultValue: () => "postgres", description: "PostgreSQL username");
userOption.AddAlias("-U");
var passwordOption = new Option<string?>("--password", description: "PostgreSQL password (or set PGPASSWORD env var)");
passwordOption.AddAlias("-W");
var includeSystemOption = new Option<bool>(
"--include-postgres-system-objects",
getDefaultValue: () => false,
description: "Include PostgreSQL system schemas (pg_catalog, pg_toast, information_schema, pg_temp) and the plpgsql extension");
var includePgToastOption = new Option<bool>(
"--include-pg-toast",
getDefaultValue: () => false,
description: "Include pg_toast schema objects (excluded by default)");
var rootCommand = new RootCommand("Extract DDL from a PostgreSQL database into discrete .sql files")
{
hostOption, portOption, databaseOption, schemaOption, outputOption, userOption, passwordOption, includeSystemOption, includePgToastOption
};
rootCommand.SetHandler(async (context) =>
{
var host = context.ParseResult.GetValueForOption(hostOption)!;
var port = context.ParseResult.GetValueForOption(portOption);
var database = context.ParseResult.GetValueForOption(databaseOption)!;
var schema = context.ParseResult.GetValueForOption(schemaOption);
var output = context.ParseResult.GetValueForOption(outputOption)!;
var username = context.ParseResult.GetValueForOption(userOption)!;
var password = context.ParseResult.GetValueForOption(passwordOption);
var includeSystem = context.ParseResult.GetValueForOption(includeSystemOption);
var includePgToast = context.ParseResult.GetValueForOption(includePgToastOption);
password ??= Environment.GetEnvironmentVariable("PGPASSWORD");
if (password == null)
{
Console.Write("Password: ");
password = ReadPasswordMasked();
Console.WriteLine();
}
var connString = $"Host={host};Port={port};Database={database};Username={username}"
+ (password != null ? $";Password={password}" : "");
Console.WriteLine($"Connecting to {host}:{port}/{database} ...");
try
{
var extractor = new SchemaExtractor(connString, output, schema, includeSystem, includePgToast);
await extractor.ExtractAllAsync();
Console.WriteLine($"\nDone. DDL written to: {Path.GetFullPath(output)}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error: {ex.Message}");
Environment.ExitCode = 1;
}
});
return await rootCommand.InvokeAsync(args);
static string ReadPasswordMasked()
{
var password = new SecureString();
while (true)
{
var key = Console.ReadKey(intercept: true);
if (key.Key == ConsoleKey.Enter)
break;
if (key.Key == ConsoleKey.Backspace)
{
if (password.Length > 0)
{
password.RemoveAt(password.Length - 1);
Console.Write("\b \b");
}
}
else if (!char.IsControl(key.KeyChar))
{
password.AppendChar(key.KeyChar);
Console.Write('*');
}
}
// Convert SecureString to plain string only at the point of use
var ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(password);
try
{
return System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
}
finally
{
System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
password.Dispose();
}
}