clickhouse-test 25.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
#!/usr/bin/env python
import sys
import os
import os.path
import re
import lxml.etree as et

from argparse import ArgumentParser
from argparse import FileType
from pprint import pprint
11
import shlex
12
import subprocess
13 14 15 16
from subprocess import check_call
from subprocess import Popen
from subprocess import PIPE
from subprocess import CalledProcessError
17 18 19
from datetime import datetime
from time import sleep
from errno import ESRCH
20
import termcolor
21
from random import random
P
proller 已提交
22
import commands
23

24

A
alesapin 已提交
25
MESSAGES_TO_RETRY = [
26
    "DB::Exception: ZooKeeper session has been expired",
A
alesapin 已提交
27 28 29
    "Coordination::Exception: Connection loss",
]

30

31 32 33 34 35 36 37 38
def remove_control_characters(s):
    """
    https://github.com/html5lib/html5lib-python/issues/96#issuecomment-43438438
    """
    def str_to_int(s, default, base=10):
        if int(s, base) < 0x10000:
            return unichr(int(s, base))
        return default
39 40 41
    s = re.sub(r"&#(\d+);?", lambda c: str_to_int(c.group(1), c.group(0)), s)
    s = re.sub(r"&#[xX]([0-9a-fA-F]+);?", lambda c: str_to_int(c.group(1), c.group(0), base=16), s)
    s = re.sub(r"[\x00-\x08\x0b\x0e-\x1f\x7f]", "", s)
42 43
    return s

A
alesapin 已提交
44 45
def run_single_test(args, ext, server_logs_level, case_file, stdout_file, stderr_file):
    if ext == '.sql':
46
        command = "{0} --send_logs_level={1} --testmode --multiquery < {2} > {3} 2> {4}".format(args.client_with_database, server_logs_level, case_file, stdout_file, stderr_file)
A
alesapin 已提交
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
    else:
        command = "{} > {} 2> {}".format(case_file, stdout_file, stderr_file)

    proc = Popen(command, shell = True)
    start_time = datetime.now()
    while (datetime.now() - start_time).total_seconds() < args.timeout and proc.poll() is None:
        sleep(0.01)

    stdout = open(stdout_file, 'r').read() if os.path.exists(stdout_file) else ''
    stdout = unicode(stdout, errors='replace', encoding='utf-8')
    stderr = open(stderr_file, 'r').read() if os.path.exists(stderr_file) else ''
    stderr = unicode(stderr, errors='replace', encoding='utf-8')

    return proc, stdout, stderr

def need_retry(stderr):
    return any(msg in stderr for msg in MESSAGES_TO_RETRY)

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
def get_processlist(client_cmd):
    try:
        return subprocess.check_output("{} --query 'SHOW PROCESSLIST FORMAT Vertical'".format(client_cmd), shell=True)
    except:
        return "" #  server seems dead

def get_stacktraces(server_pid):
    cmd = "gdb -q -ex 'set pagination off' -ex 'backtrace' -ex 'thread apply all backtrace' -ex 'detach' -ex 'quit' --pid {} 2>/dev/null".format(server_pid)
    try:
        return subprocess.check_output(cmd, shell=True)
    except Exception as ex:
        return "Error occured while receiving stack traces {}".format(str(ex))

def get_server_pid(server_tcp_port):
    cmd = "lsof -i tcp:{port} | grep '*:{port}'".format(port=server_tcp_port)
    try:
        output = subprocess.check_output(cmd, shell=True)
        if output:
            columns = output.strip().split(' ')
            return int(columns[1])
        else:
            return None # server dead
    except Exception as ex:
        return None

90 91
def main(args):

92 93
    SERVER_DIED = False

A
alesapin 已提交
94 95 96 97 98 99 100 101 102 103 104 105 106 107
    def colored(text, color=None, on_color=None, attrs=None):
           if sys.stdout.isatty() or args.force_color:
               return termcolor.colored(text, color, on_color, attrs)
           else:
               return text

    OP_SQUARE_BRACKET = colored("[", attrs=['bold'])
    CL_SQUARE_BRACKET = colored("]", attrs=['bold'])

    MSG_FAIL = OP_SQUARE_BRACKET + colored(" FAIL ", "red", attrs=['bold']) + CL_SQUARE_BRACKET
    MSG_UNKNOWN = OP_SQUARE_BRACKET + colored(" UNKNOWN ", "yellow", attrs=['bold']) + CL_SQUARE_BRACKET
    MSG_OK = OP_SQUARE_BRACKET + colored(" OK ", "green", attrs=['bold']) + CL_SQUARE_BRACKET
    MSG_SKIPPED = OP_SQUARE_BRACKET + colored(" SKIPPED ", "cyan", attrs=['bold']) + CL_SQUARE_BRACKET

