/* * Dump user-specific configuration */ static void dumpUserConfig(PGconn *conn, const char *username) { PQExpBuffer buf = createPQExpBuffer(); int count = 1; for (;;) { PGresult *res; printfPQExpBuffer(buf, "SELECT rolconfig[%d] FROM pg_authid WHERE rolname = ", count); appendStringLiteralConn(buf, username, conn); res = executeQuery(conn, buf->data); if (PQntuples(res) == 1 && !PQgetisnull(res, 0, 0)) { makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0), "ROLE", username); PQclear(res); count++; } else { PQclear(res); break; } } destroyPQExpBuffer(buf); }
/* * Helper function for dumpXXXConfig(). */ static void makeAlterConfigCommand(PGconn *conn, const char *arrayitem, const char *type, const char *name) { char *pos; char *mine; PQExpBuffer buf = createPQExpBuffer(); mine = strdup(arrayitem); pos = strchr(mine, '='); if (pos == NULL) return; *pos = 0; appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name)); appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine)); /* * Some GUC variable names are 'LIST' type and hence must not be quoted. */ if (pg_strcasecmp(mine, "DateStyle") == 0 || pg_strcasecmp(mine, "search_path") == 0) appendPQExpBuffer(buf, "%s", pos + 1); else appendStringLiteralConn(buf, pos + 1, conn); appendPQExpBuffer(buf, ";\n"); fprintf(OPF, "%s", buf->data); destroyPQExpBuffer(buf); free(mine); }
/* * This function takes a function description, e.g. "x" or "x(int)", and * issues a query on the given connection to retrieve the function's OID * using a cast to regproc or regprocedure (as appropriate). The result, * if there is one, is returned at *foid. Note that we'll fail if the * function doesn't exist OR if there are multiple matching candidates * OR if there's something syntactically wrong with the function description; * unfortunately it can be hard to tell the difference. */ static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid) { bool result = true; PQExpBuffer query; PGresult *res; query = createPQExpBuffer(); printfPQExpBuffer(query, "SELECT "); appendStringLiteralConn(query, desc, conn); appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid", strchr(desc, '(') ? "regprocedure" : "regproc"); res = PQexec(conn, query->data); if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) *foid = atooid(PQgetvalue(res, 0, 0)); else { minimal_error_message(res); result = false; } PQclear(res); destroyPQExpBuffer(query); return result; }
/* * Dump database-specific configuration */ static void dumpDatabaseConfig(PGconn *conn, const char *dbname) { PQExpBuffer buf = createPQExpBuffer(); int count = 1; for (;;) { PGresult *res; printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE datname = ", count); appendStringLiteralConn(buf, dbname, conn); appendPQExpBuffer(buf, ";"); res = executeQuery(conn, buf->data); if (!PQgetisnull(res, 0, 0)) { makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0), "DATABASE", dbname); PQclear(res); count++; } else { PQclear(res); break; } } destroyPQExpBuffer(buf); }
/* * Handle one of the "string" options of COPY. If the user gave a quoted * string, pass it to the backend as-is; if it wasn't quoted then quote * and escape it. */ static void emit_copy_option(PQExpBuffer query, const char *keyword, const char *option) { appendPQExpBufferStr(query, keyword); if (option[0] == '\'' || ((option[0] == 'E' || option[0] == 'e') && option[1] == '\'')) appendPQExpBufferStr(query, option); else appendStringLiteralConn(query, option, pset.db); }
/* * Dump user-specific configuration */ static void dumpUserConfig(PGconn *conn, const char *username) { PQExpBuffer buf = createPQExpBuffer(); int count = 1; for (;;) { PGresult *res; if (server_version >= 90000) printfPQExpBuffer(buf, "SELECT setconfig[%d] FROM pg_db_role_setting WHERE " "setdatabase = 0 AND setrole = " "(SELECT oid FROM pg_authid WHERE rolname = ", count); else if (server_version >= 80100) printfPQExpBuffer(buf, "SELECT rolconfig[%d] FROM pg_authid WHERE rolname = ", count); else printfPQExpBuffer(buf, "SELECT useconfig[%d] FROM pg_shadow WHERE usename = ", count); appendStringLiteralConn(buf, username, conn); if (server_version >= 90000) appendPQExpBuffer(buf, ")"); res = executeQuery(conn, buf->data); if (PQntuples(res) == 1 && !PQgetisnull(res, 0, 0)) { makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0), "ROLE", username, NULL, NULL); PQclear(res); count++; } else { PQclear(res); break; } } destroyPQExpBuffer(buf); }
/* * emitShSecLabels * * Format security label data retrieved by the query generated in * buildShSecLabelQuery. */ void emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, const char *target, const char *objname) { int i; for (i = 0; i < PQntuples(res); i++) { char *provider = PQgetvalue(res, i, 0); char *label = PQgetvalue(res, i, 1); /* must use fmtId result before calling it again */ appendPQExpBuffer(buffer, "SECURITY LABEL FOR %s ON %s", fmtId(provider), target); appendPQExpBuffer(buffer, " %s IS ", fmtId(objname)); appendStringLiteralConn(buffer, label, conn); appendPQExpBuffer(buffer, ";\n"); } }
int main(int argc, char *argv[]) { static struct option long_options[] = { {"host", required_argument, NULL, 'h'}, {"port", required_argument, NULL, 'p'}, {"username", required_argument, NULL, 'U'}, {"no-password", no_argument, NULL, 'w'}, {"password", no_argument, NULL, 'W'}, {"echo", no_argument, NULL, 'e'}, {"createdb", no_argument, NULL, 'd'}, {"no-createdb", no_argument, NULL, 'D'}, {"superuser", no_argument, NULL, 's'}, {"no-superuser", no_argument, NULL, 'S'}, {"createrole", no_argument, NULL, 'r'}, {"no-createrole", no_argument, NULL, 'R'}, {"inherit", no_argument, NULL, 'i'}, {"no-inherit", no_argument, NULL, 'I'}, {"login", no_argument, NULL, 'l'}, {"no-login", no_argument, NULL, 'L'}, /* adduser is obsolete, undocumented spelling of superuser */ {"adduser", no_argument, NULL, 'a'}, {"no-adduser", no_argument, NULL, 'A'}, {"connection-limit", required_argument, NULL, 'c'}, {"pwprompt", no_argument, NULL, 'P'}, {"encrypted", no_argument, NULL, 'E'}, {"unencrypted", no_argument, NULL, 'N'}, {NULL, 0, NULL, 0} }; const char *progname; int optindex; int c; char* newuser = NULL; char* host = NULL; char* port = NULL; char* username = NULL; enum trivalue prompt_password = TRI_DEFAULT; bool echo = false; char* conn_limit = NULL; bool pwprompt = false; char* newpassword = NULL; /* Tri-valued variables. */ enum trivalue createdb = TRI_DEFAULT; enum trivalue superuser = TRI_DEFAULT; enum trivalue createrole = TRI_DEFAULT; enum trivalue inherit = TRI_DEFAULT; enum trivalue login = TRI_DEFAULT; enum trivalue encrypted = TRI_DEFAULT; struct pqbuf sql; PGconn* conn; PGresult* result; progname = get_progname(argv[0]); set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts")); handle_help_version_opts(argc, argv, "createuser", help); while ((c = getopt_long(argc, argv, "h:p:U:wWedDsSaArRiIlLc:PEN", long_options, &optindex)) != -1) { switch (c) { case 'h': host = optarg; break; case 'p': port = optarg; break; case 'U': username = optarg; break; case 'w': prompt_password = TRI_NO; break; case 'W': prompt_password = TRI_YES; break; case 'e': echo = true; break; case 'd': createdb = TRI_YES; break; case 'D': createdb = TRI_NO; break; case 's': case 'a': superuser = TRI_YES; break; case 'S': case 'A': superuser = TRI_NO; break; case 'r': createrole = TRI_YES; break; case 'R': createrole = TRI_NO; break; case 'i': inherit = TRI_YES; break; case 'I': inherit = TRI_NO; break; case 'l': login = TRI_YES; break; case 'L': login = TRI_NO; break; case 'c': conn_limit = optarg; break; case 'P': pwprompt = true; break; case 'E': encrypted = TRI_YES; break; case 'N': encrypted = TRI_NO; break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } } switch (argc - optind) { case 0: break; case 1: newuser = argv[optind]; break; default: fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind + 1]); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } if (newuser == NULL) newuser = simple_prompt("Enter name of role to add: ", 128, true); if (pwprompt) { char* pw1; char* pw2; pw1 = simple_prompt("Enter password for new role: ", 100, false); pw2 = simple_prompt("Enter it again: ", 100, false); if (strcmp(pw1, pw2) != 0) { fprintf(stderr, _("Passwords didn't match.\n")); exit(1); } newpassword = pw1; free(pw2); } if (superuser == 0) { if (yesno_prompt("Shall the new role be a superuser?")) superuser = TRI_YES; else superuser = TRI_NO; } if (superuser == TRI_YES) { /* Not much point in trying to restrict a superuser */ createdb = TRI_YES; createrole = TRI_YES; } if (createdb == 0) { if (yesno_prompt("Shall the new role be allowed to create databases?")) createdb = TRI_YES; else createdb = TRI_NO; } if (createrole == 0) { if (yesno_prompt("Shall the new role be allowed to create more new roles?")) createrole = TRI_YES; else createrole = TRI_NO; } if (inherit == 0) inherit = TRI_YES; if (login == 0) login = TRI_YES; conn = connectDatabase("postgres", host, port, username, prompt_password, progname); init_pqbuf(&sql); print_pqbuf(&sql, "CREATE ROLE %s", fmtId(newuser)); if (newpassword) { if (encrypted == TRI_YES) append_pqbuf(&sql, " ENCRYPTED"); if (encrypted == TRI_NO) append_pqbuf(&sql, " UNENCRYPTED"); append_pqbuf(&sql, " PASSWORD "); if (encrypted != TRI_NO) { char* encrypted_password; encrypted_password = PQencryptPassword(newpassword, newuser); if (!encrypted_password) { fprintf(stderr, _("Password encryption failed.\n")); exit(1); } appendStringLiteralConn(&sql, encrypted_password, conn); PQfreemem(encrypted_password); } else { appendStringLiteralConn(&sql, newpassword, conn); } } if (superuser == TRI_YES) append_pqbuf(&sql, " SUPERUSER"); if (superuser == TRI_NO) append_pqbuf(&sql, " NOSUPERUSER"); if (createdb == TRI_YES) append_pqbuf(&sql, " CREATEDB"); if (createdb == TRI_NO) append_pqbuf(&sql, " NOCREATEDB"); if (createrole == TRI_YES) append_pqbuf(&sql, " CREATEROLE"); if (createrole == TRI_NO) append_pqbuf(&sql, " NOCREATEROLE"); if (inherit == TRI_YES) append_pqbuf(&sql, " INHERIT"); if (inherit == TRI_NO) append_pqbuf(&sql, " NOINHERIT"); if (login == TRI_YES) append_pqbuf(&sql, " LOGIN"); if (login == TRI_NO) append_pqbuf(&sql, " NOLOGIN"); if (conn_limit != NULL) append_pqbuf(&sql, " CONNECTION LIMIT %s", conn_limit); append_pqbuf(&sql, ";\n"); if (echo) printf("%s", sql.data); result = PQexec(conn, sql.data); if (PQresultStatus(result) != PGRES_COMMAND_OK) { fprintf(stderr, _("%s: creation of new role failed: %s"), progname, PQerrorMessage(conn)); PQfinish(conn); exit(1); } PQclear(result); PQfinish(conn); exit(0); }
/* * Dump roles */ static void dumpRoles(PGconn *conn) { PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i_rolname, i_rolsuper, i_rolinherit, i_rolcreaterole, i_rolcreatedb, i_rolcatupdate, i_rolcanlogin, i_rolconnlimit, i_rolpassword, i_rolvaliduntil, i_rolcomment, i_rolqueuename = -1, /* keep compiler quiet */ i_rolcreaterextgpfd = -1, i_rolcreaterexthttp = -1, i_rolcreatewextgpfd = -1, i_rolcreaterexthdfs = -1, i_rolcreatewexthdfs = -1; int i; bool exttab_auth = (server_version >= 80214); bool hdfs_auth = (server_version >= 80215); char *resq_col = resource_queues ? ", (SELECT rsqname FROM pg_resqueue WHERE " " pg_resqueue.oid = rolresqueue) AS rolqueuename " : ""; char *extauth_col = exttab_auth ? ", rolcreaterextgpfd, rolcreaterexthttp, rolcreatewextgpfd" : ""; char *hdfs_col = hdfs_auth ? ", rolcreaterexthdfs, rolcreatewexthdfs " : ""; /* * Query to select role info get resqueue if version support it get * external table auth on gpfdist, gpfdists and http if version support it get * external table auth on gphdfs if version support it note: rolconfig is * dumped later */ printfPQExpBuffer(buf, "SELECT rolname, rolsuper, rolinherit, " "rolcreaterole, rolcreatedb, rolcatupdate, " "rolcanlogin, rolconnlimit, rolpassword, " "rolvaliduntil, " "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment " " %s %s %s" "FROM pg_authid " "ORDER BY 1", resq_col, extauth_col, hdfs_col); res = executeQuery(conn, buf->data); i_rolname = PQfnumber(res, "rolname"); i_rolsuper = PQfnumber(res, "rolsuper"); i_rolinherit = PQfnumber(res, "rolinherit"); i_rolcreaterole = PQfnumber(res, "rolcreaterole"); i_rolcreatedb = PQfnumber(res, "rolcreatedb"); i_rolcatupdate = PQfnumber(res, "rolcatupdate"); i_rolcanlogin = PQfnumber(res, "rolcanlogin"); i_rolconnlimit = PQfnumber(res, "rolconnlimit"); i_rolpassword = PQfnumber(res, "rolpassword"); i_rolvaliduntil = PQfnumber(res, "rolvaliduntil"); i_rolcomment = PQfnumber(res, "rolcomment"); if (resource_queues) i_rolqueuename = PQfnumber(res, "rolqueuename"); if (exttab_auth) { i_rolcreaterextgpfd = PQfnumber(res, "rolcreaterextgpfd"); i_rolcreaterexthttp = PQfnumber(res, "rolcreaterexthttp"); i_rolcreatewextgpfd = PQfnumber(res, "rolcreatewextgpfd"); if (hdfs_auth) { i_rolcreaterexthdfs = PQfnumber(res, "rolcreaterexthdfs"); i_rolcreatewexthdfs = PQfnumber(res, "rolcreatewexthdfs"); } } if (PQntuples(res) > 0) fprintf(OPF, "--\n-- Roles\n--\n\n"); for (i = 0; i < PQntuples(res); i++) { const char *rolename; rolename = PQgetvalue(res, i, i_rolname); resetPQExpBuffer(buf); if (output_clean) appendPQExpBuffer(buf, "DROP ROLE %s;\n", fmtId(rolename)); /* * We dump CREATE ROLE followed by ALTER ROLE to ensure that the role * will acquire the right properties even if it already exists. (The * above DROP may therefore seem redundant, but it isn't really, * because this technique doesn't get rid of role memberships.) */ appendPQExpBuffer(buf, "CREATE ROLE %s;\n", fmtId(rolename)); appendPQExpBuffer(buf, "ALTER ROLE %s WITH", fmtId(rolename)); if (strcmp(PQgetvalue(res, i, i_rolsuper), "t") == 0) appendPQExpBuffer(buf, " SUPERUSER"); else appendPQExpBuffer(buf, " NOSUPERUSER"); if (strcmp(PQgetvalue(res, i, i_rolinherit), "t") == 0) appendPQExpBuffer(buf, " INHERIT"); else appendPQExpBuffer(buf, " NOINHERIT"); if (strcmp(PQgetvalue(res, i, i_rolcreaterole), "t") == 0) appendPQExpBuffer(buf, " CREATEROLE"); else appendPQExpBuffer(buf, " NOCREATEROLE"); if (strcmp(PQgetvalue(res, i, i_rolcreatedb), "t") == 0) appendPQExpBuffer(buf, " CREATEDB"); else appendPQExpBuffer(buf, " NOCREATEDB"); if (strcmp(PQgetvalue(res, i, i_rolcanlogin), "t") == 0) appendPQExpBuffer(buf, " LOGIN"); else appendPQExpBuffer(buf, " NOLOGIN"); if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT %s", PQgetvalue(res, i, i_rolconnlimit)); if (!PQgetisnull(res, i, i_rolpassword)) { appendPQExpBuffer(buf, " PASSWORD "); appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolpassword), conn); } if (!PQgetisnull(res, i, i_rolvaliduntil)) appendPQExpBuffer(buf, " VALID UNTIL '%s'", PQgetvalue(res, i, i_rolvaliduntil)); if (resource_queues) { if (!PQgetisnull(res, i, i_rolqueuename)) appendPQExpBuffer(buf, " RESOURCE QUEUE %s", PQgetvalue(res, i, i_rolqueuename)); } if (exttab_auth) { /* we use the same privilege for gpfdist and gpfdists */ if (!PQgetisnull(res, i, i_rolcreaterextgpfd) && strcmp(PQgetvalue(res, i, i_rolcreaterextgpfd), "t") == 0) appendPQExpBuffer(buf, " CREATEEXTTABLE (protocol='gpfdist', type='readable')"); if (!PQgetisnull(res, i, i_rolcreatewextgpfd) && strcmp(PQgetvalue(res, i, i_rolcreatewextgpfd), "t") == 0) appendPQExpBuffer(buf, " CREATEEXTTABLE (protocol='gpfdist', type='writable')"); if (!PQgetisnull(res, i, i_rolcreaterexthttp) && strcmp(PQgetvalue(res, i, i_rolcreaterexthttp), "t") == 0) appendPQExpBuffer(buf, " CREATEEXTTABLE (protocol='http')"); if (hdfs_auth) { if (!PQgetisnull(res, i, i_rolcreaterexthdfs) && strcmp(PQgetvalue(res, i, i_rolcreaterexthdfs), "t") == 0) appendPQExpBuffer(buf, " CREATEEXTTABLE (protocol='gphdfs', type='readable')"); if (!PQgetisnull(res, i, i_rolcreatewexthdfs) && strcmp(PQgetvalue(res, i, i_rolcreatewexthdfs), "t") == 0) appendPQExpBuffer(buf, " CREATEEXTTABLE (protocol='gphdfs', type='writable')"); } } appendPQExpBuffer(buf, ";\n"); if (!PQgetisnull(res, i, i_rolcomment)) { appendPQExpBuffer(buf, "COMMENT ON ROLE %s IS ", fmtId(rolename)); appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolcomment), conn); appendPQExpBuffer(buf, ";\n"); } fprintf(OPF, "%s", buf->data); dumpUserConfig(conn, rolename); } PQclear(res); fprintf(OPF, "\n\n"); destroyPQExpBuffer(buf); }
/* * Dump filespaces. */ static void dumpFilespaces(PGconn *conn) { int i, j; PGresult *res; /* * Get all filespaces execpt built-in ones (named pg_xxx) */ if (server_version < 80214) { /* Filespaces were introduced in GP 4.0 (server_version 8.2.14) */ return; } else { res = executeQuery(conn, "SELECT fsname, oid, " "pg_catalog.pg_get_userbyid(fsowner) as fsowner, " "pg_catalog.shobj_description(oid, 'pg_filespace') " "FROM pg_catalog.pg_filespace " "WHERE fsname !~ '^pg_' " "ORDER BY 1"); } if (PQntuples(res) > 0) fprintf(OPF, "--\n-- Filespaces\n--\n\n"); for (i = 0; i < PQntuples(res); i++) { PQExpBuffer buf = createPQExpBuffer(); char *fsname = PQgetvalue(res, i, 0); char *fsoid = PQgetvalue(res, i, 1); char *fsowner = PQgetvalue(res, i, 2); char *fsdesc = PQgetvalue(res, i, 3); PQExpBuffer subbuf; PGresult *entries = NULL; /* quote name if needed */ fsname = strdup(fmtId(fsname)); /* * Drop existing filespace if required. * * Note: this statement will fail if the an existing filespace is not * empty. But this is no different from the related code in the rest * of pg_dump. */ if (output_clean) appendPQExpBuffer(buf, "DROP FILESPACE %s;\n", fsname); /* Begin creating the filespace definition */ appendPQExpBuffer(buf, "CREATE FILESPACE %s", fsname); appendPQExpBuffer(buf, " OWNER %s", fmtId(fsowner)); appendPQExpBuffer(buf, " (\n"); /* * We still need to lookup all of the paths associated with this * filespace, look them up in the pg_filespace_entry table. */ subbuf = createPQExpBuffer(); appendPQExpBuffer(subbuf, "SELECT fsedbid, fselocation " "FROM pg_catalog.pg_filespace_entry " "WHERE fsefsoid = %s " "ORDER BY 1", fsoid); entries = executeQuery(conn, subbuf->data); destroyPQExpBuffer(subbuf); /* append the filespace location information to output */ for (j = 0; j < PQntuples(entries); j++) { char *dbid = PQgetvalue(entries, j, 0); char *location = PQgetvalue(entries, j, 1); if (j > 0) appendPQExpBuffer(buf, ",\n"); appendPQExpBuffer(buf, " %s: ", dbid); appendStringLiteralConn(buf, location, conn); } PQclear(entries); /* Finnish off the statement */ appendPQExpBuffer(buf, "\n);\n"); /* Add a comment on the filespace if specified */ if (fsdesc && strlen(fsdesc)) { appendPQExpBuffer(buf, "COMMENT ON FILESPACE %s IS ", fsname); appendStringLiteralConn(buf, fsdesc, conn); appendPQExpBuffer(buf, ";\n"); } /* Output the results */ fprintf(OPF, "%s", buf->data); free(fsname); destroyPQExpBuffer(buf); } PQclear(res); fprintf(OPF, "\n\n"); }
/* * vacuum_one_database * * Process tables in the given database. If the 'tables' list is empty, * process all tables in the database. * * Note that this function is only concerned with running exactly one stage * when in analyze-in-stages mode; caller must iterate on us if necessary. * * If concurrentCons is > 1, multiple connections are used to vacuum tables * in parallel. In this case and if the table list is empty, we first obtain * a list of tables from the database. */ static void vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, int stage, SimpleStringList *tables, const char *host, const char *port, const char *username, enum trivalue prompt_password, int concurrentCons, const char *progname, bool echo, bool quiet) { PQExpBufferData sql; PQExpBufferData buf; PQExpBufferData catalog_query; PGresult *res; PGconn *conn; SimpleStringListCell *cell; ParallelSlot *slots; SimpleStringList dbtables = {NULL, NULL}; int i; int ntups; bool failed = false; bool parallel = concurrentCons > 1; bool tables_listed = false; bool has_where = false; const char *stage_commands[] = { "SET default_statistics_target=1; SET vacuum_cost_delay=0;", "SET default_statistics_target=10; RESET vacuum_cost_delay;", "RESET default_statistics_target;" }; const char *stage_messages[] = { gettext_noop("Generating minimal optimizer statistics (1 target)"), gettext_noop("Generating medium optimizer statistics (10 targets)"), gettext_noop("Generating default (full) optimizer statistics") }; Assert(stage == ANALYZE_NO_STAGE || (stage >= 0 && stage < ANALYZE_NUM_STAGES)); conn = connectDatabase(dbname, host, port, username, prompt_password, progname, echo, false, true); if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600) { PQfinish(conn); fprintf(stderr, _("%s: cannot use the \"%s\" option on server versions older than PostgreSQL 9.6\n"), progname, "disable-page-skipping"); exit(1); } if (vacopts->skip_locked && PQserverVersion(conn) < 120000) { PQfinish(conn); fprintf(stderr, _("%s: cannot use the \"%s\" option on server versions older than PostgreSQL 12\n"), progname, "skip-locked"); exit(1); } if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600) { fprintf(stderr, _("%s: cannot use the \"%s\" option on server versions older than PostgreSQL 9.6\n"), progname, "--min-xid-age"); exit(1); } if (vacopts->min_mxid_age != 0 && PQserverVersion(conn) < 90600) { fprintf(stderr, _("%s: cannot use the \"%s\" option on server versions older than PostgreSQL 9.6\n"), progname, "--min-mxid-age"); exit(1); } if (!quiet) { if (stage != ANALYZE_NO_STAGE) printf(_("%s: processing database \"%s\": %s\n"), progname, PQdb(conn), _(stage_messages[stage])); else printf(_("%s: vacuuming database \"%s\"\n"), progname, PQdb(conn)); fflush(stdout); } /* * Prepare the list of tables to process by querying the catalogs. * * Since we execute the constructed query with the default search_path * (which could be unsafe), everything in this query MUST be fully * qualified. * * First, build a WITH clause for the catalog query if any tables were * specified, with a set of values made of relation names and their * optional set of columns. This is used to match any provided column * lists with the generated qualified identifiers and to filter for the * tables provided via --table. If a listed table does not exist, the * catalog query will fail. */ initPQExpBuffer(&catalog_query); for (cell = tables ? tables->head : NULL; cell; cell = cell->next) { char *just_table; const char *just_columns; /* * Split relation and column names given by the user, this is used to * feed the CTE with values on which are performed pre-run validity * checks as well. For now these happen only on the relation name. */ splitTableColumnsSpec(cell->val, PQclientEncoding(conn), &just_table, &just_columns); if (!tables_listed) { appendPQExpBuffer(&catalog_query, "WITH listed_tables (table_oid, column_list) " "AS (\n VALUES ("); tables_listed = true; } else appendPQExpBuffer(&catalog_query, ",\n ("); appendStringLiteralConn(&catalog_query, just_table, conn); appendPQExpBuffer(&catalog_query, "::pg_catalog.regclass, "); if (just_columns && just_columns[0] != '\0') appendStringLiteralConn(&catalog_query, just_columns, conn); else appendPQExpBufferStr(&catalog_query, "NULL"); appendPQExpBufferStr(&catalog_query, "::pg_catalog.text)"); pg_free(just_table); } /* Finish formatting the CTE */ if (tables_listed) appendPQExpBuffer(&catalog_query, "\n)\n"); appendPQExpBuffer(&catalog_query, "SELECT c.relname, ns.nspname"); if (tables_listed) appendPQExpBuffer(&catalog_query, ", listed_tables.column_list"); appendPQExpBuffer(&catalog_query, " FROM pg_catalog.pg_class c\n" " JOIN pg_catalog.pg_namespace ns" " ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n" " LEFT JOIN pg_catalog.pg_class t" " ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid\n"); /* Used to match the tables listed by the user */ if (tables_listed) appendPQExpBuffer(&catalog_query, " JOIN listed_tables" " ON listed_tables.table_oid OPERATOR(pg_catalog.=) c.oid\n"); /* * If no tables were listed, filter for the relevant relation types. If * tables were given via --table, don't bother filtering by relation type. * Instead, let the server decide whether a given relation can be * processed in which case the user will know about it. */ if (!tables_listed) { appendPQExpBuffer(&catalog_query, " WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array[" CppAsString2(RELKIND_RELATION) ", " CppAsString2(RELKIND_MATVIEW) "])\n"); has_where = true; } /* * For --min-xid-age and --min-mxid-age, the age of the relation is the * greatest of the ages of the main relation and its associated TOAST * table. The commands generated by vacuumdb will also process the TOAST * table for the relation if necessary, so it does not need to be * considered separately. */ if (vacopts->min_xid_age != 0) { appendPQExpBuffer(&catalog_query, " %s GREATEST(pg_catalog.age(c.relfrozenxid)," " pg_catalog.age(t.relfrozenxid)) " " OPERATOR(pg_catalog.>=) '%d'::pg_catalog.int4\n" " AND c.relfrozenxid OPERATOR(pg_catalog.!=)" " '0'::pg_catalog.xid\n", has_where ? "AND" : "WHERE", vacopts->min_xid_age); has_where = true; } if (vacopts->min_mxid_age != 0) { appendPQExpBuffer(&catalog_query, " %s GREATEST(pg_catalog.mxid_age(c.relminmxid)," " pg_catalog.mxid_age(t.relminmxid)) OPERATOR(pg_catalog.>=)" " '%d'::pg_catalog.int4\n" " AND c.relminmxid OPERATOR(pg_catalog.!=)" " '0'::pg_catalog.xid\n", has_where ? "AND" : "WHERE", vacopts->min_mxid_age); has_where = true; } /* * Execute the catalog query. We use the default search_path for this * query for consistency with table lookups done elsewhere by the user. */ appendPQExpBuffer(&catalog_query, " ORDER BY c.relpages DESC;"); executeCommand(conn, "RESET search_path;", progname, echo); res = executeQuery(conn, catalog_query.data, progname, echo); termPQExpBuffer(&catalog_query); PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, progname, echo)); /* * If no rows are returned, there are no matching tables, so we are done. */ ntups = PQntuples(res); if (ntups == 0) { PQclear(res); PQfinish(conn); return; } /* * Build qualified identifiers for each table, including the column list * if given. */ initPQExpBuffer(&buf); for (i = 0; i < ntups; i++) { appendPQExpBufferStr(&buf, fmtQualifiedId(PQgetvalue(res, i, 1), PQgetvalue(res, i, 0))); if (tables_listed && !PQgetisnull(res, i, 2)) appendPQExpBufferStr(&buf, PQgetvalue(res, i, 2)); simple_string_list_append(&dbtables, buf.data); resetPQExpBuffer(&buf); } termPQExpBuffer(&buf); PQclear(res); /* * If there are more connections than vacuumable relations, we don't need * to use them all. */ if (parallel) { if (concurrentCons > ntups) concurrentCons = ntups; if (concurrentCons <= 1) parallel = false; } /* * Setup the database connections. We reuse the connection we already have * for the first slot. If not in parallel mode, the first slot in the * array contains the connection. */ if (concurrentCons <= 0) concurrentCons = 1; slots = (ParallelSlot *) pg_malloc(sizeof(ParallelSlot) * concurrentCons); init_slot(slots, conn); if (parallel) { for (i = 1; i < concurrentCons; i++) { conn = connectDatabase(dbname, host, port, username, prompt_password, progname, echo, false, true); init_slot(slots + i, conn); } } /* * Prepare all the connections to run the appropriate analyze stage, if * caller requested that mode. */ if (stage != ANALYZE_NO_STAGE) { int j; /* We already emitted the message above */ for (j = 0; j < concurrentCons; j++) executeCommand((slots + j)->connection, stage_commands[stage], progname, echo); } initPQExpBuffer(&sql); cell = dbtables.head; do { const char *tabname = cell->val; ParallelSlot *free_slot; if (CancelRequested) { failed = true; goto finish; } /* * Get the connection slot to use. If in parallel mode, here we wait * for one connection to become available if none already is. In * non-parallel mode we simply use the only slot we have, which we * know to be free. */ if (parallel) { /* * Get a free slot, waiting until one becomes free if none * currently is. */ free_slot = GetIdleSlot(slots, concurrentCons, progname); if (!free_slot) { failed = true; goto finish; } free_slot->isFree = false; } else free_slot = slots; prepare_vacuum_command(&sql, PQserverVersion(free_slot->connection), vacopts, tabname); /* * Execute the vacuum. If not in parallel mode, this terminates the * program in case of an error. (The parallel case handles query * errors in ProcessQueryResult through GetIdleSlot.) */ run_vacuum_command(free_slot->connection, sql.data, echo, tabname, progname, parallel); cell = cell->next; } while (cell != NULL); if (parallel) { int j; /* wait for all connections to finish */ for (j = 0; j < concurrentCons; j++) { if (!GetQueryResult((slots + j)->connection, progname)) goto finish; } } finish: for (i = 0; i < concurrentCons; i++) DisconnectDatabase(slots + i); pfree(slots); termPQExpBuffer(&sql); if (failed) exit(1); }
int main(int argc, char *argv[]) { static struct option long_options[] = { {"host", required_argument, NULL, 'h'}, {"port", required_argument, NULL, 'p'}, {"username", required_argument, NULL, 'U'}, {"role", required_argument, NULL, 'g'}, {"no-password", no_argument, NULL, 'w'}, {"password", no_argument, NULL, 'W'}, {"echo", no_argument, NULL, 'e'}, {"createdb", no_argument, NULL, 'd'}, {"no-createdb", no_argument, NULL, 'D'}, {"superuser", no_argument, NULL, 's'}, {"no-superuser", no_argument, NULL, 'S'}, {"createrole", no_argument, NULL, 'r'}, {"no-createrole", no_argument, NULL, 'R'}, {"inherit", no_argument, NULL, 'i'}, {"no-inherit", no_argument, NULL, 'I'}, {"login", no_argument, NULL, 'l'}, {"no-login", no_argument, NULL, 'L'}, {"replication", no_argument, NULL, 1}, {"no-replication", no_argument, NULL, 2}, {"interactive", no_argument, NULL, 3}, /* adduser is obsolete, undocumented spelling of superuser */ {"adduser", no_argument, NULL, 'a'}, {"no-adduser", no_argument, NULL, 'A'}, {"connection-limit", required_argument, NULL, 'c'}, {"pwprompt", no_argument, NULL, 'P'}, {"encrypted", no_argument, NULL, 'E'}, {NULL, 0, NULL, 0} }; const char *progname; int optindex; int c; const char *newuser = NULL; char *host = NULL; char *port = NULL; char *username = NULL; SimpleStringList roles = {NULL, NULL}; enum trivalue prompt_password = TRI_DEFAULT; bool echo = false; bool interactive = false; char *conn_limit = NULL; bool pwprompt = false; char *newpassword = NULL; char newuser_buf[128]; char newpassword_buf[100]; /* Tri-valued variables. */ enum trivalue createdb = TRI_DEFAULT, superuser = TRI_DEFAULT, createrole = TRI_DEFAULT, inherit = TRI_DEFAULT, login = TRI_DEFAULT, replication = TRI_DEFAULT; PQExpBufferData sql; PGconn *conn; PGresult *result; progname = get_progname(argv[0]); set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts")); handle_help_version_opts(argc, argv, "createuser", help); while ((c = getopt_long(argc, argv, "h:p:U:g:wWedDsSaArRiIlLc:PE", long_options, &optindex)) != -1) { switch (c) { case 'h': host = pg_strdup(optarg); break; case 'p': port = pg_strdup(optarg); break; case 'U': username = pg_strdup(optarg); break; case 'g': simple_string_list_append(&roles, optarg); break; case 'w': prompt_password = TRI_NO; break; case 'W': prompt_password = TRI_YES; break; case 'e': echo = true; break; case 'd': createdb = TRI_YES; break; case 'D': createdb = TRI_NO; break; case 's': case 'a': superuser = TRI_YES; break; case 'S': case 'A': superuser = TRI_NO; break; case 'r': createrole = TRI_YES; break; case 'R': createrole = TRI_NO; break; case 'i': inherit = TRI_YES; break; case 'I': inherit = TRI_NO; break; case 'l': login = TRI_YES; break; case 'L': login = TRI_NO; break; case 'c': conn_limit = pg_strdup(optarg); break; case 'P': pwprompt = true; break; case 'E': /* no-op, accepted for backward compatibility */ break; case 1: replication = TRI_YES; break; case 2: replication = TRI_NO; break; case 3: interactive = true; break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } } switch (argc - optind) { case 0: break; case 1: newuser = argv[optind]; break; default: fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind + 1]); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } if (newuser == NULL) { if (interactive) { simple_prompt("Enter name of role to add: ", newuser_buf, sizeof(newuser_buf), true); newuser = newuser_buf; } else { if (getenv("PGUSER")) newuser = getenv("PGUSER"); else newuser = get_user_name_or_exit(progname); } } if (pwprompt) { char pw2[100]; simple_prompt("Enter password for new role: ", newpassword_buf, sizeof(newpassword_buf), false); simple_prompt("Enter it again: ", pw2, sizeof(pw2), false); if (strcmp(newpassword_buf, pw2) != 0) { fprintf(stderr, _("Passwords didn't match.\n")); exit(1); } newpassword = newpassword_buf; } if (superuser == 0) { if (interactive && yesno_prompt("Shall the new role be a superuser?")) superuser = TRI_YES; else superuser = TRI_NO; } if (superuser == TRI_YES) { /* Not much point in trying to restrict a superuser */ createdb = TRI_YES; createrole = TRI_YES; } if (createdb == 0) { if (interactive && yesno_prompt("Shall the new role be allowed to create databases?")) createdb = TRI_YES; else createdb = TRI_NO; } if (createrole == 0) { if (interactive && yesno_prompt("Shall the new role be allowed to create more new roles?")) createrole = TRI_YES; else createrole = TRI_NO; } if (inherit == 0) inherit = TRI_YES; if (login == 0) login = TRI_YES; conn = connectDatabase("postgres", host, port, username, prompt_password, progname, echo, false, false); initPQExpBuffer(&sql); printfPQExpBuffer(&sql, "CREATE ROLE %s", fmtId(newuser)); if (newpassword) { char *encrypted_password; appendPQExpBufferStr(&sql, " PASSWORD "); encrypted_password = PQencryptPasswordConn(conn, newpassword, newuser, NULL); if (!encrypted_password) { fprintf(stderr, _("%s: password encryption failed: %s"), progname, PQerrorMessage(conn)); exit(1); } appendStringLiteralConn(&sql, encrypted_password, conn); PQfreemem(encrypted_password); } if (superuser == TRI_YES) appendPQExpBufferStr(&sql, " SUPERUSER"); if (superuser == TRI_NO) appendPQExpBufferStr(&sql, " NOSUPERUSER"); if (createdb == TRI_YES) appendPQExpBufferStr(&sql, " CREATEDB"); if (createdb == TRI_NO) appendPQExpBufferStr(&sql, " NOCREATEDB"); if (createrole == TRI_YES) appendPQExpBufferStr(&sql, " CREATEROLE"); if (createrole == TRI_NO) appendPQExpBufferStr(&sql, " NOCREATEROLE"); if (inherit == TRI_YES) appendPQExpBufferStr(&sql, " INHERIT"); if (inherit == TRI_NO) appendPQExpBufferStr(&sql, " NOINHERIT"); if (login == TRI_YES) appendPQExpBufferStr(&sql, " LOGIN"); if (login == TRI_NO) appendPQExpBufferStr(&sql, " NOLOGIN"); if (replication == TRI_YES) appendPQExpBufferStr(&sql, " REPLICATION"); if (replication == TRI_NO) appendPQExpBufferStr(&sql, " NOREPLICATION"); if (conn_limit != NULL) appendPQExpBuffer(&sql, " CONNECTION LIMIT %s", conn_limit); if (roles.head != NULL) { SimpleStringListCell *cell; appendPQExpBufferStr(&sql, " IN ROLE "); for (cell = roles.head; cell; cell = cell->next) { if (cell->next) appendPQExpBuffer(&sql, "%s,", fmtId(cell->val)); else appendPQExpBufferStr(&sql, fmtId(cell->val)); } } appendPQExpBufferChar(&sql, ';'); if (echo) printf("%s\n", sql.data); result = PQexec(conn, sql.data); if (PQresultStatus(result) != PGRES_COMMAND_OK) { fprintf(stderr, _("%s: creation of new role failed: %s"), progname, PQerrorMessage(conn)); PQfinish(conn); exit(1); } PQclear(result); PQfinish(conn); exit(0); }
/* * Dump tablespaces. */ static void dumpTablespaces(PGconn *conn) { PGresult *res; int i; /* * Get all tablespaces except built-in ones (which we assume are named * pg_xxx) */ if (server_version >= 90000) res = executeQuery(conn, "SELECT spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, " "array_to_string(spcoptions, ', ')," "pg_catalog.shobj_description(oid, 'pg_tablespace') " "FROM pg_catalog.pg_tablespace " "WHERE spcname !~ '^pg_' " "ORDER BY 1"); else if (server_version >= 80200) res = executeQuery(conn, "SELECT spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, null, " "pg_catalog.shobj_description(oid, 'pg_tablespace') " "FROM pg_catalog.pg_tablespace " "WHERE spcname !~ '^pg_' " "ORDER BY 1"); else res = executeQuery(conn, "SELECT spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, " "null, null " "FROM pg_catalog.pg_tablespace " "WHERE spcname !~ '^pg_' " "ORDER BY 1"); if (PQntuples(res) > 0) fprintf(OPF, "--\n-- Tablespaces\n--\n\n"); for (i = 0; i < PQntuples(res); i++) { PQExpBuffer buf = createPQExpBuffer(); char *spcname = PQgetvalue(res, i, 0); char *spcowner = PQgetvalue(res, i, 1); char *spclocation = PQgetvalue(res, i, 2); char *spcacl = PQgetvalue(res, i, 3); char *spcoptions = PQgetvalue(res, i, 4); char *spccomment = PQgetvalue(res, i, 5); char *fspcname; /* needed for buildACLCommands() */ fspcname = strdup(fmtId(spcname)); appendPQExpBuffer(buf, "CREATE TABLESPACE %s", fspcname); appendPQExpBuffer(buf, " OWNER %s", fmtId(spcowner)); appendPQExpBuffer(buf, " LOCATION "); appendStringLiteralConn(buf, spclocation, conn); appendPQExpBuffer(buf, ";\n"); if (spcoptions && spcoptions[0] != '\0') appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n", fspcname, spcoptions); if (!skip_acls && !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner, "", server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"), progname, spcacl, fspcname); PQfinish(conn); exit(1); } if (spccomment && strlen(spccomment)) { appendPQExpBuffer(buf, "COMMENT ON TABLESPACE %s IS ", fspcname); appendStringLiteralConn(buf, spccomment, conn); appendPQExpBuffer(buf, ";\n"); } fprintf(OPF, "%s", buf->data); free(fspcname); destroyPQExpBuffer(buf); } PQclear(res); fprintf(OPF, "\n\n"); }
/* * Dump roles */ static void dumpRoles(PGconn *conn) { PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i_rolname, i_rolsuper, i_rolinherit, i_rolcreaterole, i_rolcreatedb, i_rolcatupdate, i_rolcanlogin, i_rolconnlimit, i_rolpassword, i_rolvaliduntil, i_rolcomment; int i; /* note: rolconfig is dumped later */ if (server_version >= 80200) printfPQExpBuffer(buf, "SELECT rolname, rolsuper, rolinherit, " "rolcreaterole, rolcreatedb, rolcatupdate, " "rolcanlogin, rolconnlimit, rolpassword, " "rolvaliduntil, " "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment " "FROM pg_authid " "ORDER BY 1"); else if (server_version >= 80100) printfPQExpBuffer(buf, "SELECT rolname, rolsuper, rolinherit, " "rolcreaterole, rolcreatedb, rolcatupdate, " "rolcanlogin, rolconnlimit, rolpassword, " "rolvaliduntil, null as rolcomment " "FROM pg_authid " "ORDER BY 1"); else printfPQExpBuffer(buf, "SELECT usename as rolname, " "usesuper as rolsuper, " "true as rolinherit, " "usesuper as rolcreaterole, " "usecreatedb as rolcreatedb, " "usecatupd as rolcatupdate, " "true as rolcanlogin, " "-1 as rolconnlimit, " "passwd as rolpassword, " "valuntil as rolvaliduntil, " "null as rolcomment " "FROM pg_shadow " "UNION ALL " "SELECT groname as rolname, " "false as rolsuper, " "true as rolinherit, " "false as rolcreaterole, " "false as rolcreatedb, " "false as rolcatupdate, " "false as rolcanlogin, " "-1 as rolconnlimit, " "null::text as rolpassword, " "null::abstime as rolvaliduntil, " "null as rolcomment " "FROM pg_group " "WHERE NOT EXISTS (SELECT 1 FROM pg_shadow " " WHERE usename = groname) " "ORDER BY 1"); res = executeQuery(conn, buf->data); i_rolname = PQfnumber(res, "rolname"); i_rolsuper = PQfnumber(res, "rolsuper"); i_rolinherit = PQfnumber(res, "rolinherit"); i_rolcreaterole = PQfnumber(res, "rolcreaterole"); i_rolcreatedb = PQfnumber(res, "rolcreatedb"); i_rolcatupdate = PQfnumber(res, "rolcatupdate"); i_rolcanlogin = PQfnumber(res, "rolcanlogin"); i_rolconnlimit = PQfnumber(res, "rolconnlimit"); i_rolpassword = PQfnumber(res, "rolpassword"); i_rolvaliduntil = PQfnumber(res, "rolvaliduntil"); i_rolcomment = PQfnumber(res, "rolcomment"); if (PQntuples(res) > 0) fprintf(OPF, "--\n-- Roles\n--\n\n"); for (i = 0; i < PQntuples(res); i++) { const char *rolename; rolename = PQgetvalue(res, i, i_rolname); resetPQExpBuffer(buf); /* * We dump CREATE ROLE followed by ALTER ROLE to ensure that the role * will acquire the right properties even if it already exists (ie, it * won't hurt for the CREATE to fail). This is particularly important * for the role we are connected as, since even with --clean we will * have failed to drop it. */ appendPQExpBuffer(buf, "CREATE ROLE %s;\n", fmtId(rolename)); appendPQExpBuffer(buf, "ALTER ROLE %s WITH", fmtId(rolename)); if (strcmp(PQgetvalue(res, i, i_rolsuper), "t") == 0) appendPQExpBuffer(buf, " SUPERUSER"); else appendPQExpBuffer(buf, " NOSUPERUSER"); if (strcmp(PQgetvalue(res, i, i_rolinherit), "t") == 0) appendPQExpBuffer(buf, " INHERIT"); else appendPQExpBuffer(buf, " NOINHERIT"); if (strcmp(PQgetvalue(res, i, i_rolcreaterole), "t") == 0) appendPQExpBuffer(buf, " CREATEROLE"); else appendPQExpBuffer(buf, " NOCREATEROLE"); if (strcmp(PQgetvalue(res, i, i_rolcreatedb), "t") == 0) appendPQExpBuffer(buf, " CREATEDB"); else appendPQExpBuffer(buf, " NOCREATEDB"); if (strcmp(PQgetvalue(res, i, i_rolcanlogin), "t") == 0) appendPQExpBuffer(buf, " LOGIN"); else appendPQExpBuffer(buf, " NOLOGIN"); if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT %s", PQgetvalue(res, i, i_rolconnlimit)); if (!PQgetisnull(res, i, i_rolpassword)) { appendPQExpBuffer(buf, " PASSWORD "); appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolpassword), conn); } if (!PQgetisnull(res, i, i_rolvaliduntil)) appendPQExpBuffer(buf, " VALID UNTIL '%s'", PQgetvalue(res, i, i_rolvaliduntil)); appendPQExpBuffer(buf, ";\n"); if (!PQgetisnull(res, i, i_rolcomment)) { appendPQExpBuffer(buf, "COMMENT ON ROLE %s IS ", fmtId(rolename)); appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolcomment), conn); appendPQExpBuffer(buf, ";\n"); } fprintf(OPF, "%s", buf->data); if (server_version >= 70300) dumpUserConfig(conn, rolename); } PQclear(res); fprintf(OPF, "\n\n"); destroyPQExpBuffer(buf); }
/* * Subroutine to actually try to execute a backslash command. */ static backslashResult exec_command(const char *cmd, PsqlScanState scan_state, PQExpBuffer query_buf) { bool success = true; /* indicate here if the command ran ok or * failed */ backslashResult status = PSQL_CMD_SKIP_LINE; /* * \a -- toggle field alignment This makes little sense but we keep it * around. */ if (strcmp(cmd, "a") == 0) { if (pset.popt.topt.format != PRINT_ALIGNED) success = do_pset("format", "aligned", &pset.popt, pset.quiet); else success = do_pset("format", "unaligned", &pset.popt, pset.quiet); } /* \C -- override table title (formerly change HTML caption) */ else if (strcmp(cmd, "C") == 0) { char *opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); success = do_pset("title", opt, &pset.popt, pset.quiet); free(opt); } /* * \c or \connect -- connect to database using the specified parameters. * * \c dbname user host port * * If any of these parameters are omitted or specified as '-', the current * value of the parameter will be used instead. If the parameter has no * current value, the default value for that parameter will be used. Some * examples: * * \c - - hst Connect to current database on current port of host * "hst" as current user. \c - usr - prt Connect to current database on * "prt" port of current host as user "usr". \c dbs Connect to * "dbs" database on current port of current host as current user. */ else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0) { char *opt1, *opt2, *opt3, *opt4; opt1 = read_connect_arg(scan_state); opt2 = read_connect_arg(scan_state); opt3 = read_connect_arg(scan_state); opt4 = read_connect_arg(scan_state); success = do_connect(opt1, opt2, opt3, opt4); free(opt1); free(opt2); free(opt3); free(opt4); } /* \cd */ else if (strcmp(cmd, "cd") == 0) { char *opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); char *dir; if (opt) dir = opt; else { #ifndef WIN32 struct passwd *pw; pw = getpwuid(geteuid()); if (!pw) { psql_error("could not get home directory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } dir = pw->pw_dir; #else /* WIN32 */ /* * On Windows, 'cd' without arguments prints the current * directory, so if someone wants to code this here instead... */ dir = "/"; #endif /* WIN32 */ } if (chdir(dir) == -1) { psql_error("\\%s: could not change directory to \"%s\": %s\n", cmd, dir, strerror(errno)); success = false; } if (pset.dirname) free(pset.dirname); pset.dirname = pg_strdup(dir); canonicalize_path(pset.dirname); if (opt) free(opt); } /* \conninfo -- display information about the current connection */ else if (strcmp(cmd, "conninfo") == 0) { char *db = PQdb(pset.db); char *host = PQhost(pset.db); if (db == NULL) printf(_("You are not connected.\n")); else { if (host == NULL) host = DEFAULT_PGSOCKET_DIR; /* If the host is an absolute path, the connection is via socket */ if (is_absolute_path(host)) printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"), db, PQuser(pset.db), host, PQport(pset.db)); else printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"), db, PQuser(pset.db), host, PQport(pset.db)); } } /* \copy */ else if (pg_strcasecmp(cmd, "copy") == 0) { /* Default fetch-it-all-and-print mode */ instr_time before, after; char *opt = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, false); if (pset.timing) INSTR_TIME_SET_CURRENT(before); success = do_copy(opt); if (pset.timing && success) { INSTR_TIME_SET_CURRENT(after); INSTR_TIME_SUBTRACT(after, before); printf(_("Time: %.3f ms\n"), INSTR_TIME_GET_MILLISEC(after)); } free(opt); } /* \copyright */ else if (strcmp(cmd, "copyright") == 0) print_copyright(); /* \d* commands */ else if (cmd[0] == 'd') { char *pattern; bool show_verbose, show_system; /* We don't do SQLID reduction on the pattern yet */ pattern = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); show_verbose = strchr(cmd, '+') ? true : false; show_system = strchr(cmd, 'S') ? true : false; switch (cmd[1]) { case '\0': case '+': case 'S': /* GPDB: This is a change from old behavior: We used to show just system tables */ if (pattern) success = describeTableDetails(pattern, show_verbose, show_system); else /* standard listing of interesting things */ success = listTables("tvsxr", NULL, show_verbose, show_system); break; case 'a': success = describeAggregates(pattern, show_verbose, show_system); break; case 'b': success = describeTablespaces(pattern, show_verbose); break; case 'c': success = listConversions(pattern, show_system); break; case 'C': success = listCasts(pattern); break; case 'd': if (strncmp(cmd, "ddp", 3) == 0) success = listDefaultACLs(pattern); else success = objectDescription(pattern, show_system); break; case 'D': success = listDomains(pattern, show_system); break; case 'f': /* function subsystem */ switch (cmd[2]) { case '\0': case '+': case 'S': case 'a': case 'n': case 't': case 'w': success = describeFunctions(&cmd[2], pattern, show_verbose, show_system); break; default: status = PSQL_CMD_UNKNOWN; break; } break; case 'g': /* no longer distinct from \du */ success = describeRoles(pattern, show_verbose); break; case 'l': success = do_lo_list(); break; case 'n': success = listSchemas(pattern, show_verbose); break; case 'o': success = describeOperators(pattern, show_system); break; case 'p': success = permissionsList(pattern); break; case 'T': success = describeTypes(pattern, show_verbose, show_system); break; case 't': case 'v': case 'i': case 's': case 'E': /* PostgreSQL use dx for extension, change to dE for foreign table */ /* case 'S': // GPDB: We used to show just system tables for this */ case 'P': /* GPDB: Parent-only tables, no children */ success = listTables(&cmd[1], pattern, show_verbose, show_system); break; case 'r': if (cmd[2] == 'd' && cmd[3] == 's') { char *pattern2 = NULL; if (pattern) pattern2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); success = listDbRoleSettings(pattern, pattern2); } else //success = PSQL_CMD_UNKNOWN; /* GPDB uses \dr for foreign tables ? */ success = listTables(&cmd[1], pattern, show_verbose, show_system); break; case 'u': success = describeRoles(pattern, show_verbose); break; case 'F': /* text search subsystem */ switch (cmd[2]) { case '\0': case '+': success = listTSConfigs(pattern, show_verbose); break; case 'p': success = listTSParsers(pattern, show_verbose); break; case 'd': success = listTSDictionaries(pattern, show_verbose); break; case 't': success = listTSTemplates(pattern, show_verbose); break; default: status = PSQL_CMD_UNKNOWN; break; } break; case 'x': /* Extensions */ if (show_verbose) success = listExtensionContents(pattern); else success = listExtensions(pattern); break; default: status = PSQL_CMD_UNKNOWN; } if (pattern) free(pattern); } /* * \e or \edit -- edit the current query buffer, or edit a file and make * it the query buffer */ else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0) { if (!query_buf) { psql_error("no query buffer\n"); status = PSQL_CMD_ERROR; } else { char *fname; fname = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); expand_tilde(&fname); if (fname) canonicalize_path(fname); if (do_edit(fname, query_buf, NULL)) status = PSQL_CMD_NEWEDIT; else status = PSQL_CMD_ERROR; free(fname); } } /* * \ef -- edit the named function, or present a blank CREATE FUNCTION * template if no argument is given */ else if (strcmp(cmd, "ef") == 0) { if (!query_buf) { psql_error("no query buffer\n"); status = PSQL_CMD_ERROR; } else { char *func; Oid foid = InvalidOid; func = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, true); if (!func) { /* set up an empty command to fill in */ printfPQExpBuffer(query_buf, "CREATE FUNCTION ( )\n" " RETURNS \n" " LANGUAGE \n" " -- common options: IMMUTABLE STABLE STRICT SECURITY DEFINER\n" "AS $function$\n" "\n$function$\n"); } else if (!lookup_function_oid(pset.db, func, &foid)) { /* error already reported */ status = PSQL_CMD_ERROR; } else if (!get_create_function_cmd(pset.db, foid, query_buf)) { /* error already reported */ status = PSQL_CMD_ERROR; } if (func) free(func); } if (status != PSQL_CMD_ERROR) { bool edited = false; if (!do_edit(0, query_buf, &edited)) status = PSQL_CMD_ERROR; else if (!edited) puts(_("No changes")); else status = PSQL_CMD_NEWEDIT; } } /* \echo and \qecho */ else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0) { char *value; char quoted; bool no_newline = false; bool first = true; FILE *fout; if (strcmp(cmd, "qecho") == 0) fout = pset.queryFout; else fout = stdout; while ((value = psql_scan_slash_option(scan_state, OT_NORMAL, "ed, false))) { if (!quoted && strcmp(value, "-n") == 0) no_newline = true; else { if (first) first = false; else fputc(' ', fout); fputs(value, fout); } free(value); } if (!no_newline) fputs("\n", fout); } /* \encoding -- set/show client side encoding */ else if (strcmp(cmd, "encoding") == 0) { char *encoding = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); if (!encoding) { /* show encoding */ puts(pg_encoding_to_char(pset.encoding)); } else { /* set encoding */ if (PQsetClientEncoding(pset.db, encoding) == -1) psql_error("%s: invalid encoding name or conversion procedure not found\n", encoding); else { /* save encoding info into psql internal data */ pset.encoding = PQclientEncoding(pset.db); pset.popt.topt.encoding = pset.encoding; SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); } free(encoding); } } /* \f -- change field separator */ else if (strcmp(cmd, "f") == 0) { char *fname = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); success = do_pset("fieldsep", fname, &pset.popt, pset.quiet); free(fname); } /* \g means send query */ else if (strcmp(cmd, "g") == 0) { char *fname = psql_scan_slash_option(scan_state, OT_FILEPIPE, NULL, false); if (!fname) pset.gfname = NULL; else { expand_tilde(&fname); pset.gfname = pg_strdup(fname); } free(fname); status = PSQL_CMD_SEND; } /* help */ else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0) { char *opt = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, false); size_t len; /* strip any trailing spaces and semicolons */ if (opt) { len = strlen(opt); while (len > 0 && (isspace((unsigned char) opt[len - 1]) || opt[len - 1] == ';')) opt[--len] = '\0'; } helpSQL(opt, pset.popt.topt.pager); free(opt); } /* HTML mode */ else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0) { if (pset.popt.topt.format != PRINT_HTML) success = do_pset("format", "html", &pset.popt, pset.quiet); else success = do_pset("format", "aligned", &pset.popt, pset.quiet); } /* \i is include file */ else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0) { char *fname = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); if (!fname) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else { expand_tilde(&fname); success = (process_file(fname, false) == EXIT_SUCCESS); free(fname); } } /* \l is list databases */ else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0) success = listAllDbs(false); else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0) success = listAllDbs(true); /* * large object things */ else if (strncmp(cmd, "lo_", 3) == 0) { char *opt1, *opt2; opt1 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); opt2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); if (strcmp(cmd + 3, "export") == 0) { if (!opt2) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else { expand_tilde(&opt2); success = do_lo_export(opt1, opt2); } } else if (strcmp(cmd + 3, "import") == 0) { if (!opt1) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else { expand_tilde(&opt1); success = do_lo_import(opt1, opt2); } } else if (strcmp(cmd + 3, "list") == 0) success = do_lo_list(); else if (strcmp(cmd + 3, "unlink") == 0) { if (!opt1) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else success = do_lo_unlink(opt1); } else status = PSQL_CMD_UNKNOWN; free(opt1); free(opt2); } /* \o -- set query output */ else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0) { char *fname = psql_scan_slash_option(scan_state, OT_FILEPIPE, NULL, true); expand_tilde(&fname); success = setQFout(fname); free(fname); } /* \p prints the current query buffer */ else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0) { if (query_buf && query_buf->len > 0) puts(query_buf->data); else if (!pset.quiet) puts(_("Query buffer is empty.")); fflush(stdout); } /* \password -- set user password */ else if (strcmp(cmd, "password") == 0) { char *pw1; char *pw2; pw1 = simple_prompt("Enter new password: "******"Enter it again: ", 100, false); if (strcmp(pw1, pw2) != 0) { fprintf(stderr, _("Passwords didn't match.\n")); success = false; } else { char *opt0 = psql_scan_slash_option(scan_state, OT_SQLID, NULL, true); char *user; char *encrypted_password; if (opt0) user = opt0; else user = PQuser(pset.db); encrypted_password = PQencryptPassword(pw1, user); if (!encrypted_password) { fprintf(stderr, _("Password encryption failed.\n")); success = false; } else { PQExpBufferData buf; PGresult *res; initPQExpBuffer(&buf); printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ", fmtId(user)); appendStringLiteralConn(&buf, encrypted_password, pset.db); res = PSQLexec(buf.data, false); termPQExpBuffer(&buf); if (!res) success = false; else PQclear(res); PQfreemem(encrypted_password); } } free(pw1); free(pw2); } /* \prompt -- prompt and set variable */ else if (strcmp(cmd, "prompt") == 0) { char *opt, *prompt_text = NULL; char *arg1, *arg2; arg1 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); arg2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); if (!arg1) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else { char *result; if (arg2) { prompt_text = arg1; opt = arg2; } else opt = arg1; if (!pset.inputfile) result = simple_prompt(prompt_text, 4096, true); else { if (prompt_text) { fputs(prompt_text, stdout); fflush(stdout); } result = gets_fromFile(stdin); } if (!SetVariable(pset.vars, opt, result)) { psql_error("\\%s: error\n", cmd); success = false; } free(result); if (prompt_text) free(prompt_text); free(opt); } } /* \pset -- set printing parameters */ else if (strcmp(cmd, "pset") == 0) { char *opt0 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); char *opt1 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); if (!opt0) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else success = do_pset(opt0, opt1, &pset.popt, pset.quiet); free(opt0); free(opt1); } /* \q or \quit */ else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0) status = PSQL_CMD_TERMINATE; /* reset(clear) the buffer */ else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0) { resetPQExpBuffer(query_buf); psql_scan_reset(scan_state); if (!pset.quiet) puts(_("Query buffer reset (cleared).")); } /* \s save history in a file or show it on the screen */ else if (strcmp(cmd, "s") == 0) { char *fname = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); #if defined(WIN32) && !defined(__CYGWIN__) /* * XXX This does not work for all terminal environments or for output * containing non-ASCII characters; see comments in simple_prompt(). */ #define DEVTTY "con" #else #define DEVTTY "/dev/tty" #endif expand_tilde(&fname); /* This scrolls off the screen when using /dev/tty */ success = saveHistory(fname ? fname : DEVTTY, -1, false, false); if (success && !pset.quiet && fname) printf(gettext("Wrote history to file \"%s/%s\".\n"), pset.dirname ? pset.dirname : ".", fname); if (!fname) putchar('\n'); free(fname); } /* \set -- generalized set variable/option command */ else if (strcmp(cmd, "set") == 0) { char *opt0 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); if (!opt0) { /* list all variables */ PrintVariables(pset.vars); success = true; } else { /* * Set variable to the concatenation of the arguments. */ char *newval; char *opt; opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); newval = pg_strdup(opt ? opt : ""); free(opt); while ((opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false))) { newval = realloc(newval, strlen(newval) + strlen(opt) + 1); if (!newval) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } strcat(newval, opt); free(opt); } if (!SetVariable(pset.vars, opt0, newval)) { psql_error("\\%s: error\n", cmd); success = false; } free(newval); } free(opt0); } /* \t -- turn off headers and row count */ else if (strcmp(cmd, "t") == 0) { char *opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); success = do_pset("tuples_only", opt, &pset.popt, pset.quiet); free(opt); } /* \T -- define html <table ...> attributes */ else if (strcmp(cmd, "T") == 0) { char *value = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); success = do_pset("tableattr", value, &pset.popt, pset.quiet); free(value); } /* \timing -- toggle timing of queries */ else if (strcmp(cmd, "timing") == 0) { char *opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); if (opt) pset.timing = ParseVariableBool(opt); else pset.timing = !pset.timing; if (!pset.quiet) { if (pset.timing) puts(_("Timing is on.")); else puts(_("Timing is off.")); } free(opt); } /* \unset */ else if (strcmp(cmd, "unset") == 0) { char *opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); if (!opt) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else if (!SetVariable(pset.vars, opt, NULL)) { psql_error("\\%s: error\n", cmd); success = false; } free(opt); } /* \w -- write query buffer to file */ else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0) { FILE *fd = NULL; bool is_pipe = false; char *fname = NULL; if (!query_buf) { psql_error("no query buffer\n"); status = PSQL_CMD_ERROR; } else { fname = psql_scan_slash_option(scan_state, OT_FILEPIPE, NULL, true); expand_tilde(&fname); if (!fname) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else { if (fname[0] == '|') { is_pipe = true; fd = popen(&fname[1], "w"); } else { canonicalize_path(fname); fd = fopen(fname, "w"); } if (!fd) { psql_error("%s: %s\n", fname, strerror(errno)); success = false; } } } if (fd) { int result; if (query_buf && query_buf->len > 0) fprintf(fd, "%s\n", query_buf->data); if (is_pipe) result = pclose(fd); else result = fclose(fd); if (result == EOF) { psql_error("%s: %s\n", fname, strerror(errno)); success = false; } } free(fname); } /* \x -- toggle expanded table representation */ else if (strcmp(cmd, "x") == 0) { char *opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); success = do_pset("expanded", opt, &pset.popt, pset.quiet); free(opt); } /* \z -- list table rights (equivalent to \dp) */ else if (strcmp(cmd, "z") == 0) { char *pattern = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); success = permissionsList(pattern); if (pattern) free(pattern); } /* \! -- shell escape */ else if (strcmp(cmd, "!") == 0) { char *opt = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, false); success = do_shell(opt); free(opt); } /* \? -- slash command help */ else if (strcmp(cmd, "?") == 0) slashUsage(pset.popt.topt.pager); #if 0 /* * These commands don't do anything. I just use them to test the parser. */ else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0) { int i = 0; char *value; while ((value = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true))) { fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value); free(value); } } #endif else status = PSQL_CMD_UNKNOWN; if (!success) status = PSQL_CMD_ERROR; return status; }
/* * Dump tablespaces. */ static void dumpTablespaces(PGconn *conn) { PGresult *res; int i; /* * Get all tablespaces execpt built-in ones (named pg_xxx) * * [FIXME] the queries need to be slightly different if the backend isn't * Greenplum, and the dump format should vary depending on if the dump is * --gp-syntax or --no-gp-syntax. */ if (server_version < 80214) { /* Filespaces were introduced in GP 4.0 (server_version 8.2.14) */ return; } else { res = executeQuery(conn, "SELECT spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "fsname, spcacl, " "pg_catalog.shobj_description(t.oid, 'pg_tablespace') " "FROM pg_catalog.pg_tablespace t, " "pg_catalog.pg_filespace fs " "WHERE t.spcfsoid = fs.oid AND spcname !~ '^pg_' " "ORDER BY 1"); } if (PQntuples(res) > 0) fprintf(OPF, "--\n-- Tablespaces\n--\n\n"); for (i = 0; i < PQntuples(res); i++) { PQExpBuffer buf = createPQExpBuffer(); char *spcname = PQgetvalue(res, i, 0); char *spcowner = PQgetvalue(res, i, 1); char *fsname = PQgetvalue(res, i, 2); char *spcacl = PQgetvalue(res, i, 3); char *spccomment = PQgetvalue(res, i, 4); /* needed for buildACLCommands() */ spcname = strdup(fmtId(spcname)); if (output_clean) appendPQExpBuffer(buf, "DROP TABLESPACE %s;\n", spcname); appendPQExpBuffer(buf, "CREATE TABLESPACE %s", spcname); appendPQExpBuffer(buf, " OWNER %s", fmtId(spcowner)); appendPQExpBuffer(buf, " FILESPACE %s;\n", fmtId(fsname)); /* Build Acls */ if (!skip_acls && !buildACLCommands(spcname, "TABLESPACE", spcacl, spcowner, server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"), progname, spcacl, spcname); PQfinish(conn); exit(1); } /* Set comments */ if (spccomment && strlen(spccomment)) { appendPQExpBuffer(buf, "COMMENT ON TABLESPACE %s IS ", spcname); appendStringLiteralConn(buf, spccomment, conn); appendPQExpBuffer(buf, ";\n"); } fprintf(OPF, "%s", buf->data); free(spcname); destroyPQExpBuffer(buf); } PQclear(res); fprintf(OPF, "\n\n"); }
/* * Dump commands to create each database. * * To minimize the number of reconnections (and possibly ensuing * password prompts) required by the output script, we emit all CREATE * DATABASE commands during the initial phase of the script, and then * run pg_dump for each database to dump the contents of that * database. We skip databases marked not datallowconn, since we'd be * unable to connect to them anyway (and besides, we don't want to * dump template0). */ static void dumpCreateDB(PGconn *conn) { PQExpBuffer buf = createPQExpBuffer(); PGresult *res; int i; fprintf(OPF, "--\n-- Database creation\n--\n\n"); res = executeQuery(conn, "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "datistemplate, datacl, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " "WHERE datallowconn ORDER BY 1"); for (i = 0; i < PQntuples(res); i++) { char *dbname = PQgetvalue(res, i, 0); char *dbowner = PQgetvalue(res, i, 1); char *dbencoding = PQgetvalue(res, i, 2); char *dbistemplate = PQgetvalue(res, i, 3); char *dbacl = PQgetvalue(res, i, 4); char *dbconnlimit = PQgetvalue(res, i, 5); char *dbtablespace = PQgetvalue(res, i, 6); char *fdbname; fdbname = strdup(fmtId(dbname)); resetPQExpBuffer(buf); /* * Skip the CREATE DATABASE commands for "template1" and "postgres", * since they are presumably already there in the destination cluster. * We do want to emit their ACLs and config options if any, however. */ if (strcmp(dbname, "template1") != 0 && strcmp(dbname, "postgres") != 0) { if (output_clean) appendPQExpBuffer(buf, "DROP DATABASE %s;\n", fdbname); appendPQExpBuffer(buf, "CREATE DATABASE %s", fdbname); appendPQExpBuffer(buf, " WITH TEMPLATE = template0"); if (strlen(dbowner) != 0) appendPQExpBuffer(buf, " OWNER = %s", fmtId(dbowner)); appendPQExpBuffer(buf, " ENCODING = "); appendStringLiteralConn(buf, dbencoding, conn); /* * Output tablespace if it isn't the default. For default, it * uses the default from the template database. If tablespace is * specified and tablespace creation failed earlier, (e.g. no such * directory), the database creation will fail too. One solution * would be to use 'SET default_tablespace' like we do in pg_dump * for setting non-default database locations. */ if (strcmp(dbtablespace, "pg_default") != 0) appendPQExpBuffer(buf, " TABLESPACE = %s", fmtId(dbtablespace)); if (strcmp(dbconnlimit, "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", dbconnlimit); appendPQExpBuffer(buf, ";\n"); if (strcmp(dbistemplate, "t") == 0) { appendPQExpBuffer(buf, "UPDATE pg_database SET datistemplate = 't' WHERE datname = "); appendStringLiteralConn(buf, dbname, conn); appendPQExpBuffer(buf, ";\n"); } } if (!skip_acls && !buildACLCommands(fdbname, "DATABASE", dbacl, dbowner, server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"), progname, dbacl, fdbname); PQfinish(conn); exit(1); } fprintf(OPF, "%s", buf->data); dumpDatabaseConfig(conn, dbname); free(fdbname); } PQclear(res); destroyPQExpBuffer(buf); fprintf(OPF, "\n\n"); }
/* * processSQLNamePattern * * Scan a wildcard-pattern string and generate appropriate WHERE clauses * to limit the set of objects returned. The WHERE clauses are appended * to the already-partially-constructed query in buf. Returns whether * any clause was added. * * conn: connection query will be sent to (consulted for escaping rules). * buf: output parameter. * pattern: user-specified pattern option, or NULL if none ("*" is implied). * have_where: true if caller already emitted "WHERE" (clauses will be ANDed * onto the existing WHERE clause). * force_escape: always quote regexp special characters, even outside * double quotes (else they are quoted only between double quotes). * schemavar: name of query variable to match against a schema-name pattern. * Can be NULL if no schema. * namevar: name of query variable to match against an object-name pattern. * altnamevar: NULL, or name of an alternative variable to match against name. * visibilityrule: clause to use if we want to restrict to visible objects * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL. * * Formatting note: the text already present in buf should end with a newline. * The appended text, if any, will end with one too. */ bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule) { PQExpBufferData schemabuf; PQExpBufferData namebuf; int encoding = PQclientEncoding(conn); bool inquotes; const char *cp; int i; bool added_clause = false; #define WHEREAND() \ (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \ have_where = true, added_clause = true) if (pattern == NULL) { /* Default: select all visible objects */ if (visibilityrule) { WHEREAND(); appendPQExpBuffer(buf, "%s\n", visibilityrule); } return added_clause; } initPQExpBuffer(&schemabuf); initPQExpBuffer(&namebuf); /* * Parse the pattern, converting quotes and lower-casing unquoted letters. * Also, adjust shell-style wildcard characters into regexp notation. * * We surround the pattern with "^(...)$" to force it to match the whole * string, as per SQL practice. We have to have parens in case the string * contains "|", else the "^" and "$" will be bound into the first and * last alternatives which is not what we want. * * Note: the result of this pass is the actual regexp pattern(s) we want * to execute. Quoting/escaping into SQL literal format will be done * below using appendStringLiteralConn(). */ appendPQExpBufferStr(&namebuf, "^("); inquotes = false; cp = pattern; while (*cp) { char ch = *cp; if (ch == '"') { if (inquotes && cp[1] == '"') { /* emit one quote, stay in inquotes mode */ appendPQExpBufferChar(&namebuf, '"'); cp++; } else inquotes = !inquotes; cp++; } else if (!inquotes && isupper((unsigned char) ch)) { appendPQExpBufferChar(&namebuf, pg_tolower((unsigned char) ch)); cp++; } else if (!inquotes && ch == '*') { appendPQExpBufferStr(&namebuf, ".*"); cp++; } else if (!inquotes && ch == '?') { appendPQExpBufferChar(&namebuf, '.'); cp++; } else if (!inquotes && ch == '.') { /* Found schema/name separator, move current pattern to schema */ resetPQExpBuffer(&schemabuf); appendPQExpBufferStr(&schemabuf, namebuf.data); resetPQExpBuffer(&namebuf); appendPQExpBufferStr(&namebuf, "^("); cp++; } else if (ch == '$') { /* * Dollar is always quoted, whether inside quotes or not. The * reason is that it's allowed in SQL identifiers, so there's a * significant use-case for treating it literally, while because * we anchor the pattern automatically there is no use-case for * having it possess its regexp meaning. */ appendPQExpBufferStr(&namebuf, "\\$"); cp++; } else { /* * Ordinary data character, transfer to pattern * * Inside double quotes, or at all times if force_escape is true, * quote regexp special characters with a backslash to avoid * regexp errors. Outside quotes, however, let them pass through * as-is; this lets knowledgeable users build regexp expressions * that are more powerful than shell-style patterns. */ if ((inquotes || force_escape) && strchr("|*+?()[]{}.^$\\", ch)) appendPQExpBufferChar(&namebuf, '\\'); i = PQmblen(cp, encoding); while (i-- && *cp) { appendPQExpBufferChar(&namebuf, *cp); cp++; } } } /* * Now decide what we need to emit. Note there will be a leading "^(" in * the patterns in any case. */ if (namebuf.len > 2) { /* We have a name pattern, so constrain the namevar(s) */ appendPQExpBufferStr(&namebuf, ")$"); /* Optimize away a "*" pattern */ if (strcmp(namebuf.data, "^(.*)$") != 0) { WHEREAND(); if (altnamevar) { appendPQExpBuffer(buf, "(%s ~ ", namevar); appendStringLiteralConn(buf, namebuf.data, conn); appendPQExpBuffer(buf, "\n OR %s ~ ", altnamevar); appendStringLiteralConn(buf, namebuf.data, conn); appendPQExpBufferStr(buf, ")\n"); } else { appendPQExpBuffer(buf, "%s ~ ", namevar); appendStringLiteralConn(buf, namebuf.data, conn); appendPQExpBufferChar(buf, '\n'); } } } if (schemabuf.len > 2) { /* We have a schema pattern, so constrain the schemavar */ appendPQExpBufferStr(&schemabuf, ")$"); /* Optimize away a "*" pattern */ if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar) { WHEREAND(); appendPQExpBuffer(buf, "%s ~ ", schemavar); appendStringLiteralConn(buf, schemabuf.data, conn); appendPQExpBufferChar(buf, '\n'); } } else { /* No schema pattern given, so select only visible objects */ if (visibilityrule) { WHEREAND(); appendPQExpBuffer(buf, "%s\n", visibilityrule); } } termPQExpBuffer(&schemabuf); termPQExpBuffer(&namebuf); return added_clause; #undef WHEREAND }
/* * Dump commands to create each database. * * To minimize the number of reconnections (and possibly ensuing * password prompts) required by the output script, we emit all CREATE * DATABASE commands during the initial phase of the script, and then * run pg_dump for each database to dump the contents of that * database. We skip databases marked not datallowconn, since we'd be * unable to connect to them anyway (and besides, we don't want to * dump template0). */ static void dumpCreateDB(PGconn *conn) { PQExpBuffer buf = createPQExpBuffer(); char *default_encoding = NULL; char *default_collate = NULL; char *default_ctype = NULL; PGresult *res; int i; fprintf(OPF, "--\n-- Database creation\n--\n\n"); /* * First, get the installation's default encoding and locale information. * We will dump encoding and locale specifications in the CREATE DATABASE * commands for just those databases with values different from defaults. * * We consider template0's encoding and locale (or, pre-7.1, template1's) * to define the installation default. Pre-8.4 installations do not have * per-database locale settings; for them, every database must necessarily * be using the installation default, so there's no need to do anything * (which is good, since in very old versions there is no good way to find * out what the installation locale is anyway...) */ if (server_version >= 80400) res = executeQuery(conn, "SELECT pg_encoding_to_char(encoding), " "datcollate, datctype " "FROM pg_database " "WHERE datname = 'template0'"); else if (server_version >= 70100) res = executeQuery(conn, "SELECT pg_encoding_to_char(encoding), " "null::text AS datcollate, null::text AS datctype " "FROM pg_database " "WHERE datname = 'template0'"); else res = executeQuery(conn, "SELECT pg_encoding_to_char(encoding), " "null::text AS datcollate, null::text AS datctype " "FROM pg_database " "WHERE datname = 'template1'"); /* If for some reason the template DB isn't there, treat as unknown */ if (PQntuples(res) > 0) { if (!PQgetisnull(res, 0, 0)) default_encoding = strdup(PQgetvalue(res, 0, 0)); if (!PQgetisnull(res, 0, 1)) default_collate = strdup(PQgetvalue(res, 0, 1)); if (!PQgetisnull(res, 0, 2)) default_ctype = strdup(PQgetvalue(res, 0, 2)); } PQclear(res); /* Now collect all the information about databases to dump */ if (server_version >= 80400) res = executeQuery(conn, "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "datcollate, datctype, datfrozenxid, " "datistemplate, datacl, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 80100) res = executeQuery(conn, "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 80000) res = executeQuery(conn, "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, -1 as datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70300) res = executeQuery(conn, "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70100) res = executeQuery(conn, "SELECT datname, " "coalesce(" "(select usename from pg_shadow where usesysid=datdba), " "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " "datistemplate, '' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d " "WHERE datallowconn ORDER BY 1"); else { /* * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal * with getting a NULL by not printing any OWNER clause. */ res = executeQuery(conn, "SELECT datname, " "(select usename from pg_shadow where usesysid=datdba), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " "'f' as datistemplate, " "'' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d " "ORDER BY 1"); } for (i = 0; i < PQntuples(res); i++) { char *dbname = PQgetvalue(res, i, 0); char *dbowner = PQgetvalue(res, i, 1); char *dbencoding = PQgetvalue(res, i, 2); char *dbcollate = PQgetvalue(res, i, 3); char *dbctype = PQgetvalue(res, i, 4); uint32 dbfrozenxid = atooid(PQgetvalue(res, i, 5)); char *dbistemplate = PQgetvalue(res, i, 6); char *dbacl = PQgetvalue(res, i, 7); char *dbconnlimit = PQgetvalue(res, i, 8); char *dbtablespace = PQgetvalue(res, i, 9); char *fdbname; fdbname = strdup(fmtId(dbname)); resetPQExpBuffer(buf); /* * Skip the CREATE DATABASE commands for "template1" and "postgres", * since they are presumably already there in the destination cluster. * We do want to emit their ACLs and config options if any, however. */ if (strcmp(dbname, "template1") != 0 && strcmp(dbname, "postgres") != 0) { appendPQExpBuffer(buf, "CREATE DATABASE %s", fdbname); appendPQExpBuffer(buf, " WITH TEMPLATE = template0"); if (strlen(dbowner) != 0) appendPQExpBuffer(buf, " OWNER = %s", fmtId(dbowner)); if (default_encoding && strcmp(dbencoding, default_encoding) != 0) { appendPQExpBuffer(buf, " ENCODING = "); appendStringLiteralConn(buf, dbencoding, conn); } if (default_collate && strcmp(dbcollate, default_collate) != 0) { appendPQExpBuffer(buf, " LC_COLLATE = "); appendStringLiteralConn(buf, dbcollate, conn); } if (default_ctype && strcmp(dbctype, default_ctype) != 0) { appendPQExpBuffer(buf, " LC_CTYPE = "); appendStringLiteralConn(buf, dbctype, conn); } /* * Output tablespace if it isn't the default. For default, it * uses the default from the template database. If tablespace is * specified and tablespace creation failed earlier, (e.g. no such * directory), the database creation will fail too. One solution * would be to use 'SET default_tablespace' like we do in pg_dump * for setting non-default database locations. */ if (strcmp(dbtablespace, "pg_default") != 0 && !no_tablespaces) appendPQExpBuffer(buf, " TABLESPACE = %s", fmtId(dbtablespace)); if (strcmp(dbconnlimit, "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", dbconnlimit); appendPQExpBuffer(buf, ";\n"); if (strcmp(dbistemplate, "t") == 0) { appendPQExpBuffer(buf, "UPDATE pg_catalog.pg_database SET datistemplate = 't' WHERE datname = "); appendStringLiteralConn(buf, dbname, conn); appendPQExpBuffer(buf, ";\n"); } if (binary_upgrade) { appendPQExpBuffer(buf, "-- For binary upgrade, set datfrozenxid.\n"); appendPQExpBuffer(buf, "UPDATE pg_catalog.pg_database " "SET datfrozenxid = '%u' " "WHERE datname = ", dbfrozenxid); appendStringLiteralConn(buf, dbname, conn); appendPQExpBuffer(buf, ";\n"); } } if (!skip_acls && !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner, "", server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"), progname, dbacl, fdbname); PQfinish(conn); exit(1); } fprintf(OPF, "%s", buf->data); if (server_version >= 70300) dumpDatabaseConfig(conn, dbname); free(fdbname); } PQclear(res); destroyPQExpBuffer(buf); fprintf(OPF, "\n\n"); }