void remove_target(file_entry_t *entry) { Assert(entry->action == FILE_ACTION_REMOVE); switch (entry->type) { case FILE_TYPE_DIRECTORY: remove_target_dir(entry->path); break; case FILE_TYPE_REGULAR: remove_target_file(entry->path); break; case FILE_TYPE_SYMLINK: remove_target_symlink(entry->path); break; } }
/* * Copy all relation data files from datadir_source to datadir_target, which * are marked in the given data page map. */ void copy_executeFileMap(filemap_t *map) { file_entry_t *entry; for (entry = map->first; entry != NULL; entry = entry->next) { execute_pagemap(&entry->pagemap, entry->path); switch (entry->action) { case FILE_ACTION_NONE: /* ok, do nothing.. */ break; case FILE_ACTION_COPY: copy_file_range(entry->path, 0, entry->newsize, true); break; case FILE_ACTION_REMOVE: remove_target_file(entry->path); break; case FILE_ACTION_TRUNCATE: truncate_target_file(entry->path, entry->newsize); break; case FILE_ACTION_COPY_TAIL: copy_file_range(entry->path, entry->oldsize, entry->newsize, false); break; case FILE_ACTION_CREATEDIR: create_target_dir(entry->path); break; } } if (dstfd != -1) close_target_file(); }
/*---- * Runs a query, which returns pieces of files from the remote source data * directory, and overwrites the corresponding parts of target files with * the received parts. The result set is expected to be of format: * * path text -- path in the data directory, e.g "base/1/123" * begin int8 -- offset within the file * chunk bytea -- file content *---- */ static void receiveFileChunks(const char *sql) { PGresult *res; if (PQsendQueryParams(conn, sql, 0, NULL, NULL, NULL, NULL, 1) != 1) pg_fatal("could not send query: %s", PQerrorMessage(conn)); pg_log(PG_DEBUG, "getting file chunks\n"); if (PQsetSingleRowMode(conn) != 1) pg_fatal("could not set libpq connection to single row mode\n"); while ((res = PQgetResult(conn)) != NULL) { char *filename; int filenamelen; int64 chunkoff; char chunkoff_str[32]; int chunksize; char *chunk; switch (PQresultStatus(res)) { case PGRES_SINGLE_TUPLE: break; case PGRES_TUPLES_OK: PQclear(res); continue; /* final zero-row result */ default: pg_fatal("unexpected result while fetching remote files: %s", PQresultErrorMessage(res)); } /* sanity check the result set */ if (PQnfields(res) != 3 || PQntuples(res) != 1) pg_fatal("unexpected result set size while fetching remote files\n"); if (PQftype(res, 0) != TEXTOID || PQftype(res, 1) != INT8OID || PQftype(res, 2) != BYTEAOID) { pg_fatal("unexpected data types in result set while fetching remote files: %u %u %u\n", PQftype(res, 0), PQftype(res, 1), PQftype(res, 2)); } if (PQfformat(res, 0) != 1 && PQfformat(res, 1) != 1 && PQfformat(res, 2) != 1) { pg_fatal("unexpected result format while fetching remote files\n"); } if (PQgetisnull(res, 0, 0) || PQgetisnull(res, 0, 1)) { pg_fatal("unexpected null values in result while fetching remote files\n"); } if (PQgetlength(res, 0, 1) != sizeof(int64)) pg_fatal("unexpected result length while fetching remote files\n"); /* Read result set to local variables */ memcpy(&chunkoff, PQgetvalue(res, 0, 1), sizeof(int64)); chunkoff = pg_recvint64(chunkoff); chunksize = PQgetlength(res, 0, 2); filenamelen = PQgetlength(res, 0, 0); filename = pg_malloc(filenamelen + 1); memcpy(filename, PQgetvalue(res, 0, 0), filenamelen); filename[filenamelen] = '\0'; chunk = PQgetvalue(res, 0, 2); /* * If a file has been deleted on the source, remove it on the target * as well. Note that multiple unlink() calls may happen on the same * file if multiple data chunks are associated with it, hence ignore * unconditionally anything missing. If this file is not a relation * data file, then it has been already truncated when creating the * file chunk list at the previous execution of the filemap. */ if (PQgetisnull(res, 0, 2)) { pg_log(PG_DEBUG, "received null value for chunk for file \"%s\", file has been deleted\n", filename); remove_target_file(filename, true); pg_free(filename); PQclear(res); continue; } /* * Separate step to keep platform-dependent format code out of * translatable strings. */ snprintf(chunkoff_str, sizeof(chunkoff_str), INT64_FORMAT, chunkoff); pg_log(PG_DEBUG, "received chunk for file \"%s\", offset %s, size %d\n", filename, chunkoff_str, chunksize); open_target_file(filename, false); write_target_range(chunk, chunkoff, chunksize); pg_free(filename); PQclear(res); } }
/* * Fetch all changed blocks from remote source data directory. */ void libpq_executeFileMap(filemap_t *map) { file_entry_t *entry; const char *sql; PGresult *res; /* * First create a temporary table, and load it with the blocks that * we need to fetch. */ sql = "create temporary table fetchchunks(path text, begin int4, len int4);"; res = PQexec(conn, sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "error creating temporary table: %s\n", PQresultErrorMessage(res)); exit(1); } sql = "copy fetchchunks from stdin"; res = PQexec(conn, sql); if (PQresultStatus(res) != PGRES_COPY_IN) { fprintf(stderr, "unexpected result while sending file list: %s\n", PQresultErrorMessage(res)); exit(1); } for (entry = map->first; entry != NULL; entry = entry->next) { execute_pagemap(&entry->pagemap, entry->path); switch (entry->action) { case FILE_ACTION_NONE: /* ok, do nothing.. */ break; case FILE_ACTION_COPY: /* Truncate the old file out of the way, if any */ open_target_file(entry->path, true); copy_file_range(entry->path, 0, entry->newsize); break; case FILE_ACTION_REMOVE: remove_target_file(entry->path); break; case FILE_ACTION_TRUNCATE: truncate_target_file(entry->path, entry->newsize); break; case FILE_ACTION_COPY_TAIL: copy_file_range(entry->path, entry->oldsize, entry->newsize); break; case FILE_ACTION_CREATEDIR: create_target_dir(entry->path); break; case FILE_ACTION_REMOVEDIR: remove_target_dir(entry->path); break; } } if (PQputCopyEnd(conn, NULL) != 1) { fprintf(stderr, "error sending end-of-COPY: %s\n", PQerrorMessage(conn)); exit(1); } while ((res = PQgetResult(conn)) != NULL) { if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "unexpected result while sending file list: %s\n", PQresultErrorMessage(res)); exit(1); } } /* Ok, we've sent the file list. Now receive the files */ sql = "-- fetch all the blocks listed in the temp table.\n" "select path, begin, \n" " pg_read_binary_file(path, begin, len) as chunk\n" "from fetchchunks\n"; receiveFileChunks(sql); }