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