108
    def is_data_present():
P
proller 已提交
109 110
        clickhouse_proc = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
        (stdout, stderr) = clickhouse_proc.communicate("EXISTS TABLE test.hits")
P
proller 已提交
111
        if clickhouse_proc.returncode != 0:
P
proller 已提交
112
            raise CalledProcessError(clickhouse_proc.returncode, args.client, stderr)
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

        return stdout.startswith('1')


    def dump_report(destination, suite, test_case, report):
        if destination is not None:
            destination_file = os.path.join(destination, suite, test_case + ".xml")
            destination_dir = os.path.dirname(destination_file)
            if not os.path.exists(destination_dir):
                os.makedirs(destination_dir)
            with open(destination_file, 'w') as report_file:
                report_root = et.Element("testsuites", attrib = {'name': 'ClickHouse Tests'})
                report_suite = et.Element("testsuite", attrib = {"name": suite})
                report_suite.append(report)
                report_root.append(report_suite)
                report_file.write(et.tostring(report_root, encoding = "UTF-8", xml_declaration=True, pretty_print=True))

P
proller 已提交
130 131 132
    base_dir = os.path.abspath(args.queries)
    tmp_dir = os.path.abspath(args.tmp)

133
    # Keep same default values as in queries/shell_config.sh
P
proller 已提交
134
    os.environ.setdefault("CLICKHOUSE_BINARY", args.binary)
P
proller 已提交
135
    #os.environ.setdefault("CLICKHOUSE_CLIENT", args.client)
136
    os.environ.setdefault("CLICKHOUSE_CONFIG", args.configserver)
P
proller 已提交
137 138
    if args.configclient:
        os.environ.setdefault("CLICKHOUSE_CONFIG_CLIENT", args.configclient)
P
proller 已提交
139
    os.environ.setdefault("CLICKHOUSE_TMP", tmp_dir)
140
    os.environ.setdefault("CLICKHOUSE_DATABASE", args.database)
141

142
    # Force to print server warnings in stderr
143 144
    # Shell scripts could change logging level
    server_logs_level = "warning"
145 146
    os.environ.setdefault("CLICKHOUSE_CLIENT_SERVER_LOGS_LEVEL", server_logs_level)

147
    if args.zookeeper is None:
P
proller 已提交
148
        code, out = commands.getstatusoutput(args.extract_from_config +" --try --config " + args.configserver + ' --key zookeeper | grep . | wc -l')
149 150 151 152 153 154
        try:
            if int(out) > 0:
                args.zookeeper = True
            else:
                args.zookeeper = False
        except ValueError:
155 156 157
            args.zookeeper = False

    if args.shard is None:
P
proller 已提交
158
        code, out = commands.getstatusoutput(args.extract_from_config + " --try --config " + args.configserver + ' --key listen_host | grep -E "127.0.0.2|::"')
P
proller 已提交
159
        if out:
P
proller 已提交
160
            args.shard = True
P
proller 已提交
161 162
        else:
            args.shard = False
163

164 165
    passed_total = 0
    skipped_total = 0
166 167
    failures_total = 0

P
proller 已提交
168
    clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
169
    clickhouse_proc_create.communicate("CREATE DATABASE IF NOT EXISTS " + args.database)
170 171 172
    if args.database != "test":
        clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
        clickhouse_proc_create.communicate("CREATE DATABASE IF NOT EXISTS test")
173 174 175 176 177

    def is_test_from_dir(suite_dir, case):
        case_file = os.path.join(suite_dir, case)
        (name, ext) = os.path.splitext(case)
        return os.path.isfile(case_file) and (ext == '.sql' or ext == '.sh' or ext == '.py')
