From ed39cf53788242d6b6990497ae86250dd658d26c Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Sun, 20 Feb 2022 11:03:20 -0800 Subject: [PATCH v1 3/5] initdb: perform everything during --boot, mostly in backend. Author: Reviewed-By: Discussion: https://postgr.es/m/ Backpatch: --- src/include/bootstrap/bootstrap.h | 1 + src/include/commands/extension.h | 2 + src/include/tcop/tcopprot.h | 1 + src/include/utils/relcache.h | 1 + src/backend/bootstrap/bootscanner.l | 6 + src/backend/bootstrap/bootstrap.c | 306 +++++++++++++++- src/backend/catalog/Makefile | 2 + src/backend/catalog/description.sql | 16 + src/backend/catalog/privileges.sql | 154 ++++++++ src/backend/commands/extension.c | 5 +- src/backend/main/main.c | 1 + src/backend/tcop/postgres.c | 12 + src/backend/utils/cache/relcache.c | 11 +- src/bin/initdb/initdb.c | 532 ++-------------------------- 14 files changed, 535 insertions(+), 515 deletions(-) create mode 100644 src/backend/catalog/description.sql create mode 100644 src/backend/catalog/privileges.sql diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h index 471414909f3..f94e9339373 100644 --- a/src/include/bootstrap/bootstrap.h +++ b/src/include/bootstrap/bootstrap.h @@ -54,6 +54,7 @@ extern void boot_get_type_io_data(Oid typid, Oid *typinput, Oid *typoutput); +extern void boot_input(FILE *file); extern int boot_yyparse(void); extern int boot_yylex(void); diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h index e24e3759f0c..cd1668e5865 100644 --- a/src/include/commands/extension.h +++ b/src/include/commands/extension.h @@ -48,6 +48,8 @@ extern ObjectAddress ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt * extern Oid get_extension_oid(const char *extname, bool missing_ok); extern char *get_extension_name(Oid ext_oid); extern bool extension_file_exists(const char *extensionName); +extern void execute_sql_string(const char *sql); +extern char *read_whole_file(const char *filename, int *length); extern ObjectAddress AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *oldschema); diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index 15a11bc3ff1..47c8ff283e4 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -60,6 +60,7 @@ extern PlannedStmt *pg_plan_query(Query *querytree, const char *query_string, extern List *pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions, ParamListInfo boundParams); +extern void exec_simple_query_bootstrap(const char *query_string); extern bool check_max_stack_depth(int *newval, void **extra, GucSource source); extern void assign_max_stack_depth(int newval, void *extra); diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 84d6afef19b..e400146f648 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -93,6 +93,7 @@ extern int errtableconstraint(Relation rel, const char *conname); extern void RelationCacheInitialize(void); extern void RelationCacheInitializePhase2(void); extern void RelationCacheInitializePhase3(void); +extern void RelationCacheInitializePhase3b(bool needNewCacheFile); /* * Routine to create a relcache entry for an about-to-be-created relation diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l index 3094ccb93f4..72c3f40a88f 100644 --- a/src/backend/bootstrap/bootscanner.l +++ b/src/backend/bootstrap/bootscanner.l @@ -125,3 +125,9 @@ yyerror(const char *message) { elog(ERROR, "%s at line %d", message, yyline); } + +void +boot_input(FILE *file) +{ + yyin = file; +} diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 667c829064d..3fd8ed60715 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -2,7 +2,10 @@ * * bootstrap.c * routines to support running postgres in 'bootstrap' mode - * bootstrap mode is used to create the initial template database + * + * bootstrap mode is used to create the initial template1 database, perform + * additional initialization it via SQL scripts, and then create template0, + * postgres from template1. * * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -26,10 +29,14 @@ #include "access/xlog_internal.h" #include "bootstrap/bootstrap.h" #include "catalog/index.h" +#include "catalog/pg_authid_d.h" #include "catalog/pg_collation.h" #include "catalog/pg_type.h" +#include "commands/extension.h" #include "common/link-canary.h" +#include "common/string.h" #include "libpq/pqsignal.h" +#include "mb/pg_wchar.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "pg_getopt.h" @@ -41,6 +48,7 @@ #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/inval.h" #include "utils/memutils.h" #include "utils/rel.h" #include "utils/relmapper.h" @@ -55,6 +63,12 @@ static void populate_typ_list(void); static Oid gettype(char *type); static void cleanup(void); +static void bootstrap_load_nonbki(const char *share_path); +static void bootstrap_create_databases(void); + +static void exec_sql(const char *share_path, const char *str); +static void exec_sql_file(const char *share_path, const char *str); + /* ---------------- * global variables * ---------------- @@ -206,6 +220,7 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) char *progname = argv[0]; int flag; char *userDoption = NULL; + char *share_path = NULL; Assert(!IsUnderPostmaster); @@ -221,7 +236,12 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) argv++; argc--; - while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:X:-:")) != -1) + /* + * XXX: -s for share_path is probably a bad choice, it conflicts with a + * normal postgres option. Also, should probably just determine share path + * ourselves. + */ + while ((flag = getopt(argc, argv, "B:c:d:D:s:Fkr:X:-:")) != -1) { switch (flag) { @@ -247,6 +267,9 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) case 'F': SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); break; + case 's': + share_path = optarg; + break; case 'k': bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION; break; @@ -338,6 +361,12 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) abort(); } + if (share_path == NULL) + { + write_stderr("%s: -s is required in --boot mode\n", progname); + proc_exit(1); + } + /* * Do backend-like initialization for bootstrap mode */ @@ -365,11 +394,34 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) } /* - * Process bootstrap input. + * Process bootstrap file to create initial template1 contents. */ - StartTransactionCommand(); - boot_yyparse(); - CommitTransactionCommand(); + { + char bootstrap_file[MAXPGPATH]; + FILE *boot; + instr_time start_ts, boot_ts; + + INSTR_TIME_SET_CURRENT(start_ts); + + sprintf(bootstrap_file, "%s/%s", share_path, "postgres.bki"); + + boot = fopen(bootstrap_file, "r"); + if (boot == NULL) + elog(ERROR, "could not open bootstrap file \"%s\": %m", + bootstrap_file); + boot_input(boot); + + StartTransactionCommand(); + boot_yyparse(); + CommitTransactionCommand(); + + fclose(boot); + + INSTR_TIME_SET_CURRENT(boot_ts); + + elog(LOG, "boot in %.3f ms", + (INSTR_TIME_GET_DOUBLE(boot_ts) - INSTR_TIME_GET_DOUBLE(start_ts)) * 1000); + } /* * We should now know about all mapped relations, so it's okay to write @@ -377,11 +429,251 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) */ RelationMapFinishBootstrap(); - /* Clean up and exit */ + /* Clean up and exit FIXME */ cleanup(); + + /* + * Now that the catalog is populated with crucial contents, bring up + * system caches into a fully valid state. + */ + + SetProcessingMode(InitProcessing); + + IgnoreSystemIndexes = false; + + StartTransactionCommand(); + /* Seeing odd "row is too big:" failures without */ + InvalidateSystemCaches(); + /* FIXME: speechless-making API */ + RelationCacheInitializePhase3b(true); + CommitTransactionCommand(); + + /* + * Load further catalog contents by running a bunch of SQL commands. + */ + SetProcessingMode(NormalProcessing); + + bootstrap_load_nonbki(share_path); + + bootstrap_create_databases(); + proc_exit(0); } +/* + * Create template0 and postgres from template1. + * + * XXX: Several of the statements contain commands that cannot be executed in + * a transaction (VACUUM, CREATE DATABASE) and thus require a fairly + * complicated dance to maintain correct state. The easiest is to just rely on + * exec_simple_query() (via a wrapper) for that. Don't want to do that for + * everything else, because it's considerably faster to use exec_sql(). + */ +static void +bootstrap_create_databases(void) +{ + /* + * pg_upgrade tries to preserve database OIDs across upgrades. It's smart + * enough to drop and recreate a conflicting database with the same name, + * but if the same OID were used for one system-created database in the + * old cluster and a different system-created database in the new cluster, + * it would fail. To avoid that, assign a fixed OID to template0 rather + * than letting the server choose one. + * + * (Note that, while the user could have dropped and recreated these + * objects in the old cluster, the problem scenario only exists if the OID + * that is in use in the old cluster is also used in the new cluster - and + * the new cluster should be the result of a fresh initdb.) + */ + static const char *const template0_setup[] = { + "CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false OID = " + CppAsString2(Template0ObjectId) ";\n", + + /* + * template0 shouldn't have any collation-dependent objects, so unset + * the collation version. This disables collation version checks when + * making a new database from it. + */ + "UPDATE pg_database SET datcollversion = NULL WHERE datname = 'template0';\n", + + /* + * While we are here, do set the collation version on template1. + */ + "UPDATE pg_database SET datcollversion = pg_database_collation_actual_version(oid) WHERE datname = 'template1';\n", + + /* + * Explicitly revoke public create-schema and create-temp-table + * privileges in template1 and template0; else the latter would be on + * by default + */ + "REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;\n", + "REVOKE CREATE,TEMPORARY ON DATABASE template0 FROM public;\n", + + "COMMENT ON DATABASE template0 IS 'unmodifiable empty database';\n", + NULL + }; + + /* Assign a fixed OID to postgres, for the same reasons as template0 */ + static const char *const postgres_setup[] = { + "CREATE DATABASE postgres OID = " CppAsString2(PostgresObjectId) ";\n", + "COMMENT ON DATABASE postgres IS 'default administrative connection database';\n", + NULL + }; + instr_time start_ts, created_ts; + + INSTR_TIME_SET_CURRENT(start_ts); + + + MessageContext = AllocSetContextCreate(TopMemoryContext, + "MessageContext", + ALLOCSET_DEFAULT_SIZES); + + /* + * clean everything up in template1 + */ + exec_simple_query_bootstrap("VACUUM FREEZE"); + + /* + * copy template1 to template0 + */ + for (const char *const *line = template0_setup; *line; line++) + exec_simple_query_bootstrap(*line); + + /* + * copy template1 to postgres + */ + for (const char *const *line = postgres_setup; *line; line++) + exec_simple_query_bootstrap(*line); + + /* + * Finally vacuum to clean up dead rows in pg_database + */ + exec_simple_query_bootstrap("VACUUM pg_database"); + + MemoryContextDelete(MessageContext); + MessageContext = NULL; + + INSTR_TIME_SET_CURRENT(created_ts); + + elog(LOG, "created template0 and postgres in %.3f ms", + (INSTR_TIME_GET_DOUBLE(created_ts) - INSTR_TIME_GET_DOUBLE(start_ts)) * 1000); +} + +static void +bootstrap_load_nonbki(const char *share_path) +{ + StringInfoData sql; + + initStringInfo(&sql); + + StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); + + exec_sql_file(share_path, "system_constraints.sql"); + exec_sql_file(share_path, "system_functions.sql"); + + /* + * set up the shadow password table + */ + exec_sql("pg_authid", "REVOKE ALL ON pg_authid FROM public;"); + + /* + * Advance the OID counter so that subsequently-created objects aren't + * pinned. Subsequent objects are all droppable at the whim of the DBA. + */ + StopGeneratingPinnedObjectIds(); + + exec_sql_file(share_path, "system_views.sql"); + + exec_sql_file(share_path, "description.sql"); + + /* populate pg_collation */ + { + /* + * Add an SQL-standard name. We don't want to pin this, so it doesn't go + * in pg_collation.h. But add it before reading system collations, so + * that it wins if libc defines a locale named ucs_basic. + */ + appendStringInfo(&sql, + "INSERT INTO pg_collation (oid, collname, " + " collnamespace, collowner, " + " collprovider, collisdeterministic, collencoding, " + " collcollate, collctype) " + "VALUES (" + " pg_nextoid('pg_catalog.pg_collation', 'oid', " + " 'pg_catalog.pg_collation_oid_index'), " + " 'ucs_basic', 'pg_catalog'::regnamespace, %u, " + " '%c', true, %d, 'C', 'C');", + BOOTSTRAP_SUPERUSERID, COLLPROVIDER_LIBC, PG_UTF8); + exec_sql("pg_collation", sql.data); + resetStringInfo(&sql); + + /* Now import all collations we can find in the operating system */ + exec_sql("import collations", + "SELECT pg_import_system_collations('pg_catalog');"); + } + + exec_sql_file(share_path, "snowball_create.sql"); + + exec_sql_file(share_path, "privileges.sql"); + + exec_sql_file(share_path, "information_schema.sql"); + + exec_sql("plpgsql", "CREATE EXTENSION plpgsql;"); + + /* + * Process SQL coming from initdb. This includes things like setting + * up passwords, which would be a bit of pain to move to the backend. + */ + while (true) + { + if (!pg_get_line_buf(stdin, &sql)) + break; + + /* XXX: better descriptor than more */ + exec_sql("more", sql.data); + resetStringInfo(&sql); + } + + /* Run analyze before VACUUM so the statistics are frozen. */ + exec_sql("analyze", "ANALYZE"); + + PopActiveSnapshot(); + CommitTransactionCommand(); +} + +static void +exec_sql(const char *name, const char *sql) +{ + instr_time start_ts, exec_ts; + + INSTR_TIME_SET_CURRENT(start_ts); + + execute_sql_string(sql); + + INSTR_TIME_SET_CURRENT(exec_ts); + + elog(LOG, "exec %s in %.3f ms", + name, + (INSTR_TIME_GET_DOUBLE(exec_ts) - INSTR_TIME_GET_DOUBLE(start_ts)) * 1000); +} + +static void +exec_sql_file(const char *share_path, const char *filename) +{ + int length; + char *str; + char filepath[MAXPGPATH]; + + sprintf(filepath, "%s/%s", share_path, filename); + + str = read_whole_file(filepath, &length); + + exec_sql(filename, str); + + pfree(str); +} + /* ---------------------------------------------------------------- * misc functions diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index eefebb7bb83..d014e52968a 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -125,6 +125,8 @@ install-data: bki-stamp installdirs $(INSTALL_DATA) $(call vpathsearch,system_constraints.sql) '$(DESTDIR)$(datadir)/system_constraints.sql' $(INSTALL_DATA) $(srcdir)/system_functions.sql '$(DESTDIR)$(datadir)/system_functions.sql' $(INSTALL_DATA) $(srcdir)/system_views.sql '$(DESTDIR)$(datadir)/system_views.sql' + $(INSTALL_DATA) $(srcdir)/description.sql '$(DESTDIR)$(datadir)/description.sql' + $(INSTALL_DATA) $(srcdir)/privileges.sql '$(DESTDIR)$(datadir)/privileges.sql' $(INSTALL_DATA) $(srcdir)/information_schema.sql '$(DESTDIR)$(datadir)/information_schema.sql' $(INSTALL_DATA) $(srcdir)/sql_features.txt '$(DESTDIR)$(datadir)/sql_features.txt' diff --git a/src/backend/catalog/description.sql b/src/backend/catalog/description.sql new file mode 100644 index 00000000000..b46a3094452 --- /dev/null +++ b/src/backend/catalog/description.sql @@ -0,0 +1,16 @@ +/* Create default descriptions for operator implementation functions */ +WITH funcdescs AS ( + SELECT p.oid as p_oid, o.oid as o_oid, oprname + FROM pg_proc p JOIN pg_operator o ON oprcode = p.oid +) +INSERT INTO pg_description + SELECT p_oid, 'pg_proc'::regclass, 0, + 'implementation of ' || oprname || ' operator' + FROM funcdescs + WHERE NOT EXISTS ( + SELECT 1 FROM pg_description + WHERE objoid = p_oid AND classoid = 'pg_proc'::regclass) + AND NOT EXISTS ( + SELECT 1 FROM pg_description + WHERE objoid = o_oid AND classoid = 'pg_operator'::regclass + AND description LIKE 'deprecated%'); diff --git a/src/backend/catalog/privileges.sql b/src/backend/catalog/privileges.sql new file mode 100644 index 00000000000..3843c970df5 --- /dev/null +++ b/src/backend/catalog/privileges.sql @@ -0,0 +1,154 @@ +/* + * Set up privileges + * + * We mark most system catalogs as world-readable. We don't currently have + * to touch functions, languages, or databases, because their default + * permissions are OK. + * + * Some objects may require different permissions by default, so we + * make sure we don't overwrite privilege sets that have already been + * set (NOT NULL). + * + * Also populate pg_init_privs to save what the privileges are at init + * time. This is used by pg_dump to allow users to change privileges + * on catalog objects and to have those privilege changes preserved + * across dump/reload and pg_upgrade. + * + * Note that pg_init_privs is only for per-database objects and therefore + * we don't include databases or tablespaces. + */ + +UPDATE pg_class + SET relacl = (SELECT array_agg(a.acl) FROM + (SELECT '=r/"POSTGRES"' as acl + UNION SELECT unnest(pg_catalog.acldefault( + CASE WHEN relkind = 'S' THEN 's' + ELSE 'r' END::"char", 10::oid)) -- FIXME, inlined BOOTSTRAP_SUPERUSERID + ) as a) + WHERE relkind IN ('r', 'v', 'm','S') + AND relacl IS NULL; + +GRANT USAGE ON SCHEMA pg_catalog, public TO PUBLIC; +REVOKE ALL ON pg_largeobject FROM PUBLIC; +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + oid, + (SELECT oid FROM pg_class WHERE relname = 'pg_class'), + 0, + relacl, + 'i' + FROM + pg_class + WHERE + relacl IS NOT NULL + AND relkind IN ('r', 'v', 'm','S'); + +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + pg_class.oid, + (SELECT oid FROM pg_class WHERE relname = 'pg_class'), + pg_attribute.attnum, + pg_attribute.attacl, + 'i' + FROM + pg_class + JOIN pg_attribute ON (pg_class.oid = pg_attribute.attrelid) + WHERE + pg_attribute.attacl IS NOT NULL + AND pg_class.relkind IN ('r', 'v', 'm', 'S'); + +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + oid, + (SELECT oid FROM pg_class WHERE relname = 'pg_proc'), + 0, + proacl, + 'i' + FROM + pg_proc + WHERE + proacl IS NOT NULL; + +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + oid, + (SELECT oid FROM pg_class WHERE relname = 'pg_type'), + 0, + typacl, + 'i' + FROM + pg_type + WHERE + typacl IS NOT NULL; + +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + oid, + (SELECT oid FROM pg_class WHERE relname = 'pg_language'), + 0, + lanacl, + 'i' + FROM + pg_language + WHERE + lanacl IS NOT NULL; + +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + oid, + (SELECT oid FROM pg_class WHERE + relname = 'pg_largeobject_metadata'), + 0, + lomacl, + 'i' + FROM + pg_largeobject_metadata + WHERE + lomacl IS NOT NULL; + +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + oid, + (SELECT oid FROM pg_class WHERE relname = 'pg_namespace'), + 0, + nspacl, + 'i' + FROM + pg_namespace + WHERE + nspacl IS NOT NULL; + +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + oid, + (SELECT oid FROM pg_class WHERE + relname = 'pg_foreign_data_wrapper'), + 0, + fdwacl, + 'i' + FROM + pg_foreign_data_wrapper + WHERE + fdwacl IS NOT NULL; + +INSERT INTO pg_init_privs + (objoid, classoid, objsubid, initprivs, privtype) + SELECT + oid, + (SELECT oid FROM pg_class + WHERE relname = 'pg_foreign_server'), + 0, + srvacl, + 'i' + FROM + pg_foreign_server + WHERE + srvacl IS NOT NULL; diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 0e04304cb09..c46e607bccb 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -127,7 +127,6 @@ static void ApplyExtensionUpdates(Oid extensionOid, char *origSchemaName, bool cascade, bool is_create); -static char *read_whole_file(const char *filename, int *length); /* @@ -716,7 +715,7 @@ read_extension_script_file(const ExtensionControlFile *control, * on printing the whole string as errcontext in case of any error, and that * could be very long. */ -static void +void execute_sql_string(const char *sql) { List *raw_parsetree_list; @@ -3429,7 +3428,7 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt, * The file contents are returned as a single palloc'd chunk. For convenience * of the callers, an extra \0 byte is added to the end. */ -static char * +char * read_whole_file(const char *filename, int *length) { char *buf; diff --git a/src/backend/main/main.c b/src/backend/main/main.c index 3d67ce9dcea..3b28f9c57d0 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -394,6 +394,7 @@ help(const char *progname) printf(_(" --check selects check mode (must be first argument)\n")); printf(_(" DBNAME database name (mandatory argument in bootstrapping mode)\n")); printf(_(" -r FILENAME send stdout and stderr to given file\n")); + printf(_(" -s SHAREPATH path to share directory\n")); printf(_("\nPlease read the documentation for the complete list of run-time\n" "configuration settings and how to set them on the command line or in\n" diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 3c7d08209f3..1e48645a748 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -1314,6 +1314,18 @@ exec_simple_query(const char *query_string) debug_query_string = NULL; } +/* just for bootstrap */ +void +exec_simple_query_bootstrap(const char *query_string) +{ + MemoryContextSwitchTo(MessageContext); + SetCurrentStatementStartTimestamp(); + + exec_simple_query(query_string); + + MemoryContextReset(MessageContext); +} + /* * exec_parse_message * diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 2707fed12f4..6b4b14819c1 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -3949,8 +3949,6 @@ RelationCacheInitializePhase2(void) void RelationCacheInitializePhase3(void) { - HASH_SEQ_STATUS status; - RelIdCacheEnt *idhentry; MemoryContext oldcxt; bool needNewCacheFile = !criticalSharedRelcachesBuilt; @@ -3992,6 +3990,15 @@ RelationCacheInitializePhase3(void) if (IsBootstrapProcessingMode()) return; + RelationCacheInitializePhase3b(needNewCacheFile); +} + +void +RelationCacheInitializePhase3b(bool needNewCacheFile) +{ + HASH_SEQ_STATUS status; + RelIdCacheEnt *idhentry; + /* * If we didn't get the critical system indexes loaded into relcache, do * so now. These are critical because the catcache and/or opclass cache diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 37ac928b2ef..bff2e5cb407 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -76,6 +76,7 @@ #include "getopt_long.h" #include "mb/pg_wchar.h" #include "miscadmin.h" +#include "portability/instr_time.h" /* Ideally this would be in a .h file, but it hardly seems worth the trouble */ @@ -158,12 +159,7 @@ static char *bki_file; static char *hba_file; static char *ident_file; static char *conf_file; -static char *dictionary_file; -static char *info_schema_file; static char *features_file; -static char *system_constraints_file; -static char *system_functions_file; -static char *system_views_file; static bool success = false; static bool made_new_pgdata = false; static bool found_existing_pgdata = false; @@ -201,8 +197,7 @@ static bool authwarning = false; * but here it is more convenient to pass it as an environment variable * (no quoting to worry about). */ -static const char *boot_options = "-F -c log_checkpoints=false"; -static const char *backend_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true -c log_checkpoints=false"; +static const char *boot_options = " -F -c allow_system_table_mods=true -c search_path=pg_catalog -c exit_on_error=true"; /* Additional switches to pass to backend (either boot or standalone) */ static char *extra_options = ""; @@ -254,21 +249,10 @@ static void write_version_file(const char *extrapath); static void set_null_conf(void); static void test_config_settings(void); static void setup_config(void); -static void bootstrap_template1(void); -static void setup_auth(FILE *cmdfd); static void get_su_pwd(void); -static void setup_depend(FILE *cmdfd); -static void setup_run_file(FILE *cmdfd, const char *filename); -static void setup_description(FILE *cmdfd); -static void setup_collation(FILE *cmdfd); -static void setup_privileges(FILE *cmdfd); static void set_info_version(void); static void setup_schema(FILE *cmdfd); -static void load_plpgsql(FILE *cmdfd); static void set_remaining_details(FILE *cmdfd); -static void vacuum_db(FILE *cmdfd); -static void make_template0(FILE *cmdfd); -static void make_postgres(FILE *cmdfd); static void trapsig(int signum); static void check_ok(void); static char *escape_quotes(const char *src); @@ -1326,89 +1310,6 @@ setup_config(void) check_ok(); } - -/* - * run the BKI script in bootstrap mode to create template1 - */ -static void -bootstrap_template1(void) -{ - PG_CMD_DECL; - char **line; - char **bki_lines; - char headerline[MAXPGPATH]; - - printf(_("running bootstrap script ... ")); - fflush(stdout); - - bki_lines = readfile(bki_file); - - /* Check that bki file appears to be of the right version */ - - snprintf(headerline, sizeof(headerline), "# PostgreSQL %s\n", - PG_MAJORVERSION); - - if (strcmp(headerline, *bki_lines) != 0) - { - pg_log_error("input file \"%s\" does not belong to PostgreSQL %s", - bki_file, PG_VERSION); - fprintf(stderr, - _("Check your installation or specify the correct path " - "using the option -L.\n")); - exit(1); - } - - /* Also ensure backend isn't confused by this environment var: */ - unsetenv("PGCLIENTENCODING"); - - snprintf(cmd, sizeof(cmd), - "\"%s\" --boot -X %d %s %s %s %s", - backend_exec, - wal_segment_size_mb * (1024 * 1024), - data_checksums ? "-k" : "", - boot_options, extra_options, - debug ? "-d 5" : ""); - - - PG_CMD_OPEN; - - for (line = bki_lines; *line != NULL; line++) - { - PG_CMD_PUTS(*line); - free(*line); - } - - PG_CMD_CLOSE; - - free(bki_lines); - - check_ok(); -} - -/* - * set up the shadow password table - */ -static void -setup_auth(FILE *cmdfd) -{ - const char *const *line; - static const char *const pg_authid_setup[] = { - /* - * The authid table shouldn't be readable except through views, to - * ensure passwords are not publicly visible. - */ - "REVOKE ALL ON pg_authid FROM public;\n\n", - NULL - }; - - for (line = pg_authid_setup; *line != NULL; line++) - PG_CMD_PUTS(*line); - - if (superuser_password) - PG_CMD_PRINTF("ALTER USER \"%s\" WITH PASSWORD E'%s';\n\n", - username, escape_quotes(superuser_password)); -} - /* * get the superuser password if required */ @@ -1472,249 +1373,6 @@ get_su_pwd(void) superuser_password = pwd1; } -/* - * set up pg_depend - */ -static void -setup_depend(FILE *cmdfd) -{ - const char *const *line; - static const char *const pg_depend_setup[] = { - /* - * Advance the OID counter so that subsequently-created objects aren't - * pinned. - */ - "SELECT pg_stop_making_pinned_objects();\n\n", - NULL - }; - - for (line = pg_depend_setup; *line != NULL; line++) - PG_CMD_PUTS(*line); -} - -/* - * Run external file - */ -static void -setup_run_file(FILE *cmdfd, const char *filename) -{ - char **lines; - - lines = readfile(filename); - - for (char **line = lines; *line != NULL; line++) - { - PG_CMD_PUTS(*line); - free(*line); - } - - PG_CMD_PUTS("\n\n"); - - free(lines); -} - -/* - * fill in extra description data - */ -static void -setup_description(FILE *cmdfd) -{ - /* Create default descriptions for operator implementation functions */ - PG_CMD_PUTS("WITH funcdescs AS ( " - "SELECT p.oid as p_oid, o.oid as o_oid, oprname " - "FROM pg_proc p JOIN pg_operator o ON oprcode = p.oid ) " - "INSERT INTO pg_description " - " SELECT p_oid, 'pg_proc'::regclass, 0, " - " 'implementation of ' || oprname || ' operator' " - " FROM funcdescs " - " WHERE NOT EXISTS (SELECT 1 FROM pg_description " - " WHERE objoid = p_oid AND classoid = 'pg_proc'::regclass) " - " AND NOT EXISTS (SELECT 1 FROM pg_description " - " WHERE objoid = o_oid AND classoid = 'pg_operator'::regclass" - " AND description LIKE 'deprecated%');\n\n"); -} - -/* - * populate pg_collation - */ -static void -setup_collation(FILE *cmdfd) -{ - /* - * Add an SQL-standard name. We don't want to pin this, so it doesn't go - * in pg_collation.h. But add it before reading system collations, so - * that it wins if libc defines a locale named ucs_basic. - */ - PG_CMD_PRINTF("INSERT INTO pg_collation (oid, collname, collnamespace, collowner, collprovider, collisdeterministic, collencoding, collcollate, collctype)" - "VALUES (pg_nextoid('pg_catalog.pg_collation', 'oid', 'pg_catalog.pg_collation_oid_index'), 'ucs_basic', 'pg_catalog'::regnamespace, %u, '%c', true, %d, 'C', 'C');\n\n", - BOOTSTRAP_SUPERUSERID, COLLPROVIDER_LIBC, PG_UTF8); - - /* Now import all collations we can find in the operating system */ - PG_CMD_PUTS("SELECT pg_import_system_collations('pg_catalog');\n\n"); -} - -/* - * Set up privileges - * - * We mark most system catalogs as world-readable. We don't currently have - * to touch functions, languages, or databases, because their default - * permissions are OK. - * - * Some objects may require different permissions by default, so we - * make sure we don't overwrite privilege sets that have already been - * set (NOT NULL). - * - * Also populate pg_init_privs to save what the privileges are at init - * time. This is used by pg_dump to allow users to change privileges - * on catalog objects and to have those privilege changes preserved - * across dump/reload and pg_upgrade. - * - * Note that pg_init_privs is only for per-database objects and therefore - * we don't include databases or tablespaces. - */ -static void -setup_privileges(FILE *cmdfd) -{ - const char *const *line; - static const char *const privileges_setup[] = { - "UPDATE pg_class " - " SET relacl = (SELECT array_agg(a.acl) FROM " - " (SELECT '=r/\"POSTGRES\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = " CppAsString2(RELKIND_SEQUENCE) " THEN 's' " - " ELSE 'r' END::\"char\"," CppAsString2(BOOTSTRAP_SUPERUSERID) "::oid))" - " ) as a) " - " WHERE relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_VIEW) ", " CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_SEQUENCE) ")" - " AND relacl IS NULL;\n\n", - "GRANT USAGE ON SCHEMA pg_catalog, public TO PUBLIC;\n\n", - "REVOKE ALL ON pg_largeobject FROM PUBLIC;\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " oid," - " (SELECT oid FROM pg_class WHERE relname = 'pg_class')," - " 0," - " relacl," - " 'i'" - " FROM" - " pg_class" - " WHERE" - " relacl IS NOT NULL" - " AND relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_VIEW) ", " CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_SEQUENCE) ");\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " pg_class.oid," - " (SELECT oid FROM pg_class WHERE relname = 'pg_class')," - " pg_attribute.attnum," - " pg_attribute.attacl," - " 'i'" - " FROM" - " pg_class" - " JOIN pg_attribute ON (pg_class.oid = pg_attribute.attrelid)" - " WHERE" - " pg_attribute.attacl IS NOT NULL" - " AND pg_class.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_VIEW) ", " CppAsString2(RELKIND_MATVIEW) ", " - CppAsString2(RELKIND_SEQUENCE) ");\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " oid," - " (SELECT oid FROM pg_class WHERE relname = 'pg_proc')," - " 0," - " proacl," - " 'i'" - " FROM" - " pg_proc" - " WHERE" - " proacl IS NOT NULL;\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " oid," - " (SELECT oid FROM pg_class WHERE relname = 'pg_type')," - " 0," - " typacl," - " 'i'" - " FROM" - " pg_type" - " WHERE" - " typacl IS NOT NULL;\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " oid," - " (SELECT oid FROM pg_class WHERE relname = 'pg_language')," - " 0," - " lanacl," - " 'i'" - " FROM" - " pg_language" - " WHERE" - " lanacl IS NOT NULL;\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " oid," - " (SELECT oid FROM pg_class WHERE " - " relname = 'pg_largeobject_metadata')," - " 0," - " lomacl," - " 'i'" - " FROM" - " pg_largeobject_metadata" - " WHERE" - " lomacl IS NOT NULL;\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " oid," - " (SELECT oid FROM pg_class WHERE relname = 'pg_namespace')," - " 0," - " nspacl," - " 'i'" - " FROM" - " pg_namespace" - " WHERE" - " nspacl IS NOT NULL;\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " oid," - " (SELECT oid FROM pg_class WHERE " - " relname = 'pg_foreign_data_wrapper')," - " 0," - " fdwacl," - " 'i'" - " FROM" - " pg_foreign_data_wrapper" - " WHERE" - " fdwacl IS NOT NULL;\n\n", - "INSERT INTO pg_init_privs " - " (objoid, classoid, objsubid, initprivs, privtype)" - " SELECT" - " oid," - " (SELECT oid FROM pg_class " - " WHERE relname = 'pg_foreign_server')," - " 0," - " srvacl," - " 'i'" - " FROM" - " pg_foreign_server" - " WHERE" - " srvacl IS NOT NULL;\n\n", - NULL - }; - - for (line = privileges_setup; *line != NULL; line++) - PG_CMD_PUTS(*line); -} - /* * extract the strange version of version required for information schema * (09.08.0007abc) @@ -1749,29 +1407,18 @@ set_info_version(void) static void setup_schema(FILE *cmdfd) { - setup_run_file(cmdfd, info_schema_file); - PG_CMD_PRINTF("UPDATE information_schema.sql_implementation_info " " SET character_value = '%s' " - " WHERE implementation_info_name = 'DBMS VERSION';\n\n", + " WHERE implementation_info_name = 'DBMS VERSION';\n", infoversion); PG_CMD_PRINTF("COPY information_schema.sql_features " " (feature_id, feature_name, sub_feature_id, " " sub_feature_name, is_supported, comments) " - " FROM E'%s';\n\n", + " FROM E'%s';\n", escape_quotes(features_file)); } -/* - * load PL/pgSQL server-side language - */ -static void -load_plpgsql(FILE *cmdfd) -{ - PG_CMD_PUTS("CREATE EXTENSION plpgsql;\n\n"); -} - /* * Set some remaining details that aren't known when postgres.bki is made. * @@ -1796,8 +1443,8 @@ set_remaining_details(FILE *cmdfd) * locale/encoding without cheating. */ static char *final_details[] = { - "UPDATE pg_authid SET rolname = E'SUPERUSER_NAME' WHERE rolname = 'POSTGRES';\n\n", - "UPDATE pg_database SET encoding = E'ENCODING', datcollate = E'LC_COLLATE', datctype = E'LC_CTYPE';\n\n", + "UPDATE pg_authid SET rolname = E'SUPERUSER_NAME' WHERE rolname = 'POSTGRES';\n", + "UPDATE pg_database SET encoding = E'ENCODING', datcollate = E'LC_COLLATE', datctype = E'LC_CTYPE';\n", NULL }; @@ -1814,93 +1461,6 @@ set_remaining_details(FILE *cmdfd) PG_CMD_PUTS(*line); } -/* - * clean everything up in template1 - */ -static void -vacuum_db(FILE *cmdfd) -{ - /* Run analyze before VACUUM so the statistics are frozen. */ - PG_CMD_PUTS("ANALYZE;\n\nVACUUM FREEZE;\n\n"); -} - -/* - * copy template1 to template0 - */ -static void -make_template0(FILE *cmdfd) -{ - const char *const *line; - - /* - * pg_upgrade tries to preserve database OIDs across upgrades. It's smart - * enough to drop and recreate a conflicting database with the same name, - * but if the same OID were used for one system-created database in the - * old cluster and a different system-created database in the new cluster, - * it would fail. To avoid that, assign a fixed OID to template0 rather - * than letting the server choose one. - * - * (Note that, while the user could have dropped and recreated these - * objects in the old cluster, the problem scenario only exists if the OID - * that is in use in the old cluster is also used in the new cluster - and - * the new cluster should be the result of a fresh initdb.) - */ - static const char *const template0_setup[] = { - "CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false OID = " - CppAsString2(Template0ObjectId) ";\n\n", - - /* - * template0 shouldn't have any collation-dependent objects, so unset - * the collation version. This disables collation version checks when - * making a new database from it. - */ - "UPDATE pg_database SET datcollversion = NULL WHERE datname = 'template0';\n\n", - - /* - * While we are here, do set the collation version on template1. - */ - "UPDATE pg_database SET datcollversion = pg_database_collation_actual_version(oid) WHERE datname = 'template1';\n\n", - - /* - * Explicitly revoke public create-schema and create-temp-table - * privileges in template1 and template0; else the latter would be on - * by default - */ - "REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;\n\n", - "REVOKE CREATE,TEMPORARY ON DATABASE template0 FROM public;\n\n", - - "COMMENT ON DATABASE template0 IS 'unmodifiable empty database';\n\n", - - /* - * Finally vacuum to clean up dead rows in pg_database - */ - "VACUUM pg_database;\n\n", - NULL - }; - - for (line = template0_setup; *line; line++) - PG_CMD_PUTS(*line); -} - -/* - * copy template1 to postgres - */ -static void -make_postgres(FILE *cmdfd) -{ - const char *const *line; - - /* Assign a fixed OID to postgres, for the same reasons as template0 */ - static const char *const postgres_setup[] = { - "CREATE DATABASE postgres OID = " CppAsString2(PostgresObjectId) ";\n\n", - "COMMENT ON DATABASE postgres IS 'default administrative connection database';\n\n", - NULL - }; - - for (line = postgres_setup; *line; line++) - PG_CMD_PUTS(*line); -} - /* * signal handler in case we are interrupted. * @@ -2446,16 +2006,10 @@ setup_locale_encoding(void) void setup_data_file_paths(void) { - set_input(&bki_file, "postgres.bki"); set_input(&hba_file, "pg_hba.conf.sample"); set_input(&ident_file, "pg_ident.conf.sample"); set_input(&conf_file, "postgresql.conf.sample"); - set_input(&dictionary_file, "snowball_create.sql"); - set_input(&info_schema_file, "information_schema.sql"); set_input(&features_file, "sql_features.txt"); - set_input(&system_constraints_file, "system_constraints.sql"); - set_input(&system_functions_file, "system_functions.sql"); - set_input(&system_views_file, "system_views.sql"); if (show_setting || debug) { @@ -2474,16 +2028,9 @@ setup_data_file_paths(void) exit(0); } - check_input(bki_file); check_input(hba_file); check_input(ident_file); check_input(conf_file); - check_input(dictionary_file); - check_input(info_schema_file); - check_input(features_file); - check_input(system_constraints_file); - check_input(system_functions_file); - check_input(system_views_file); } @@ -2744,6 +2291,7 @@ initialize_data_directory(void) { PG_CMD_DECL; int i; + instr_time last_ts, cur_ts; setup_signals(); @@ -2788,68 +2336,46 @@ initialize_data_directory(void) write_version_file(NULL); /* Select suitable configuration settings */ + INSTR_TIME_SET_CURRENT(last_ts); set_null_conf(); test_config_settings(); + INSTR_TIME_SET_CURRENT(cur_ts); + fprintf(stderr, "config determination in %.3f ms\n", + (INSTR_TIME_GET_DOUBLE(cur_ts) - INSTR_TIME_GET_DOUBLE(last_ts)) * 1000); /* Now create all the text config files */ setup_config(); - /* Bootstrap template1 */ - bootstrap_template1(); - /* * Make the per-database PG_VERSION for template1 only after init'ing it + * + * FIXME: move to server */ write_version_file("base/1"); - /* - * Create the stuff we don't need to use bootstrap mode for, using a - * backend running in simple standalone mode. - */ - fputs(_("performing post-bootstrap initialization ... "), stdout); - fflush(stdout); - snprintf(cmd, sizeof(cmd), - "\"%s\" %s %s template1 >%s", - backend_exec, backend_options, extra_options, + "\"%s\" --boot -s \"%s\" -X %d %s %s %s %s >%s 2>&1", + backend_exec, + share_path, + wal_segment_size_mb * (1024 * 1024), + data_checksums ? "-k" : "", + boot_options, extra_options, + debug ? "-d 5" : "", DEVNULL); + /* Also ensure backend isn't confused by this environment var: */ + unsetenv("PGCLIENTENCODING"); + + printf(_("running database bootstrap ... ")); + PG_CMD_OPEN; - setup_auth(cmdfd); - - setup_run_file(cmdfd, system_constraints_file); - - setup_run_file(cmdfd, system_functions_file); - - setup_depend(cmdfd); - - /* - * Note that no objects created after setup_depend() will be "pinned". - * They are all droppable at the whim of the DBA. - */ - - setup_run_file(cmdfd, system_views_file); - - setup_description(cmdfd); - - setup_collation(cmdfd); - - setup_run_file(cmdfd, dictionary_file); - - setup_privileges(cmdfd); - setup_schema(cmdfd); - - load_plpgsql(cmdfd); - set_remaining_details(cmdfd); - vacuum_db(cmdfd); - - make_template0(cmdfd); - - make_postgres(cmdfd); + if (superuser_password) + PG_CMD_PRINTF("ALTER USER \"%s\" WITH PASSWORD E'%s';\n", + username, escape_quotes(superuser_password)); PG_CMD_CLOSE; @@ -3178,7 +2704,7 @@ main(int argc, char *argv[]) else printf(_("\nSync to disk skipped.\nThe data directory might become corrupt if the operating system crashes.\n")); - if (authwarning) + if (authwarning && false) { printf("\n"); pg_log_warning("enabling \"trust\" authentication for local connections"); -- 2.34.0