Hi

2016-09-26 14:57 GMT+02:00 Ryan Murphy <ryanfmur...@gmail.com>:

> 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 (pgsql-hackers@postgresql.org)
> 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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to