P
proller 已提交
178

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
    def sute_key_func(item):
       if args.order == 'random':
             return random()

       if -1 == item.find('_'):
           return 99998

       prefix, suffix = item.split('_', 1)

       try:
           return int(prefix), suffix
       except ValueError:
           return 99997

    for suite in sorted(os.listdir(base_dir), key=sute_key_func):
194 195 196 197 198 199 200
        if SERVER_DIED:
            break

        suite_dir = os.path.join(base_dir, suite)
        suite_re_obj = re.search('^[0-9]+_(.*)$', suite)
        if not suite_re_obj: #skip .gitignore and so on
            continue
201 202 203 204 205

        suite_tmp_dir = os.path.join(tmp_dir, suite)
        if not os.path.exists(suite_tmp_dir):
            os.makedirs(suite_tmp_dir)

206 207 208 209
        suite = suite_re_obj.group(1)
        if os.path.isdir(suite_dir):

            failures = 0
210
            failures_chain = 0
211
            if 'stateful' in suite and not is_data_present():
212
                print("Won't run stateful tests because test data wasn't loaded.")
213
                continue
214 215 216
            if 'stateless' in suite and args.no_stateless:
                print("Won't run stateless tests because they were manually disabled.")
                continue
217 218 219
            if 'stateful' in suite and args.no_stateful:
                print("Won't run stateful tests because they were manually disabled.")
                continue
220

P
proller 已提交
221 222 223
            # Reverse sort order: we want run newest test first.
            # And not reverse subtests
            def key_func(item):
224
                if args.order == 'random':
225
                    return random()
226 227 228 229 230 231

                reverse = 1 if args.order == 'asc' else -1

                if -1 == item.find('_'):
                    return 99998

P
proller 已提交
232
                prefix, suffix = item.split('_', 1)
233 234 235 236 237 238

                try:
                    return reverse * int(prefix), suffix
                except ValueError:
                    return 99997

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
            run_n, run_total = args.parallel.split('/')
            run_n = float(run_n)
            run_total = float(run_total)
            all_tests = os.listdir(suite_dir)
            all_tests = filter(lambda case: is_test_from_dir(suite_dir, case), all_tests)
            all_tests = sorted(filter(lambda case: re.search(args.test, case) if args.test else True, all_tests), key=key_func)
            tests_n = len(all_tests)
            start = int(tests_n / run_total * (run_n - 1))
            if start > 0:
                start = start + 1
            end = int(tests_n / run_total * (run_n))
            all_tests = all_tests[start : end]

            print("\nRunning {} {} tests.".format(tests_n, suite) + (" {} .. {} ".format(start, end) if run_total > 1 else "") + "\n")

            for case in all_tests:
255 256 257 258
                if SERVER_DIED:
                    break

                case_file = os.path.join(suite_dir, case)
259 260
                (name, ext) = os.path.splitext(case)

261 262 263
                report_testcase = et.Element("testcase", attrib = {"name": name})

                try:
264 265 266
                    sys.stdout.write("{0:72}".format(name + ": "))
                    if run_total == 1:
                        sys.stdout.flush()
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290

                    if args.skip and any(s in name for s in args.skip):
                        report_testcase.append(et.Element("skipped", attrib = {"message": "skip"}))
                        print(MSG_SKIPPED + " - skip")
                        skipped_total += 1
                    elif not args.zookeeper and 'zookeeper' in name:
                        report_testcase.append(et.Element("skipped", attrib = {"message": "no zookeeper"}))
                        print(MSG_SKIPPED + " - no zookeeper")
                        skipped_total += 1
                    elif not args.shard and 'shard' in name:
                        report_testcase.append(et.Element("skipped", attrib = {"message": "no shard"}))
                        print(MSG_SKIPPED + " - no shard")
                        skipped_total += 1
                    elif not args.no_long and 'long' in name:
                        report_testcase.append(et.Element("skipped", attrib = {"message": "no long"}))
                        print(MSG_SKIPPED + " - no long")
                        skipped_total += 1
                    else:
                        disabled_file = os.path.join(suite_dir, name) + '.disabled'

                        if os.path.exists(disabled_file) and not args.disabled:
                            message = open(disabled_file, 'r').read()
                            report_testcase.append(et.Element("skipped", attrib = {"message": message}))
                            print(MSG_SKIPPED + " - " + message)
291
                        else:
292

