# Copyright (c) 2021-2025, PostgreSQL Global Development Group # To test successful data directory creation with an additional feature, first # try to elaborate the "successful creation" test instead of adding a test. # Successful initdb consumes much time and I/O. use strict; use warnings FATAL => 'all'; use Fcntl ':mode'; use File::stat qw{lstat}; use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; my $tempdir = PostgreSQL::Test::Utils::tempdir; my $xlogdir = "$tempdir/pgxlog"; my $datadir = "$tempdir/data"; my $supports_syncfs = check_pg_config("#define HAVE_SYNCFS 1"); program_help_ok('initdb'); program_version_ok('initdb'); program_options_handling_ok('initdb'); command_fails([ 'initdb', '--sync-only', "$tempdir/nonexistent" ], 'sync missing data directory'); mkdir $xlogdir; mkdir "$xlogdir/lost+found"; command_fails([ 'initdb', '--waldir' => $xlogdir, $datadir ], 'existing nonempty xlog directory'); rmdir "$xlogdir/lost+found"; command_fails( [ 'initdb', '--waldir' => 'pgxlog', $datadir ], 'relative xlog directory not allowed'); command_fails([ 'initdb', '--username' => 'pg_test', $datadir ], 'role names cannot begin with "pg_"'); mkdir $datadir; # make sure we run one successful test without a TZ setting so we test # initdb's time zone setting code { # delete local only works from perl 5.12, so use the older way to do this local (%ENV) = %ENV; delete $ENV{TZ}; # while we are here, also exercise --text-search-config and --set options command_ok( [ 'initdb', '--no-sync', '--text-search-config' => 'german', '--set' => 'default_text_search_config=german', '--waldir' => $xlogdir, $datadir ], 'successful creation'); # Permissions on PGDATA should be default SKIP: { skip "unix-style permissions not supported on Windows", 1 if ($windows_os); ok(check_mode_recursive($datadir, 0700, 0600), "check PGDATA permissions"); } } # Control file should tell that data checksums are enabled by default. command_like( [ 'pg_controldata', $datadir ], qr/Data page checksum version:.*1/, 'checksums are enabled in control file'); command_ok([ 'initdb', '--sync-only', $datadir ], 'sync only'); command_ok([ 'initdb', '--sync-only', '--no-sync-data-files', $datadir ], '--no-sync-data-files'); command_fails([ 'initdb', $datadir ], 'existing data directory'); if ($supports_syncfs) { command_ok( [ 'initdb', '--sync-only', $datadir, '--sync-method' => 'syncfs' ], 'sync method syncfs'); } else { command_fails( [ 'initdb', '--sync-only', $datadir, '--sync-method' => 'syncfs' ], 'sync method syncfs'); } # Check group access on PGDATA SKIP: { skip "unix-style permissions not supported on Windows", 2 if ($windows_os || $Config::Config{osname} eq 'cygwin'); # Init a new db with group access my $datadir_group = "$tempdir/data_group"; command_ok([ 'initdb', '--allow-group-access', $datadir_group ], 'successful creation with group access'); ok(check_mode_recursive($datadir_group, 0750, 0640), 'check PGDATA permissions'); } # Locale provider tests if ($ENV{with_icu} eq 'yes') { command_fails_like( [ 'initdb', '--no-sync', '--locale-provider' => 'icu', "$tempdir/data2" ], qr/initdb: error: locale must be specified if provider is icu/, 'locale provider ICU requires --icu-locale'); command_ok( [ 'initdb', '--no-sync', '--locale-provider' => 'icu', '--icu-locale' => 'en', "$tempdir/data3" ], 'option --icu-locale'); command_like( [ 'initdb', '--no-sync', '--auth' => 'trust', '--locale-provider' => 'icu', '--locale' => 'und', '--lc-collate' => 'C', '--lc-ctype' => 'C', '--lc-messages' => 'C', '--lc-numeric' => 'C', '--lc-monetary' => 'C', '--lc-time' => 'C', "$tempdir/data4" ], qr/^\s+default collation:\s+und\n/ms, 'options --locale-provider=icu --locale=und --lc-*=C'); command_fails_like( [ 'initdb', '--no-sync', '--locale-provider' => 'icu', '--icu-locale' => '@colNumeric=lower', "$tempdir/dataX" ], qr/could not open collator for locale/, 'fails for invalid ICU locale'); command_fails_like( [ 'initdb', '--no-sync', '--locale-provider' => 'icu', '--encoding' => 'SQL_ASCII', '--icu-locale' => 'en', "$tempdir/dataX" ], qr/error: encoding mismatch/, 'fails for encoding not supported by ICU'); command_fails_like( [ 'initdb', '--no-sync', '--locale-provider' => 'icu', '--icu-locale' => 'nonsense-nowhere', "$tempdir/dataX" ], qr/error: locale "nonsense-nowhere" has unknown language "nonsense"/, 'fails for nonsense language'); command_fails_like( [ 'initdb', '--no-sync', '--locale-provider' => 'icu', '--icu-locale' => '@colNumeric=lower', "$tempdir/dataX" ], qr/could not open collator for locale "und-u-kn-lower": U_ILLEGAL_ARGUMENT_ERROR/, 'fails for invalid collation argument'); } else { command_fails( [ 'initdb', '--no-sync', '--locale-provider' => 'icu', "$tempdir/data2" ], 'locale provider ICU fails since no ICU support'); } command_fails( [ 'initdb', '--no-sync', '--locale-provider' => 'builtin', "$tempdir/data6" ], 'locale provider builtin fails without --locale'); command_ok( [ 'initdb', '--no-sync', '--locale-provider' => 'builtin', '--locale' => 'C', "$tempdir/data7" ], 'locale provider builtin with --locale'); command_ok( [ 'initdb', '--no-sync', '--locale-provider' => 'builtin', '--encoding' => 'UTF-8', '--lc-collate' => 'C', '--lc-ctype' => 'C', '--builtin-locale' => 'C.UTF-8', "$tempdir/data8" ], 'locale provider builtin with --encoding=UTF-8 --builtin-locale=C.UTF-8'); command_fails( [ 'initdb', '--no-sync', '--locale-provider' => 'builtin', '--encoding' => 'SQL_ASCII', '--lc-collate' => 'C', '--lc-ctype' => 'C', '--builtin-locale' => 'C.UTF-8', "$tempdir/data9" ], 'locale provider builtin with --builtin-locale=C.UTF-8 fails for SQL_ASCII' ); command_ok( [ 'initdb', '--no-sync', '--locale-provider' => 'builtin', '--lc-ctype' => 'C', '--locale' => 'C', "$tempdir/data10" ], 'locale provider builtin with --lc-ctype'); command_fails( [ 'initdb', '--no-sync', '--locale-provider' => 'builtin', '--icu-locale' => 'en', "$tempdir/dataX" ], 'fails for locale provider builtin with ICU locale'); command_fails( [ 'initdb', '--no-sync', '--locale-provider' => 'builtin', '--icu-rules' => '""', "$tempdir/dataX" ], 'fails for locale provider builtin with ICU rules'); command_fails( [ 'initdb', '--no-sync', '--locale-provider' => 'xyz', "$tempdir/dataX" ], 'fails for invalid locale provider'); command_fails( [ 'initdb', '--no-sync', '--locale-provider' => 'libc', '--icu-locale' => 'en', "$tempdir/dataX" ], 'fails for invalid option combination'); command_fails( [ 'initdb', '--no-sync', '--set' => 'foo=bar', "$tempdir/dataX" ], 'fails for invalid --set option'); # Make sure multiple invocations of --set parameters are added case # insensitive. command_ok( [ 'initdb', '--no-sync', '--set' => 'work_mem=128', '--set' => 'Work_Mem=256', '--set' => 'WORK_MEM=512', "$tempdir/dataY" ], 'multiple --set options with different case'); my $conf = slurp_file("$tempdir/dataY/postgresql.conf"); ok($conf !~ qr/^WORK_MEM = /m, "WORK_MEM should not be configured"); ok($conf !~ qr/^Work_Mem = /m, "Work_Mem should not be configured"); ok($conf =~ qr/^work_mem = 512/m, "work_mem should be in config"); # Test the no-data-checksums flag my $datadir_nochecksums = "$tempdir/data_no_checksums"; command_ok([ 'initdb', '--no-data-checksums', $datadir_nochecksums ], 'successful creation without data checksums'); # Control file should tell that data checksums are disabled. command_like( [ 'pg_controldata', $datadir_nochecksums ], qr/Data page checksum version:.*0/, 'checksums are disabled in control file'); # pg_checksums fails with checksums disabled. This is # not part of the tests included in pg_checksums to save from # the creation of an extra instance. command_fails( [ 'pg_checksums', '--pgdata' => $datadir_nochecksums ], "pg_checksums fails with data checksum disabled"); done_testing();