📄 pg_backup_archiver.c
字号:
/* * On Windows, we need to use binary mode to read/write non-text archive * formats. Force stdin/stdout into binary mode if that is what we are * using. */#ifdef WIN32 if (fmt != archNull && (AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0)) { if (mode == archModeWrite) setmode(fileno(stdout), O_BINARY); else setmode(fileno(stdin), O_BINARY); }#endif#if 0 write_msg(modulename, "archive format is %d\n", fmt);#endif if (fmt == archUnknown) AH->format = _discoverArchiveFormat(AH); else AH->format = fmt; switch (AH->format) { case archCustom: InitArchiveFmt_Custom(AH); break; case archFiles: InitArchiveFmt_Files(AH); break; case archNull: InitArchiveFmt_Null(AH); break; case archTar: InitArchiveFmt_Tar(AH); break; default: die_horribly(AH, modulename, "unrecognized file format \"%d\"\n", fmt); } /* sql error handling */ AH->public.exit_on_error = true; AH->public.n_errors = 0; return AH;}voidWriteDataChunks(ArchiveHandle *AH){ TocEntry *te = AH->toc->next; StartDataPtr startPtr; EndDataPtr endPtr; while (te != AH->toc) { if (te->dataDumper != NULL) { AH->currToc = te; /* printf("Writing data for %d (%x)\n", te->id, te); */ if (strcmp(te->desc, "BLOBS") == 0) { startPtr = AH->StartBlobsPtr; endPtr = AH->EndBlobsPtr; } else { startPtr = AH->StartDataPtr; endPtr = AH->EndDataPtr; } if (startPtr != NULL) (*startPtr) (AH, te); /* * printf("Dumper arg for %d is %x\n", te->id, te->dataDumperArg); */ /* * The user-provided DataDumper routine needs to call * AH->WriteData */ (*te->dataDumper) ((Archive *) AH, te->dataDumperArg); if (endPtr != NULL) (*endPtr) (AH, te); AH->currToc = NULL; } te = te->next; }}voidWriteToc(ArchiveHandle *AH){ TocEntry *te; char workbuf[32]; int i; /* printf("%d TOC Entries to save\n", AH->tocCount); */ WriteInt(AH, AH->tocCount); for (te = AH->toc->next; te != AH->toc; te = te->next) { WriteInt(AH, te->dumpId); WriteInt(AH, te->dataDumper ? 1 : 0); /* OID is recorded as a string for historical reasons */ sprintf(workbuf, "%u", te->catalogId.tableoid); WriteStr(AH, workbuf); sprintf(workbuf, "%u", te->catalogId.oid); WriteStr(AH, workbuf); WriteStr(AH, te->tag); WriteStr(AH, te->desc); WriteStr(AH, te->defn); WriteStr(AH, te->dropStmt); WriteStr(AH, te->copyStmt); WriteStr(AH, te->namespace); WriteStr(AH, te->tablespace); WriteStr(AH, te->owner); WriteStr(AH, te->withOids ? "true" : "false"); /* Dump list of dependencies */ for (i = 0; i < te->nDeps; i++) { sprintf(workbuf, "%d", te->dependencies[i]); WriteStr(AH, workbuf); } WriteStr(AH, NULL); /* Terminate List */ if (AH->WriteExtraTocPtr) (*AH->WriteExtraTocPtr) (AH, te); }}voidReadToc(ArchiveHandle *AH){ int i; char *tmp; DumpId *deps; int depIdx; int depSize; TocEntry *te = AH->toc->next; AH->tocCount = ReadInt(AH); AH->maxDumpId = 0; for (i = 0; i < AH->tocCount; i++) { te = (TocEntry *) calloc(1, sizeof(TocEntry)); te->dumpId = ReadInt(AH); if (te->dumpId > AH->maxDumpId) AH->maxDumpId = te->dumpId; /* Sanity check */ if (te->dumpId <= 0) die_horribly(AH, modulename, "entry ID %d out of range -- perhaps a corrupt TOC\n", te->dumpId); te->hadDumper = ReadInt(AH); if (AH->version >= K_VERS_1_8) { tmp = ReadStr(AH); sscanf(tmp, "%u", &te->catalogId.tableoid); free(tmp); } else te->catalogId.tableoid = InvalidOid; tmp = ReadStr(AH); sscanf(tmp, "%u", &te->catalogId.oid); free(tmp); te->tag = ReadStr(AH); te->desc = ReadStr(AH); te->defn = ReadStr(AH); te->dropStmt = ReadStr(AH); if (AH->version >= K_VERS_1_3) te->copyStmt = ReadStr(AH); if (AH->version >= K_VERS_1_6) te->namespace = ReadStr(AH); if (AH->version >= K_VERS_1_10) te->tablespace = ReadStr(AH); te->owner = ReadStr(AH); if (AH->version >= K_VERS_1_9) { if (strcmp(ReadStr(AH), "true") == 0) te->withOids = true; else te->withOids = false; } else te->withOids = true; /* Read TOC entry dependencies */ if (AH->version >= K_VERS_1_5) { depSize = 100; deps = (DumpId *) malloc(sizeof(DumpId) * depSize); depIdx = 0; for (;;) { tmp = ReadStr(AH); if (!tmp) break; /* end of list */ if (depIdx >= depSize) { depSize *= 2; deps = (DumpId *) realloc(deps, sizeof(DumpId) * depSize); } sscanf(tmp, "%d", &deps[depIdx]); free(tmp); depIdx++; } if (depIdx > 0) /* We have a non-null entry */ { deps = (DumpId *) realloc(deps, sizeof(DumpId) * depIdx); te->dependencies = deps; te->nDeps = depIdx; } else { free(deps); te->dependencies = NULL; te->nDeps = 0; } } else { te->dependencies = NULL; te->nDeps = 0; } if (AH->ReadExtraTocPtr) (*AH->ReadExtraTocPtr) (AH, te); ahlog(AH, 3, "read TOC entry %d (ID %d) for %s %s\n", i, te->dumpId, te->desc, te->tag); te->prev = AH->toc->prev; AH->toc->prev->next = te; AH->toc->prev = te; te->next = AH->toc; }}static teReqs_tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls){ teReqs res = REQ_ALL; /* ENCODING objects are dumped specially, so always reject here */ if (strcmp(te->desc, "ENCODING") == 0) return 0; /* If it's an ACL, maybe ignore it */ if ((!include_acls || ropt->aclsSkip) && strcmp(te->desc, "ACL") == 0) return 0; if (!ropt->create && strcmp(te->desc, "DATABASE") == 0) return 0; /* Check options for selective dump/restore */ if (ropt->schemaNames) { /* If no namespace is specified, it means all. */ if (!te->namespace) return 0; if (strcmp(ropt->schemaNames, te->namespace) != 0) return 0; } if (ropt->selTypes) { if (strcmp(te->desc, "TABLE") == 0 || strcmp(te->desc, "TABLE DATA") == 0) { if (!ropt->selTable) return 0; if (ropt->tableNames && strcmp(ropt->tableNames, te->tag) != 0) return 0; } else if (strcmp(te->desc, "INDEX") == 0) { if (!ropt->selIndex) return 0; if (ropt->indexNames && strcmp(ropt->indexNames, te->tag) != 0) return 0; } else if (strcmp(te->desc, "FUNCTION") == 0) { if (!ropt->selFunction) return 0; if (ropt->functionNames && strcmp(ropt->functionNames, te->tag) != 0) return 0; } else if (strcmp(te->desc, "TRIGGER") == 0) { if (!ropt->selTrigger) return 0; if (ropt->triggerNames && strcmp(ropt->triggerNames, te->tag) != 0) return 0; } else return 0; } /* * Check if we had a dataDumper. Indicates if the entry is schema or data */ if (!te->hadDumper) { /* * Special Case: If 'SEQUENCE SET' then it is considered a data entry */ if (strcmp(te->desc, "SEQUENCE SET") == 0) res = res & REQ_DATA; else res = res & ~REQ_DATA; } /* * Special case: <Init> type with <Max OID> tag; this is obsolete and we * always ignore it. */ if ((strcmp(te->desc, "<Init>") == 0) && (strcmp(te->tag, "Max OID") == 0)) return 0; /* Mask it if we only want schema */ if (ropt->schemaOnly) res = res & REQ_SCHEMA; /* Mask it we only want data */ if (ropt->dataOnly) res = res & REQ_DATA; /* Mask it if we don't have a schema contribution */ if (!te->defn || strlen(te->defn) == 0) res = res & ~REQ_SCHEMA; /* Finally, if we used a list, limit based on that as well */ if (ropt->limitToList && !ropt->idWanted[te->dumpId - 1]) return 0; return res;}/* * Issue SET commands for parameters that we want to have set the same way * at all times during execution of a restore script. */static void_doSetFixedOutputState(ArchiveHandle *AH){ TocEntry *te; /* If we have an encoding setting, emit that */ te = AH->toc->next; while (te != AH->toc) { if (strcmp(te->desc, "ENCODING") == 0) { ahprintf(AH, "%s", te->defn); break; } te = te->next; } /* Make sure function checking is disabled */ ahprintf(AH, "SET check_function_bodies = false;\n"); /* Avoid annoying notices etc */ ahprintf(AH, "SET client_min_messages = warning;\n"); ahprintf(AH, "\n");}/* * Issue a SET SESSION AUTHORIZATION command. Caller is responsible * for updating state if appropriate. If user is NULL or an empty string, * the specification DEFAULT will be used. */static void_doSetSessionAuth(ArchiveHandle *AH, const char *user){ PQExpBuffer cmd = createPQExpBuffer(); appendPQExpBuffer(cmd, "SET SESSION AUTHORIZATION "); /* * SQL requires a string literal here. Might as well be correct. */ if (user && *user) appendStringLiteral(cmd, user, false); else appendPQExpBuffer(cmd, "DEFAULT"); appendPQExpBuffer(cmd, ";"); if (RestoringToDB(AH)) { PGresult *res; res = PQexec(AH->connection, cmd->data); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) /* NOT warn_or_die_horribly... use -O instead to skip this. */ die_horribly(AH, modulename, "could not set session user to \"%s\": %s", user, PQerrorMessage(AH->connection)); PQclear(res); } else ahprintf(AH, "%s\n\n", cmd->data); destroyPQExpBuffer(cmd);}/* * Issue a SET default_with_oids command. Caller is responsible * for updating state if appropriate. */static void_doSetWithOids(ArchiveHandle *AH, const bool withOids){ PQExpBuffer cmd = createPQExpBuffer(); appendPQExpBuffer(cmd, "SET default_with_oids = %s;", withOids ? "true" : "false"); if (RestoringToDB(AH)) { PGresult *res; res = PQexec(AH->connection, cmd->data); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) warn_or_die_horribly(AH, modulename, "could not set default_with_oids: %s", PQerrorMessage(AH->connection)); PQclear(res); } else ahprintf(AH, "%s\n\n", cmd->data); destroyPQExpBuffer(cmd);}/* * Issue the commands to connect to the specified database. * * If we're currently restoring right into a database, this will * actually establish a connection. Otherwise it puts a \connect into * the script output. * * NULL dbname implies reconnecting to the current DB (pretty useless). */static void_reconnectToDB(ArchiveHandle *AH, const char *dbname){ if (RestoringToDB(AH)) ReconnectToServer(AH, dbname, NULL); else { PQExpBuffer qry = createPQExpBuffer(); appendPQExpBuffer(qry, "\\connect %s\n\n", dbname ? fmtId(dbname) : "-"); ahprintf(AH, "%s", qry->data); destroyPQExpBuffer(qry); } /* * NOTE: currUser keeps track of what the imaginary session user in our * script is. It's now effectively reset to the original userID. */ if (AH->currUser) free(AH->currUser); AH->currUser = strdup(""); /* don't assume we still know the output schema */ if (AH->currSchema) free(AH->currSchema); AH->currSchema = strdup(""); AH->currWithOids = -1; /* re-establish fixed state */ _doSetFixedOutputState(AH);}/* * Become the specified user, and update state to avoid redundant commands * * NULL or empty argument is taken to mean restoring the session default */static void_becomeUser(ArchiveHandle *AH, const char *user){ if (!user) user = ""; /* avoid null pointers */ if (AH->currUser && strcmp(AH->currUser, user) == 0) return; /* no need to do anything */ _doSetSessionAuth(AH, user); /* * NOTE: currUser keeps track of what the imaginary session user in our * script is */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -