Hi
2016-09-26 14:57 GMT+02:00 Ryan Murphy <[email protected]>:
> Hi Pavel,
>
> I just tried to apply your patch psql-setfileref-initial.patch (using git
> apply) to the newest revision of postgres at the time (da6c4f6ca88) and it
> failed to patch startup.c. Thinking that the patch was for some previous
> revision, I then checked out d062245b5, which was from 2016-08-31, and
> tried applying the patch from there. I had the same problem:
>
> $ git apply psql-setfileref-initial.patch
> error: patch failed: src/bin/psql/startup.c:106
> error: src/bin/psql/startup.c: patch does not apply
>
> However, when I applied the changes to startup.c manually and removed them
> from the patch, the rest of the patch applied without a problem. I don't
> know if I may have done something wrong in trying to apply the patch, but
> you may want to double check if you need to regenerate your patch from the
> latest revision so it will apply smoothly for reviewers.
>
please, can you check attached patch? It worked in my laptop.
Regards
Pavel
>
> In the meantime, I haven't had a chance to try out the fileref feature yet
> but I'll give it a go when I get a chance!
>
> Thanks!
> Ryan
> --
> Sent via pgsql-hackers mailing list ([email protected])
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index a9a2fdb..af38ff9 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1363,6 +1363,32 @@ exec_command(const char *cmd,
free(envval);
}
+ /* \setfileref - set variable by reference on file */
+ else if (strcmp(cmd, "setfileref") == 0)
+ {
+ char *name = psql_scan_slash_option(scan_state,
+ OT_NORMAL, NULL, false);
+
+ char *ref = psql_scan_slash_option(scan_state,
+ OT_NORMAL, NULL, false);
+
+ success = false;
+
+ if (!name || !ref)
+ {
+ psql_error("\\%s: missing required argument\n", cmd);
+ success = false;
+ }
+ else
+ {
+ if (!SetFileRef(pset.vars, name, ref))
+ {
+ psql_error("\\%s: error while setting variable\n", cmd);
+ success = false;
+ }
+ }
+ }
+
/* \sf -- show a function's source code */
else if (strcmp(cmd, "sf") == 0 || strcmp(cmd, "sf+") == 0)
{
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index a7789df..b160228 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -25,6 +25,7 @@
#include "settings.h"
#include "command.h"
#include "copy.h"
+#include "catalog/pg_type.h"
#include "crosstabview.h"
#include "fe_utils/mbprint.h"
@@ -33,7 +34,6 @@ static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec);
static bool command_no_begin(const char *query);
static bool is_select_command(const char *query);
-
/*
* openQueryOutputFile --- attempt to open a query output file
*
@@ -109,6 +109,120 @@ setQFout(const char *fname)
return true;
}
+void
+psql_reset_query_params(void)
+{
+ int i;
+
+ for (i = 0; i < pset.nparams; i++)
+ if (pset.params[i] != NULL)
+ {
+ PQfreemem(pset.params[i]);
+ pset.params[i] = NULL;
+ }
+
+ pset.nparams = 0;
+}
+
+/*
+ * Load a content of the file_ref related file to query params buffer.
+ * When escaping is requested, then the text content is expected.
+ * Without escaping the bytea content is expected and related bytea
+ * escaping is processed.
+ */
+static char *
+get_file_ref_content(const char *value, bool escape, bool as_ident)
+{
+ PQExpBufferData buffer;
+ FILE *fd = NULL;
+ char *fname;
+ char *escaped_value;
+ char line[1024];
+ size_t size;
+
+ fname = pstrdup(value);
+
+ expand_tilde(&fname);
+ if (!fname)
+ {
+ psql_error("missing valid path to file\n");
+ return NULL;
+ }
+
+ canonicalize_path(fname);
+
+ fd = fopen(fname, PG_BINARY_R);
+ if (!fd)
+ {
+ psql_error("%s: %s\n", fname, strerror(errno));
+ PQfreemem(fname);
+ return NULL;
+ }
+
+ /* can append another parameter */
+ if (pset.nparams >= MAX_BINARY_PARAMS)
+ {
+ psql_error("too much binary parameters");
+ PQfreemem(fname);
+ return NULL;
+ }
+
+ if (!pset.db)
+ {
+ psql_error("cannot escape without active connection\n");
+ PQfreemem(fname);
+ return NULL;
+ }
+
+ initPQExpBuffer(&buffer);
+
+ while ((size = fread(line, 1, sizeof(line), fd)) > 0)
+ appendBinaryPQExpBuffer(&buffer, line, size);
+
+ if (ferror(fd))
+ {
+ psql_error("%s: %s\n", fname, strerror(errno));
+ PQfreemem(fname);
+ termPQExpBuffer(&buffer);
+ return NULL;
+ }
+
+ if (escape)
+ {
+ if (as_ident)
+ escaped_value =
+ PQescapeIdentifier(pset.db, buffer.data, buffer.len);
+ else
+ escaped_value =
+ PQescapeLiteral(pset.db, buffer.data, buffer.len);
+ pset.paramTypes[pset.nparams] = UNKNOWNOID;
+ }
+ else
+ {
+ escaped_value = (char *)
+ PQescapeByteaConn(pset.db,
+ (const unsigned char *) buffer.data, buffer.len, &size);
+ pset.paramTypes[pset.nparams] = BYTEAOID;
+ }
+
+ /* fname, buffer is not necessary longer */
+ PQfreemem(fname);
+ termPQExpBuffer(&buffer);
+
+ if (escaped_value == NULL)
+ {
+ const char *error = PQerrorMessage(pset.db);
+
+ psql_error("%s", error);
+ return NULL;
+ }
+
+ pset.params[pset.nparams] = escaped_value;
+
+ snprintf(line, sizeof(line) - 1, "$%d", ++pset.nparams);
+
+ return pstrdup(line);
+}
/*
* Variable-fetching callback for flex lexer
@@ -125,11 +239,15 @@ psql_get_variable(const char *varname, bool escape, bool as_ident)
{
char *result;
const char *value;
+ bool is_file_ref;
- value = GetVariable(pset.vars, varname);
+ value = (char *) GetVariableOrFileRef(pset.vars, varname, &is_file_ref);
if (!value)
return NULL;
+ if (is_file_ref)
+ return get_file_ref_content(value, escape, as_ident);
+
if (escape)
{
char *escaped_value;
@@ -1287,7 +1405,16 @@ SendQuery(const char *query)
if (pset.timing)
INSTR_TIME_SET_CURRENT(before);
- results = PQexec(pset.db, query);
+ if (pset.nparams > 0)
+ results = PQexecParams(pset.db, query,
+ pset.nparams,
+ pset.paramTypes,
+ (const char * const *) pset.params,
+ NULL,
+ NULL,
+ 0);
+ else
+ results = PQexec(pset.db, query);
/* these operations are included in the timing result: */
ResetCancelConn();
@@ -1432,7 +1559,6 @@ sendquery_cleanup:
return OK;
}
-
/*
* ExecQueryUsingCursor: run a SELECT-like query using a cursor
*
diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h
index bdcb58f..4f46b9c 100644
--- a/src/bin/psql/common.h
+++ b/src/bin/psql/common.h
@@ -38,6 +38,8 @@ extern int PSQLexecWatch(const char *query, const printQueryOpt *opt);
extern bool SendQuery(const char *query);
+void psql_reset_query_params(void);
+
extern bool is_superuser(void);
extern bool standard_strings(void);
extern const char *session_username(void);
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index 37dfa4d..23fd6d3 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -23,7 +23,6 @@ const PsqlScanCallbacks psqlscan_callbacks = {
psql_error
};
-
/*
* Main processing loop for reading lines of input
* and sending them to the backend.
@@ -403,6 +402,9 @@ MainLoop(FILE *source)
psql_scan_finish(scan_state);
free(line);
+ /* reset binary parameters */
+ psql_reset_query_params();
+
if (slashCmdStatus == PSQL_CMD_TERMINATE)
{
successResult = EXIT_SUCCESS;
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 8cfe9d2..2874704 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -77,6 +77,8 @@ enum trivalue
TRI_YES
};
+#define MAX_BINARY_PARAMS 32
+
typedef struct _psqlSettings
{
PGconn *db; /* connection to backend */
@@ -135,6 +137,9 @@ typedef struct _psqlSettings
const char *prompt3;
PGVerbosity verbosity; /* current error verbosity level */
PGContextVisibility show_context; /* current context display level */
+ int nparams;
+ Oid paramTypes[MAX_BINARY_PARAMS];
+ char *params[MAX_BINARY_PARAMS];
} PsqlSettings;
extern PsqlSettings pset;
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 7ce05fb..05715a6 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -107,6 +107,7 @@ main(int argc, char *argv[])
char password[100];
char *password_prompt = NULL;
bool new_pass;
+ int i;
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("psql"));
@@ -140,6 +141,10 @@ main(int argc, char *argv[])
pset.cur_cmd_source = stdin;
pset.cur_cmd_interactive = false;
+ pset.nparams = 0;
+ for (i = 0; i < MAX_BINARY_PARAMS; i++)
+ pset.params[i] = NULL;
+
/* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */
pset.popt.topt.format = PRINT_ALIGNED;
pset.popt.topt.border = 1;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 50a45eb..ff4bf73 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1294,7 +1294,7 @@ psql_completion(const char *text, int start, int end)
"\\f", "\\g", "\\gexec", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
- "\\s", "\\set", "\\setenv", "\\sf", "\\sv", "\\t", "\\T",
+ "\\s", "\\set", "\\setenv", "\\setfileref", "\\sf", "\\sv", "\\t", "\\T",
"\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL
};
@@ -3128,6 +3128,12 @@ psql_completion(const char *text, int start, int end)
matches = completion_matches(text, complete_from_files);
}
+ else if (Matches2("\\setfileref", MatchAny))
+ {
+ completion_charp = "\\";
+ matches = completion_matches(text, complete_from_files);
+ }
+
/*
* Finally, we look through the list of "things", such as TABLE, INDEX and
* check if that was the previous word. If so, execute the query to get a
diff --git a/src/bin/psql/variables.c b/src/bin/psql/variables.c
index f43f418..1c12719 100644
--- a/src/bin/psql/variables.c
+++ b/src/bin/psql/variables.c
@@ -71,6 +71,31 @@ GetVariable(VariableSpace space, const char *name)
if (strcmp(current->name, name) == 0)
{
/* this is correct answer when value is NULL, too */
+
+ return current->value;
+ }
+ }
+
+ return NULL;
+}
+
+const char *
+GetVariableOrFileRef(VariableSpace space, const char *name, bool *is_file_ref)
+{
+ struct _variable *current;
+
+ if (!space)
+ return NULL;
+
+ for (current = space->next; current; current = current->next)
+ {
+ if (strcmp(current->name, name) == 0)
+ {
+ /* this is correct answer when value is NULL, too */
+
+ if (is_file_ref)
+ *is_file_ref = current->is_file_ref;
+
return current->value;
}
}
@@ -178,7 +203,7 @@ PrintVariables(VariableSpace space)
for (ptr = space->next; ptr; ptr = ptr->next)
{
if (ptr->value)
- printf("%s = '%s'\n", ptr->name, ptr->value);
+ printf("%s = %s'%s'\n", ptr->name, ptr->is_file_ref ? "^" : "", ptr->value);
if (cancel_pressed)
break;
}
@@ -218,6 +243,47 @@ SetVariable(VariableSpace space, const char *name, const char *value)
/* not present, make new entry */
current = pg_malloc(sizeof *current);
current->name = pg_strdup(name);
+ current->is_file_ref = false;
+ current->value = pg_strdup(value);
+ current->assign_hook = NULL;
+ current->next = NULL;
+ previous->next = current;
+ return true;
+}
+
+bool
+SetFileRef(VariableSpace space, const char *name, const char *value)
+{
+ struct _variable *current,
+ *previous;
+
+ if (!space)
+ return false;
+
+ if (!valid_variable_name(name))
+ return false;
+
+ if (!value)
+ return DeleteVariable(space, name);
+
+ for (previous = space, current = space->next;
+ current;
+ previous = current, current = current->next)
+ {
+ if (strcmp(current->name, name) == 0)
+ {
+ /* found entry, so update */
+ if (current->value)
+ free(current->value);
+ current->value = pg_strdup(value);
+ return true;
+ }
+ }
+
+ /* not present, make new entry */
+ current = pg_malloc(sizeof *current);
+ current->is_file_ref = true;
+ current->name = pg_strdup(name);
current->value = pg_strdup(value);
current->assign_hook = NULL;
current->next = NULL;
diff --git a/src/bin/psql/variables.h b/src/bin/psql/variables.h
index d7a05a1..8b24441 100644
--- a/src/bin/psql/variables.h
+++ b/src/bin/psql/variables.h
@@ -26,6 +26,7 @@ struct _variable
{
char *name;
char *value;
+ bool is_file_ref;
VariableAssignHook assign_hook;
struct _variable *next;
};
@@ -34,6 +35,7 @@ typedef struct _variable *VariableSpace;
VariableSpace CreateVariableSpace(void);
const char *GetVariable(VariableSpace space, const char *name);
+const char *GetVariableOrFileRef(VariableSpace space, const char *name, bool *is_file_ref);
bool ParseVariableBool(const char *value, const char *name);
int ParseVariableNum(const char *val,
@@ -49,6 +51,7 @@ int GetVariableNum(VariableSpace space,
void PrintVariables(VariableSpace space);
bool SetVariable(VariableSpace space, const char *name, const char *value);
+bool SetFileRef(VariableSpace space, const char *name, const char *value);
bool SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook);
bool SetVariableBool(VariableSpace space, const char *name);
bool DeleteVariable(VariableSpace space, const char *name);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers