This is an automated email from the ASF dual-hosted git repository.

fjy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-druid.git


The following commit(s) were added to refs/heads/master by this push:
     new ac4c7e2  Enhancements to dsql. (#6929)
ac4c7e2 is described below

commit ac4c7e21a2995e31f79246cca32c12a338c3d3ad
Author: Gian Merlino <[email protected]>
AuthorDate: Mon Jan 28 20:02:43 2019 -0500

    Enhancements to dsql. (#6929)
    
    - CLI history, basic autocomplete through deadline.
    - Include timeout in query context.
    - Group CLI options into... groups.
---
 examples/bin/dsql      |  11 +----
 examples/bin/dsql-main | 112 ++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 85 insertions(+), 38 deletions(-)

diff --git a/examples/bin/dsql b/examples/bin/dsql
index 004b01e..b402e17 100755
--- a/examples/bin/dsql
+++ b/examples/bin/dsql
@@ -21,16 +21,9 @@ PWD="$(pwd)"
 WHEREAMI="$(dirname "$0")"
 WHEREAMI="$(cd "$WHEREAMI" && pwd)"
 
-RLWRAP=""
-
-if [ -x "$(command -v rlwrap)" ]
-then
-  RLWRAP="rlwrap -C dsql"
-fi
-
 if [ -x "$(command -v python2)" ]
 then
-  exec $RLWRAP python2 "$WHEREAMI/dsql-main" "$@"
+  exec python2 "$WHEREAMI/dsql-main" "$@"
 else
-  exec $RLWRAP "$WHEREAMI/dsql-main" "$@"
+  exec "$WHEREAMI/dsql-main" "$@"
 fi
diff --git a/examples/bin/dsql-main b/examples/bin/dsql-main
index c8fee6f..8128931 100755
--- a/examples/bin/dsql-main
+++ b/examples/bin/dsql-main
@@ -26,7 +26,9 @@ import csv
 import errno
 import json
 import numbers
+import os
 import re
+import readline
 import ssl
 import sys
 import time
@@ -34,17 +36,30 @@ import unicodedata
 import urllib2
 
 class DruidSqlException(Exception):
+  def friendly_message(self):
+    return self.message if self.message else "Query failed"
+
   def write_to(self, f):
     f.write('\x1b[31m')
-    f.write(self.message if self.message else "Query failed")
+    f.write(self.friendly_message())
     f.write('\x1b[0m')
     f.write('\n')
     f.flush()
 
-def do_query(url, sql, context, timeout, user, password, 
ignore_ssl_verification, ca_file, ca_path):
+def do_query_with_args(url, sql, context, args):
+  return do_query(url, sql, context, args.timeout, args.user, 
args.ignore_ssl_verification, args.cafile, args.capath)
+
+def do_query(url, sql, context, timeout, user, ignore_ssl_verification, 
ca_file, ca_path):
   json_decoder = json.JSONDecoder(object_pairs_hook=collections.OrderedDict)
   try:
-    sql_json = json.dumps({'query' : sql, 'context' : context})
+    if timeout <= 0:
+      timeout = None
+      query_context = context
+    elif int(context.get('timeout', 0)) / 1000 < timeout:
+      query_context = context.copy()
+      query_context['timeout'] = timeout * 1000;
+
+    sql_json = json.dumps({'query' : sql, 'context' : query_context})
 
     # SSL stuff
     ssl_context =  None;
@@ -57,12 +72,9 @@ def do_query(url, sql, context, timeout, user, password, 
ignore_ssl_verification
         ssl_context.load_verify_locations(cafile=ca_file, capath=ca_path)
 
     req = urllib2.Request(url, sql_json, {'Content-Type' : 'application/json'})
-    if timeout <= 0:
-      timeout = None
 
-    if (user and password):
-      basicAuthEncoding = base64.b64encode('%s:%s' % (user, password))
-      req.add_header("Authorization", "Basic %s" % basicAuthEncoding)
+    if user:
+      req.add_header("Authorization", "Basic %s" % base64.b64encode(user))
 
     response = urllib2.urlopen(req, None, timeout, context=ssl_context)
 
@@ -311,7 +323,7 @@ def print_table(rows):
   print("")
 
 def display_query(url, sql, context, args):
-  rows = do_query(url, sql, context, args.timeout, args.user, args.password, 
args.ignore_ssl_verification, args.cafile, args.capath)
+  rows = do_query_with_args(url, sql, context, args)
 
   if args.format == 'csv':
     print_csv(rows, args.header)
@@ -322,7 +334,7 @@ def display_query(url, sql, context, args):
   elif args.format == 'table':
     print_table(rows)
 
-def sql_escape(s):
+def sql_literal_escape(s):
   if s is None:
     return "''"
   elif isinstance(s, unicode):
@@ -343,20 +355,49 @@ def sql_escape(s):
   escaped.append("'")
   return ''.join(escaped)
 
+def make_readline_completer(url, context, args):
+  starters = [
+    'EXPLAIN PLAN FOR',
+    'SELECT'
+  ]
+
+  middlers = [
+    'FROM',
+    'WHERE',
+    'GROUP BY',
+    'ORDER BY',
+    'LIMIT'
+  ]
+
+  def readline_completer(text, state):
+    if readline.get_begidx() == 0:
+      results = [x for x in starters if x.startswith(text.upper())] + [None]
+    else:
+      results = ([x for x in middlers if x.startswith(text.upper())] + [None])
+
+    return results[state] + " "
+
+  print("Connected to [" + args.host + "].")
+  print("")
+
+  return readline_completer
+
 def main():
   parser = argparse.ArgumentParser(description='Druid SQL command-line 
client.')
-  parser.add_argument('--host', '-H', type=str, 
default='http://localhost:8082/', help='Broker host or url')
-  parser.add_argument('--timeout', type=int, default=0, help='Timeout in 
seconds, 0 for no timeout')
-  parser.add_argument('--format', type=str, default='table', choices=('csv', 
'tsv', 'json', 'table'), help='Result format')
-  parser.add_argument('--header', action='store_true', help='Include header 
row for formats "csv" and "tsv"')
-  parser.add_argument('--tsv-delimiter', type=str, default='\t', 
help='Delimiter for format "tsv"')
-  parser.add_argument('--context-option', '-c', type=str, action='append', 
help='Set context option for this connection')
-  parser.add_argument('--execute', '-e', type=str, help='Execute single SQL 
query')
-  parser.add_argument('--user', '-u', type=str, help='Username for HTTP basic 
auth')
-  parser.add_argument('--password', '-p', type=str, help='Password for HTTP 
basic auth')
-  parser.add_argument('--ignore-ssl-verification', '-k', action='store_true', 
default=False, help='Skip verification of SSL certificates.')
-  parser.add_argument('--cafile', type=str, help='Path to SSL CA file for 
validating server certificates. See load_verify_locations() in 
https://docs.python.org/2/library/ssl.html#ssl.SSLContext.')
-  parser.add_argument('--capath', type=str, help='SSL CA path for validating 
server certificates. See load_verify_locations() in 
https://docs.python.org/2/library/ssl.html#ssl.SSLContext.')
+  parser_cnn = parser.add_argument_group('Connection options')
+  parser_fmt = parser.add_argument_group('Formatting options')
+  parser_oth = parser.add_argument_group('Other options')
+  parser_cnn.add_argument('--host', '-H', type=str, 
default='http://localhost:8082/', help='Druid query host or url, like 
https://localhost:8282/')
+  parser_cnn.add_argument('--user', '-u', type=str, help='HTTP basic 
authentication credentials, like user:password')
+  parser_cnn.add_argument('--timeout', type=int, default=0, help='Timeout in 
seconds')
+  parser_cnn.add_argument('--cafile', type=str, help='Path to SSL CA file for 
validating server certificates. See load_verify_locations() in 
https://docs.python.org/2/library/ssl.html#ssl.SSLContext.')
+  parser_cnn.add_argument('--capath', type=str, help='SSL CA path for 
validating server certificates. See load_verify_locations() in 
https://docs.python.org/2/library/ssl.html#ssl.SSLContext.')
+  parser_cnn.add_argument('--ignore-ssl-verification', '-k', 
action='store_true', default=False, help='Skip verification of SSL 
certificates.')
+  parser_fmt.add_argument('--format', type=str, default='table', 
choices=('csv', 'tsv', 'json', 'table'), help='Result format')
+  parser_fmt.add_argument('--header', action='store_true', help='Include 
header row for formats "csv" and "tsv"')
+  parser_fmt.add_argument('--tsv-delimiter', type=str, default='\t', 
help='Delimiter for format "tsv"')
+  parser_oth.add_argument('--context-option', '-c', type=str, action='append', 
help='Set context option for this connection, see 
https://docs.imply.io/on-prem/query-data/sql for options')
+  parser_oth.add_argument('--execute', '-e', type=str, help='Execute single 
SQL query')
   args = parser.parse_args()
 
   # Build broker URL
@@ -381,7 +422,19 @@ def main():
   else:
     # interactive mode
     print("Welcome to dsql, the command-line client for Druid SQL.")
-    print("Type \"\h\" for help.")
+
+    readline_history_file = os.path.expanduser("~/.dsql_history")
+    readline.parse_and_bind('tab: complete')
+    readline.set_history_length(500)
+    readline.set_completer(make_readline_completer(url, context, args))
+
+    try:
+      readline.read_history_file(readline_history_file)
+    except IOError:
+      # IOError can happen if the file doesn't exist.
+      pass
+
+    print("Type \"\\h\" for help.")
 
     while True:
       sql = ''
@@ -400,7 +453,7 @@ def main():
             extra_info = dmatch.group(2)
             arg = dmatch.group(3).strip()
             if arg:
-              sql = "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE 
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = " + sql_escape(arg)
+              sql = "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE 
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = " + sql_literal_escape(arg)
               if not include_system:
                 sql = sql + " AND TABLE_SCHEMA = 'druid'"
               # break to execute sql
@@ -415,11 +468,11 @@ def main():
           hmatch = re.match(r'^\\h\s*$', more_sql)
           if hmatch:
             print("Commands:")
-            print("  \d             show tables")
-            print("  \dS            show tables, including system tables")
-            print("  \d table_name  describe table")
-            print("  \h             show this help")
-            print("  \q             exit this program")
+            print("  \\d             show tables")
+            print("  \\dS            show tables, including system tables")
+            print("  \\d table_name  describe table")
+            print("  \\h             show this help")
+            print("  \\q             exit this program")
             print("Or enter a SQL query ending with a semicolon (;).")
             continue
 
@@ -432,6 +485,7 @@ def main():
           sql = (sql + ' ' + more_sql).strip()
 
       try:
+        readline.write_history_file(readline_history_file)
         display_query(url, sql.rstrip(';'), context, args)
       except DruidSqlException as e:
         e.write_to(sys.stdout)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to