293 294 295
                            if args.testname:
                                clickhouse_proc = Popen(shlex.split(args.client_with_database), stdin=PIPE, stdout=PIPE, stderr=PIPE)
                                clickhouse_proc.communicate("SELECT 'Running test {suite}/{case} from pid={pid}';".format(pid = os.getpid(), case = case, suite = suite))
P
proller 已提交
296

297 298 299
                            reference_file = os.path.join(suite_dir, name) + '.reference'
                            stdout_file = os.path.join(suite_tmp_dir, name) + '.stdout'
                            stderr_file = os.path.join(suite_tmp_dir, name) + '.stderr'
P
proller 已提交
300

301 302 303 304 305 306 307
                            proc, stdout, stderr = run_single_test(args, ext, server_logs_level, case_file, stdout_file, stderr_file)
                            if proc.returncode is None:
                                try:
                                    proc.kill()
                                except OSError as e:
                                    if e.errno != ESRCH:
                                        raise
A
Alexey Milovidov 已提交
308

309 310
                                failure = et.Element("failure", attrib = {"message": "Timeout"})
                                report_testcase.append(failure)
A
Alexey Milovidov 已提交
311

312 313 314 315 316 317 318 319 320 321 322 323 324
                                failures += 1
                                print("{0} - Timeout!".format(MSG_FAIL))
                            else:
                                counter = 1
                                while proc.returncode != 0 and need_retry(stderr):
                                    proc, stdout, stderr = run_single_test(args, ext, server_logs_level, case_file, stdout_file, stderr_file)
                                    sleep(2**counter)
                                    counter += 1
                                    if counter > 6:
                                        break

                                if proc.returncode != 0:
                                    failure = et.Element("failure", attrib = {"message": "return code {}".format(proc.returncode)})
E
Evgeny Konkov 已提交
325
                                    report_testcase.append(failure)
A
Alexey Milovidov 已提交
326

327 328 329 330
                                    stdout_element = et.Element("system-out")
                                    stdout_element.text = et.CDATA(stdout)
                                    report_testcase.append(stdout_element)

331
                                    failures += 1
332 333
                                    failures_chain += 1
                                    print("{0} - return code {1}".format(MSG_FAIL, proc.returncode))
A
Alexey Milovidov 已提交
334

335 336 337 338 339
                                    if stderr:
                                        stderr_element = et.Element("system-err")
                                        stderr_element.text = et.CDATA(stderr)
                                        report_testcase.append(stderr_element)
                                        print(stderr.encode('utf-8'))
A
Alexey Milovidov 已提交
340

341 342 343 344 345 346
                                    if args.stop and ('Connection refused' in stderr or 'Attempt to read after eof' in stderr) and not 'Received exception from server' in stderr:
                                        SERVER_DIED = True

                                elif stderr:
                                    failure = et.Element("failure", attrib = {"message": "having stderror"})
                                    report_testcase.append(failure)
A
Alexey Milovidov 已提交
347

348 349 350
                                    stderr_element = et.Element("system-err")
                                    stderr_element.text = et.CDATA(stderr)
                                    report_testcase.append(stderr_element)
A
Alexey Milovidov 已提交
351

352 353 354 355 356 357
                                    failures += 1
                                    failures_chain += 1
                                    print("{0} - having stderror:\n{1}".format(MSG_FAIL, stderr.encode('utf-8')))
                                elif 'Exception' in stdout:
                                    failure = et.Element("error", attrib = {"message": "having exception"})
                                    report_testcase.append(failure)
A
Alexey Milovidov 已提交
358

359 360 361
                                    stdout_element = et.Element("system-out")
                                    stdout_element.text = et.CDATA(stdout)
                                    report_testcase.append(stdout_element)
A
Alexey Milovidov 已提交
362

363 364 365 366 367 368 369 370 371
                                    failures += 1
                                    failures_chain += 1
                                    print("{0} - having exception:\n{1}".format(MSG_FAIL, stdout.encode('utf-8')))
                                elif not os.path.isfile(reference_file):
                                    skipped = et.Element("skipped", attrib = {"message": "no reference file"})
                                    report_testcase.append(skipped)
                                    print("{0} - no reference file".format(MSG_UNKNOWN))
                                else:
                                    result_is_different = subprocess.call(['diff', '-q', reference_file, stdout_file], stdout = PIPE)
A
Alexey Milovidov 已提交
372

