Line data Source code
1 : use std::path::PathBuf;
2 : use std::str::FromStr;
3 :
4 : use anyhow::*;
5 : use clap::{Arg, ArgMatches, Command, value_parser};
6 : use postgres::Client;
7 : use wal_craft::*;
8 :
9 0 : fn main() -> Result<()> {
10 0 : env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("wal_craft=info"))
11 0 : .init();
12 0 : let arg_matches = cli().get_matches();
13 0 :
14 0 : let wal_craft = |arg_matches: &ArgMatches, client: &mut Client| {
15 0 : let intermediate_lsns = match arg_matches
16 0 : .get_one::<String>("type")
17 0 : .map(|s| s.as_str())
18 0 : .context("'type' is required")?
19 : {
20 0 : Simple::NAME => Simple::craft(client)?,
21 0 : LastWalRecordXlogSwitch::NAME => LastWalRecordXlogSwitch::craft(client)?,
22 0 : LastWalRecordXlogSwitchEndsOnPageBoundary::NAME => {
23 0 : LastWalRecordXlogSwitchEndsOnPageBoundary::craft(client)?
24 : }
25 0 : WalRecordCrossingSegmentFollowedBySmallOne::NAME => {
26 0 : WalRecordCrossingSegmentFollowedBySmallOne::craft(client)?
27 : }
28 0 : LastWalRecordCrossingSegment::NAME => LastWalRecordCrossingSegment::craft(client)?,
29 0 : a => panic!("Unknown --type argument: {a}"),
30 : };
31 0 : let end_of_wal_lsn = client.pg_current_wal_insert_lsn()?;
32 0 : for lsn in intermediate_lsns {
33 0 : println!("intermediate_lsn = {lsn}");
34 0 : }
35 0 : println!("end_of_wal = {end_of_wal_lsn}");
36 0 : Ok(())
37 0 : };
38 :
39 0 : match arg_matches.subcommand() {
40 0 : None => panic!("No subcommand provided"),
41 0 : Some(("print-postgres-config", _)) => {
42 0 : for cfg in REQUIRED_POSTGRES_CONFIG.iter() {
43 0 : println!("{cfg}");
44 0 : }
45 0 : Ok(())
46 : }
47 :
48 0 : Some(("with-initdb", arg_matches)) => {
49 0 : let cfg = Conf {
50 0 : pg_version: *arg_matches
51 0 : .get_one::<u32>("pg-version")
52 0 : .context("'pg-version' is required")?,
53 0 : pg_distrib_dir: arg_matches
54 0 : .get_one::<PathBuf>("pg-distrib-dir")
55 0 : .context("'pg-distrib-dir' is required")?
56 0 : .to_owned(),
57 0 : datadir: arg_matches
58 0 : .get_one::<PathBuf>("datadir")
59 0 : .context("'datadir' is required")?
60 0 : .to_owned(),
61 0 : };
62 0 : cfg.initdb()?;
63 0 : let srv = cfg.start_server()?;
64 0 : wal_craft(arg_matches, &mut srv.connect_with_timeout()?)?;
65 0 : srv.kill();
66 0 : Ok(())
67 : }
68 0 : Some(("in-existing", arg_matches)) => wal_craft(
69 0 : arg_matches,
70 0 : &mut postgres::Config::from_str(
71 0 : arg_matches
72 0 : .get_one::<String>("connection")
73 0 : .context("'connection' is required")?,
74 : )
75 0 : .context(
76 0 : "'connection' argument value could not be parsed as a postgres connection string",
77 0 : )?
78 0 : .connect(postgres::NoTls)?,
79 : ),
80 0 : Some(_) => panic!("Unknown subcommand"),
81 : }
82 0 : }
83 :
84 1 : fn cli() -> Command {
85 1 : let type_arg = &Arg::new("type")
86 1 : .help("Type of WAL to craft")
87 1 : .value_parser([
88 1 : Simple::NAME,
89 1 : LastWalRecordXlogSwitch::NAME,
90 1 : LastWalRecordXlogSwitchEndsOnPageBoundary::NAME,
91 1 : WalRecordCrossingSegmentFollowedBySmallOne::NAME,
92 1 : LastWalRecordCrossingSegment::NAME,
93 1 : ])
94 1 : .required(true);
95 1 :
96 1 : Command::new("Postgres WAL crafter")
97 1 : .about("Crafts Postgres databases with specific WAL properties")
98 1 : .subcommand(
99 1 : Command::new("print-postgres-config")
100 1 : .about("Print the configuration required for PostgreSQL server before running this script")
101 1 : )
102 1 : .subcommand(
103 1 : Command::new("with-initdb")
104 1 : .about("Craft WAL in a new data directory first initialized with initdb")
105 1 : .arg(type_arg)
106 1 : .arg(
107 1 : Arg::new("datadir")
108 1 : .help("Data directory for the Postgres server")
109 1 : .value_parser(value_parser!(PathBuf))
110 1 : .required(true)
111 1 : )
112 1 : .arg(
113 1 : Arg::new("pg-distrib-dir")
114 1 : .long("pg-distrib-dir")
115 1 : .value_parser(value_parser!(PathBuf))
116 1 : .help("Directory with Postgres distributions (bin and lib directories, e.g. pg_install containing subpath `v14/bin/postgresql`)")
117 1 : .default_value("/usr/local")
118 1 : )
119 1 : .arg(
120 1 : Arg::new("pg-version")
121 1 : .long("pg-version")
122 1 : .help("Postgres version to use for the initial tenant")
123 1 : .value_parser(value_parser!(u32))
124 1 : .required(true)
125 1 :
126 1 : )
127 1 : )
128 1 : .subcommand(
129 1 : Command::new("in-existing")
130 1 : .about("Craft WAL at an existing recently created Postgres database. Note that server may append new WAL entries on shutdown.")
131 1 : .arg(type_arg)
132 1 : .arg(
133 1 : Arg::new("connection")
134 1 : .help("Connection string to the Postgres database to populate")
135 1 : .required(true)
136 1 : )
137 1 : )
138 1 : }
139 :
140 : #[test]
141 1 : fn verify_cli() {
142 1 : cli().debug_assert();
143 1 : }
|