/* * write out the PG_VERSION file in the specified directory. If mirror is true, * mirror the file creation to our segment mirror. * * XXX: API is terrible, make it cleaner */ void set_short_version(const char *path, DbDirNode *dbDirNode, bool mirror) { char *short_version; bool gotdot = false; int end; char *fullname; FILE *version_file; /* Construct short version string (should match initdb.c) */ short_version = pstrdup(PG_VERSION); for (end = 0; short_version[end] != '\0'; end++) { if (short_version[end] == '.') { Assert(end != 0); if (gotdot) break; else gotdot = true; } else if (short_version[end] < '0' || short_version[end] > '9') { /* gone past digits and dots */ break; } } Assert(end > 0 && short_version[end - 1] != '.' && gotdot); short_version[end++] = '\n'; short_version[end] = '\0'; if (mirror) { MirroredFlatFileOpen mirroredOpen; Insist(!PointerIsValid(path)); Insist(PointerIsValid(dbDirNode)); MirroredFlatFile_OpenInDbDir(&mirroredOpen, dbDirNode, "PG_VERSION", O_CREAT | O_WRONLY | PG_BINARY, S_IRUSR | S_IWUSR, /* suppressError */ false); MirroredFlatFile_Append(&mirroredOpen, short_version, end, /* suppressError */ false); MirroredFlatFile_Flush(&mirroredOpen, /* suppressError */ false); MirroredFlatFile_Close(&mirroredOpen); } else { Insist(!PointerIsValid(dbDirNode)); Insist(PointerIsValid(path)); /* Now write the file */ fullname = palloc(strlen(path) + 11 + 1); sprintf(fullname, "%s/PG_VERSION", path); version_file = AllocateFile(fullname, PG_BINARY_W); if (version_file == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to file \"%s\": %m", fullname))); fprintf(version_file, "%s", short_version); if (FreeFile(version_file)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to file \"%s\": %m", fullname))); pfree(fullname); } pfree(short_version); }
/* * write_database_file: update the flat database file * * A side effect is to determine the oldest database's datfrozenxid * so we can set or update the XID wrap limit. * * Also, if "startup" is true, we tell relcache.c to clear out the relcache * init file in each database. That's a bit nonmodular, but scanning * pg_database twice during system startup seems too high a price for keeping * things better separated. */ static void write_database_file(Relation drel, bool startup) { StringInfoData buffer; HeapScanDesc scan; HeapTuple tuple; NameData oldest_datname; TransactionId oldest_datfrozenxid = InvalidTransactionId; MirroredFlatFileOpen mirroredOpen; initStringInfo(&buffer); MirroredFlatFile_Open( &mirroredOpen, "global", "pg_database", O_CREAT | O_TRUNC | O_WRONLY | PG_BINARY, S_IRUSR | S_IWUSR, /* suppressError */ false, /* atomic operation */ true, /*isMirrorRecovery */ false); /* * Read pg_database and write the file. */ scan = heap_beginscan(drel, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple); char *datname; Oid datoid; Oid dattablespace; TransactionId datfrozenxid; datname = NameStr(dbform->datname); datoid = HeapTupleGetOid(tuple); dattablespace = dbform->dattablespace; datfrozenxid = dbform->datfrozenxid; /* * Identify the oldest datfrozenxid. This must match * the logic in vac_truncate_clog() in vacuum.c. * * MPP-20053: Skip databases that cannot be connected to in computing * the oldest database. */ if (dbform->datallowconn && TransactionIdIsNormal(datfrozenxid)) { if (oldest_datfrozenxid == InvalidTransactionId || TransactionIdPrecedes(datfrozenxid, oldest_datfrozenxid)) { oldest_datfrozenxid = datfrozenxid; namestrcpy(&oldest_datname, datname); } } /* * Check for illegal characters in the database name. */ if (!name_okay(datname)) { ereport(LOG, (errmsg("invalid database name \"%s\"", datname))); continue; } /* * The file format is: "dbname" oid tablespace frozenxid * * The xids are not needed for backend startup, but are of use to * autovacuum, and might also be helpful for forensic purposes. */ sputs_quote(&buffer, datname); appendStringInfo(&buffer, " %u %u %u\n", datoid, dattablespace, datfrozenxid); /* * MPP-10111 - During database expansion we need to be able to bring a * database up in order to correct the filespace locations in the * catalog. At this point we will not be able to resolve database paths * for databases not stored in "pg_default" or "pg_global". * * This is solved by passing a special guc to the startup during this * phase of expand to bypass logic involving non-system tablespaces. * Since we are bypassing the clearing of the relation cache on these * databases we need to ensure that we don't try to use them at all * elsewhere. This is done with a similar check in * PersistentTablespace_GetPrimaryAndMirrorFilespaces(). */ if (gp_before_filespace_setup && !IsBuiltinTablespace(dattablespace)) continue; } heap_endscan(scan); MirroredFlatFile_Append(&mirroredOpen, buffer.data, buffer.len, /* suppressError */ false); MirroredFlatFile_Flush(&mirroredOpen, /* suppressError */ false); MirroredFlatFile_Close(&mirroredOpen); if (buffer.maxlen > 0) pfree(buffer.data); /* * Set the transaction ID wrap limit using the oldest datfrozenxid */ if (oldest_datfrozenxid != InvalidTransactionId) SetTransactionIdLimit(oldest_datfrozenxid, &oldest_datname); }