373 374 375 376 377 378
                                    if result_is_different:
                                        diff = Popen(['diff', '--unified', reference_file, stdout_file], stdout = PIPE).communicate()[0]
                                        diff = unicode(diff, errors='replace', encoding='utf-8')
                                        cat = Popen(['cat', '-vet'], stdin=PIPE, stdout=PIPE).communicate(input=diff.encode(encoding='utf-8', errors='replace'))[0]

                                        failure = et.Element("failure", attrib = {"message": "result differs with reference"})
E
Evgeny Konkov 已提交
379
                                        report_testcase.append(failure)
A
Alexey Milovidov 已提交
380

E
Evgeny Konkov 已提交
381
                                        stdout_element = et.Element("system-out")
382 383 384 385
                                        try:
                                            stdout_element.text = et.CDATA(diff)
                                        except:
                                            stdout_element.text = et.CDATA(remove_control_characters(diff))
386

387
                                        report_testcase.append(stdout_element)
388
                                        failures += 1
389
                                        print("{0} - result differs with reference:\n{1}".format(MSG_FAIL, cat.encode('utf-8')))
E
Evgeny Konkov 已提交
390
                                    else:
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
                                        passed_total += 1
                                        failures_chain = 0
                                        print(MSG_OK)
                                        if os.path.exists(stdout_file):
                                            os.remove(stdout_file)
                                        if os.path.exists(stderr_file):
                                            os.remove(stderr_file)
                except KeyboardInterrupt as e:
                    print(colored("Break tests execution", "red"))
                    raise e
                except:
                    import traceback
                    exc_type, exc_value, tb = sys.exc_info()
                    error = et.Element("error", attrib = {"type": exc_type.__name__, "message": str(exc_value)})
                    report_testcase.append(error)

                    failures += 1
                    print("{0} - Test internal error: {1}\n{2}\n{3}".format(MSG_FAIL, exc_type.__name__, exc_value, "\n".join(traceback.format_tb(tb, 10))))
                finally:
                    dump_report(args.output, suite, name, report_testcase)

                if failures_chain >= 20:
                    break
414

415 416
            failures_total = failures_total + failures

417
    exit_code = 0
418
    if failures_total > 0:
419
        print(colored("\nHaving {failures_total} errors! {passed_total} tests passed. {skipped_total} tests skipped.".format(passed_total = passed_total, skipped_total = skipped_total, failures_total = failures_total), "red", attrs=["bold"]))
420
        exit_code = 1
421
    else:
422
        print(colored("\n{passed_total} tests passed. {skipped_total} tests skipped.".format(passed_total = passed_total, skipped_total = skipped_total), "green", attrs=["bold"]))
423 424

    if args.hung_check:
425
        processlist = get_processlist(args.client_with_database)
426 427 428 429 430 431 432 433
        if processlist:
            server_pid = get_server_pid(os.getenv("CLICKHOUSE_PORT_TCP", '9000'))
            print(colored("\nFound hung queries in processlist:", "red", attrs=["bold"]))
            print(processlist)
            if server_pid:
                print("\nStacktraces of all threads:")
                print(get_stacktraces(server_pid))
            exit_code = 1
434 435
        else:
            print(colored("\nNo queries hung.", "green", attrs=["bold"]))
436 437 438

    sys.exit(exit_code)

439

A
alesapin 已提交
440
def find_binary(name):
441 442
    if os.path.exists(name) and os.access(name, os.X_OK):
        return True
A
alesapin 已提交
443 444 445 446 447 448 449
    paths = os.environ.get("PATH").split(':')
    for path in paths:
        if os.access(os.path.join(path, name), os.X_OK):
            return True

    # maybe it wasn't in PATH
    return os.access(os.path.join('/usr/bin', name), os.X_OK)
450 451

if __name__ == '__main__':
452 453 454
    parser=ArgumentParser(description='ClickHouse functional tests')
    parser.add_argument('-q', '--queries', help='Path to queries dir')
    parser.add_argument('--tmp', help='Path to tmp dir')
455
    parser.add_argument('-b', '--binary', default='clickhouse', help='Path to clickhouse binary or name of binary in PATH')
