PSQL_EDITOR question

This supposed to be just a short TIL entry about PSQL_EDITOR variable. While trying to figure out all possible pitfalls, I’ve decided to write a full blog post. You may skip to the summary though. All tests done on Windows 10 with PostgreSQL 12 installed.

As you know, PostgreSQL ships with an interactive console tool named psql, which allows you to use a lot of enhanced features, like watching, for example. There is a lots of information about tweaking psql in POSIX environments. Still, there is a lack of such tips for Windows users. If you, my dear reader, are a Linux user, I’m sure you will also find this information useful anyway. Who knows what the next purchase of Microsoft will be. After LinkedIn, GitHub, and Citus, you cannot be sure anymore! 😉

I’ve chosen Sublime as a target because it’s not a simple console or single window application, but rather an advanced text processor with enhanced file handling, e.g. it will restore the unsaved files on startup.

According to the manual PSQL_EDITOR, EDITOR and VISUAL are environment variables, which are used inside psql to call an editor for input.

Let’s try to set the environment variable then.

$ SET PSQL_EDITOR=C:\Program Files\Sublime Text 3\subl.exe

$ psql

postgres=# \e

postgres=# \e
could not open temporary file "C:\Temp\psql.edit.3288.sql": File exists

As you can see nothing was executed after the first \e command, even though I’ve edited and saved the content of the query.
What’s wrong here? First of all, I’ve used subl.exe instead of sublime.exe which is special command line tool:

Sublime Text includes a command line tool, subl, to work with files on the command line. This can be used to open files and projects in Sublime Text, and also works as an EDITOR for unix tools, such as git and subversion.

psql runs editor and then waits for the editor process to finish. But subl is just a special tool, which in fact launches sublime.exe and then just dies. So psql gets signal about subl finishes and returns, but the temp file wasn’t even edited yet.

When you’re trying to execute \e next time, is reports about temp file existence.

Let’s check Sublime documentation:

Specifying -w will cause the subl command to not exit until the file is closed.

But the problem is we cannot pass additional parameters in Windows:

$ SET PSQL_EDITOR=C:\Program Files\Sublime Text 3\subl.exe -w

$ psql -d postgres 
psql (12.1) 
Type "help" for help. 

postgres=# \e     
'"C:\Program Files\Sublime Text 3\subl.exe -w"' is not recognized as 
an internal or external command, operable program or batch file. 

The only additional parameter that can be passed is PSQL_EDITOR_LINENUMBER_ARG. Let’s try to examine psql sources, to know if we can pass arbitrary command line into:

static bool
editFile(const char *fname, int lineno)
{
	...

		/*
	 * On Unix the EDITOR value should *not* be quoted, since it might include
	 * switches, eg, EDITOR="pico -t"; it's up to the user to put quotes in it
	 * if necessary.  But this policy is not very workable on Windows, due to
	 * severe brain damage in their command shell plus the fact that standard
	 * program paths include spaces.
	 */
	...
	if (lineno > 0)
		sys = psprintf("\"%s\" %s%d \"%s\"",
					   editorName, editor_lineno_arg, lineno, fname);
	else
		sys = psprintf("\"%s\" \"%s\"",
					   editorName, fname);
	...
}

You can see now why we cannot include any arguments in the PSQL_EDITOR. It’s quoted by default before execution on Windows.

Of course, we may use just sublime_text.exe:

$ SET PSQL_EDITOR=C:\Program Files\Sublime Text 3\sublime_text.exe

$ psql -d postgres  
psql (12.1)  
Type "help" for help.

postgres=# \set ECHO queries
postgres=# \e         
select version();
                          version
------------------------------------------------------------
 PostgreSQL 12.1, compiled by Visual C++ build 1914, 64-bit
(1 row)

This approach has one definite disadvantage. You need to close Sublime editor, not only tab with the file if you want to continue to work with psql. Assuming we’re usually working with a lot of files simultaneously that is a huge problem.

To keep all advantages of Sublime Text we may create a simple batch script in the home folder:

$ ECHO @ECHO OFF > %USERPROFILE%\subl.bat

$ ECHO "C:\Program Files\Sublime Text 3\subl.exe" --wait %* >> %USERPROFILE%\subl.bat

$ type %USERPROFILE%\subl.bat
@ECHO OFF
"C:\Program Files\Sublime Text 3\subl.exe" --wait %*

$ SET PSQL_EDITOR=%USERPROFILE%\subl.bat

$ psql -d postgres          
psql (12.1)                 
Type "help" for help.       

postgres=# \set ECHO queries
postgres=# \e               
select version();
                          version
------------------------------------------------------------
 PostgreSQL 12.1, compiled by Visual C++ build 1914, 64-bit
(1 row)

It works like a charm! You’ve just closed the tab and continued working with psql normally.

In the screenshot above you may see a very common error people are constantly making. They are trying to set EDITOR inside the psql and use SET command for that. SET is used for internal psql variables, to work with environment variables one should use \setenv command. But this time, make sure you’re using proper quoting:

postgres=# \setenv PSQL_EDITOR %USERPROFILE%\subl.bat
invalid command \subl.bat
Try \? for help.

postgres=# \setenv PSQL_EDITOR "%USERPROFILE%\subl.bat"
postgres=# \e
select version();
                          version
------------------------------------------------------------
 PostgreSQL 12.1, compiled by Visual C++ build 1914, 64-bit
(1 row)

TL;DR:

1. PSQL_EDITOR is the environment variable controlled by shell. You should set it before calling psql or inside psql with special command \setenv
2. There are several synonyms available PSQL_EDITOR, EDITOR, VISUAL
3. In Windows you cannot use arbitrary shell command as PSQL_EDITOR value, only the name or full path of proper file (binary, batch, .cmd, etc) because of the forced quoting inside psql
4. If you need some complex command to be used, use script for that (cmd, powershell) or create shortcut with proper arguments.