/* * Copy a file from source to target, between 'begin' and 'end' offsets. */ static void copy_file_range(const char *path, off_t begin, off_t end, bool trunc) { char buf[BLCKSZ]; char srcpath[MAXPGPATH]; int srcfd; snprintf(srcpath, sizeof(srcpath), "%s/%s", datadir_source, path); srcfd = open(srcpath, O_RDONLY | PG_BINARY, 0); if (srcfd < 0) { fprintf(stderr, "could not open source file \"%s\": %s\n", srcpath, strerror(errno)); exit(1); } if (lseek(srcfd, begin, SEEK_SET) == -1) { fprintf(stderr, "could not seek in source file: %s\n", strerror(errno)); exit(1); } open_target_file(path, trunc); while (end - begin > 0) { int readlen; int len; if (end - begin > sizeof(buf)) len = sizeof(buf); else len = end - begin; readlen = read(srcfd, buf, len); if (readlen < 0) { fprintf(stderr, "could not read file \"%s\": %s\n", srcpath, strerror(errno)); exit(1); } else if (readlen == 0) { fprintf(stderr, "unexpected EOF while reading file \"%s\"\n", srcpath); exit(1); } write_file_range(buf, begin, readlen); begin += readlen; } }
/* * 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 int4 -- 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) { fprintf(stderr, "could not send query: %s\n", PQerrorMessage(conn)); exit(1); } if (verbose) fprintf(stderr, "getting chunks: %s\n", sql); if (PQsetSingleRowMode(conn) != 1) { fprintf(stderr, "could not set libpq connection to single row mode\n"); exit(1); } if (verbose) fprintf(stderr, "sent query\n"); while ((res = PQgetResult(conn)) != NULL) { char *filename; int filenamelen; int chunkoff; int chunksize; char *chunk; switch(PQresultStatus(res)) { case PGRES_SINGLE_TUPLE: break; case PGRES_TUPLES_OK: continue; /* final zero-row result */ default: fprintf(stderr, "unexpected result while fetching remote files: %s\n", PQresultErrorMessage(res)); exit(1); } /* sanity check the result set */ if (!(PQnfields(res) == 3 && PQntuples(res) == 1)) { fprintf(stderr, "unexpected result set size while fetching remote files\n"); exit(1); } if (!(PQftype(res, 0) == TEXTOID && PQftype(res, 1) == INT4OID && PQftype(res, 2) == BYTEAOID)) { fprintf(stderr, "unexpected data types in result set while fetching remote files: %u %u %u\n", PQftype(res, 0), PQftype(res, 1), PQftype(res, 2)); exit(1); } if (!(PQfformat(res, 0) == 1 && PQfformat(res, 1) == 1 && PQfformat(res, 2) == 1)) { fprintf(stderr, "unexpected result format while fetching remote files\n"); exit(1); } if (!(!PQgetisnull(res, 0, 0) && !PQgetisnull(res, 0, 1) && !PQgetisnull(res, 0, 2) && PQgetlength(res, 0, 1) == sizeof(int32))) { fprintf(stderr, "unexpected result set while fetching remote files\n"); exit(1); } /* Read result set to local variables */ memcpy(&chunkoff, PQgetvalue(res, 0, 1), sizeof(int32)); chunkoff = ntohl(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 (verbose) fprintf(stderr, "received chunk for file \"%s\", off %d, len %d\n", filename, chunkoff, chunksize); open_target_file(filename, false); write_file_range(chunk, chunkoff, chunksize); } }