456 457 458 459 460 461 462 463 464 465 466 467
    parser.add_argument('-c', '--client', help='Client program')
    parser.add_argument('--extract_from_config', help='extract-from-config program')
    parser.add_argument('--configclient', help='Client config (if you use not default ports)')
    parser.add_argument('--configserver', default= '/etc/clickhouse-server/config.xml', help='Preprocessed server config')
    parser.add_argument('-o', '--output', help='Output xUnit compliant test report directory')
    parser.add_argument('-t', '--timeout', type=int, default=600, help='Timeout for each test case in seconds')
    parser.add_argument('test', nargs='?', help='Optional test case name regex')
    parser.add_argument('-d', '--disabled', action='store_true', default=False, help='Also run disabled tests')
    parser.add_argument('--stop', action='store_true', default=None, dest='stop', help='Stop on network errors')
    parser.add_argument('--order', default='desc', help='Run order (asc, desc, random)')
    parser.add_argument('--testname', action='store_true', default=None, dest='testname', help='Make query with test name before test run')
    parser.add_argument('--hung-check', action='store_true', default=False)
A
alesapin 已提交
468
    parser.add_argument('--force-color', action='store_true', default=False)
469 470
    parser.add_argument('--database', default='test', help='Default database for tests')
    parser.add_argument('--parallel', default='1/1', help='Parralel test run number/total')
471 472

    parser.add_argument('--no-stateless', action='store_true', help='Disable all stateless tests')
473
    parser.add_argument('--no-stateful', action='store_true', help='Disable all stateful tests')
474 475 476 477 478 479 480 481
    parser.add_argument('--skip', nargs='+', help="Skip these tests")
    parser.add_argument('--no-long', action='store_false', dest='no_long', help='Do not run long tests')
    group=parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--zookeeper', action='store_true', default=None, dest='zookeeper', help='Run zookeeper related tests')
    group.add_argument('--no-zookeeper', action='store_false', default=None, dest='zookeeper', help='Do not run zookeeper related tests')
    group=parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--shard', action='store_true', default=None, dest='shard', help='Run sharding related tests (required to clickhouse-server listen 127.0.0.2 127.0.0.3)')
    group.add_argument('--no-shard', action='store_false', default=None, dest='shard', help='Do not run shard related tests')
482 483

    args = parser.parse_args()
484 485 486

    if args.queries is None and os.path.isdir('queries'):
        args.queries = 'queries'
P
proller 已提交
487
    elif args.queries is None:
P
proller 已提交
488 489 490 491
        if (os.path.isdir('/usr/local/share/clickhouse-test/queries')):
            args.queries = '/usr/local/share/clickhouse-test/queries'
        if (args.queries is None and os.path.isdir('/usr/share/clickhouse-test/queries')):
            args.queries = '/usr/share/clickhouse-test/queries'
492 493
        if args.tmp is None:
            args.tmp = '/tmp/clickhouse-test'
P
proller 已提交
494 495
    if args.tmp is None:
        args.tmp = args.queries
496
    if args.client is None:
A
alesapin 已提交
497
        if find_binary(args.binary + '-client'):
P
proller 已提交
498
            args.client = args.binary + '-client'
A
alesapin 已提交
499
        elif find_binary(args.binary):
P
proller 已提交
500
            args.client = args.binary + ' client'
501
        else:
A
alesapin 已提交
502 503 504 505
            print("No 'clickhouse' binary found in PATH")
            parser.print_help()
            exit(1)

P
proller 已提交
506
        if args.configclient:
507 508 509 510 511
            args.client += ' --config-file=' + args.configclient
        if os.getenv("CLICKHOUSE_HOST"):
            args.client += ' --host=' + os.getenv("CLICKHOUSE_HOST")
        if os.getenv("CLICKHOUSE_PORT_TCP"):
            args.client += ' --port=' + os.getenv("CLICKHOUSE_PORT_TCP")
512 513 514 515 516 517
        if os.getenv("CLICKHOUSE_DATABASE"):
            args.client += ' --database=' + os.getenv("CLICKHOUSE_DATABASE")

    args.client_with_database = args.client
    if args.database:
        args.client_with_database += ' --database=' + args.database
518

P
proller 已提交
519 520 521 522 523 524
    if args.extract_from_config is None:
        if os.access(args.binary + '-extract-from-config', os.X_OK):
            args.extract_from_config = args.binary + '-extract-from-config'
        else:
            args.extract_from_config = args.binary + ' extract-from-config'

525
    main(args)