88from time import sleep
99
1010import click
11+ import pyperclip
1112import sqlparse
1213
1314from . import export
2324tee_file = None
2425once_file = None
2526written_to_once_file = False
27+ pipe_once_process = None
28+ written_to_pipe_once_process = False
2629delimiter_command = DelimiterCommand ()
2730
2831
@@ -115,7 +118,7 @@ def get_editor_query(sql):
115118 # The reason we can't simply do .strip('\e') is that it strips characters,
116119 # not a substring. So it'll strip "e" in the end of the sql also!
117120 # Ex: "select * from style\e" -> "select * from styl".
118- pattern = re .compile ('(^\\ \e| \ \ \e$)' )
121+ pattern = re .compile (r '(^\\e| \\e$)' )
119122 while pattern .search (sql ):
120123 sql = pattern .sub ('' , sql )
121124
@@ -159,6 +162,47 @@ def open_external_editor(filename=None, sql=None):
159162 return (query , message )
160163
161164
165+ @export
166+ def clip_command (command ):
167+ """Is this a clip command?
168+
169+ :param command: string
170+
171+ """
172+ # It is possible to have `\clip` or `SELECT * FROM \clip`. So we check
173+ # for both conditions.
174+ return command .strip ().endswith ('\\ clip' ) or command .strip ().startswith ('\\ clip' )
175+
176+
177+ @export
178+ def get_clip_query (sql ):
179+ """Get the query part of a clip command."""
180+ sql = sql .strip ()
181+
182+ # The reason we can't simply do .strip('\clip') is that it strips characters,
183+ # not a substring. So it'll strip "c" in the end of the sql also!
184+ pattern = re .compile ('(^\\ \clip|\\ \clip$)' )
185+ while pattern .search (sql ):
186+ sql = pattern .sub ('' , sql )
187+
188+ return sql
189+
190+
191+ @export
192+ def copy_query_to_clipboard (sql = None ):
193+ """Send query to the clipboard."""
194+
195+ sql = sql or ''
196+ message = None
197+
198+ try :
199+ pyperclip .copy (u'{sql}' .format (sql = sql ))
200+ except RuntimeError as e :
201+ message = 'Error clipping query: %s.' % e .strerror
202+
203+ return message
204+
205+
162206@special_command ('\\ f' , '\\ f [name [args..]]' , 'List or execute favorite queries.' , arg_type = PARSED_QUERY , case_sensitive = True )
163207def execute_favorite_query (cur , arg , ** _ ):
164208 """Returns (title, rows, headers, status)"""
@@ -337,7 +381,11 @@ def write_tee(output):
337381def set_once (arg , ** _ ):
338382 global once_file , written_to_once_file
339383
340- once_file = parseargfile (arg )
384+ try :
385+ once_file = open (** parseargfile (arg ))
386+ except (IOError , OSError ) as e :
387+ raise OSError ("Cannot write to file '{}': {}" .format (
388+ e .filename , e .strerror ))
341389 written_to_once_file = False
342390
343391 return [(None , None , None , "" )]
@@ -347,26 +395,68 @@ def set_once(arg, **_):
347395def write_once (output ):
348396 global once_file , written_to_once_file
349397 if output and once_file :
350- try :
351- f = open (** once_file )
352- except (IOError , OSError ) as e :
353- once_file = None
354- raise OSError ("Cannot write to file '{}': {}" .format (
355- e .filename , e .strerror ))
356- with f :
357- click .echo (output , file = f , nl = False )
358- click .echo (u"\n " , file = f , nl = False )
398+ click .echo (output , file = once_file , nl = False )
399+ click .echo (u"\n " , file = once_file , nl = False )
400+ once_file .flush ()
359401 written_to_once_file = True
360402
361403
362404@export
363405def unset_once_if_written ():
364406 """Unset the once file, if it has been written to."""
365- global once_file
366- if written_to_once_file :
407+ global once_file , written_to_once_file
408+ if written_to_once_file and once_file :
409+ once_file .close ()
367410 once_file = None
368411
369412
413+ @special_command ('\\ pipe_once' , '\\ | command' ,
414+ 'Send next result to a subprocess.' ,
415+ aliases = ('\\ |' , ))
416+ def set_pipe_once (arg , ** _ ):
417+ global pipe_once_process , written_to_pipe_once_process
418+ pipe_once_cmd = shlex .split (arg )
419+ if len (pipe_once_cmd ) == 0 :
420+ raise OSError ("pipe_once requires a command" )
421+ written_to_pipe_once_process = False
422+ pipe_once_process = subprocess .Popen (pipe_once_cmd ,
423+ stdin = subprocess .PIPE ,
424+ stdout = subprocess .PIPE ,
425+ stderr = subprocess .PIPE ,
426+ bufsize = 1 ,
427+ encoding = 'UTF-8' ,
428+ universal_newlines = True )
429+ return [(None , None , None , "" )]
430+
431+
432+ @export
433+ def write_pipe_once (output ):
434+ global pipe_once_process , written_to_pipe_once_process
435+ if output and pipe_once_process :
436+ try :
437+ click .echo (output , file = pipe_once_process .stdin , nl = False )
438+ click .echo (u"\n " , file = pipe_once_process .stdin , nl = False )
439+ except (IOError , OSError ) as e :
440+ pipe_once_process .terminate ()
441+ raise OSError (
442+ "Failed writing to pipe_once subprocess: {}" .format (e .strerror ))
443+ written_to_pipe_once_process = True
444+
445+
446+ @export
447+ def unset_pipe_once_if_written ():
448+ """Unset the pipe_once cmd, if it has been written to."""
449+ global pipe_once_process , written_to_pipe_once_process
450+ if written_to_pipe_once_process :
451+ (stdout_data , stderr_data ) = pipe_once_process .communicate ()
452+ if len (stdout_data ) > 0 :
453+ print (stdout_data .rstrip (u"\n " ))
454+ if len (stderr_data ) > 0 :
455+ print (stderr_data .rstrip (u"\n " ))
456+ pipe_once_process = None
457+ written_to_pipe_once_process = False
458+
459+
370460@special_command (
371461 'watch' ,
372462 'watch [seconds] [-c] query' ,
0 commit comments