kaqing 2.0.145__py3-none-any.whl → 2.0.189__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kaqing might be problematic. Click here for more details.
- adam/__init__.py +0 -2
- adam/app_session.py +9 -12
- adam/apps.py +18 -4
- adam/batch.py +4 -4
- adam/checks/check_utils.py +16 -46
- adam/checks/cpu.py +7 -1
- adam/checks/cpu_metrics.py +52 -0
- adam/checks/disk.py +2 -3
- adam/columns/columns.py +3 -1
- adam/columns/cpu.py +3 -1
- adam/columns/cpu_metrics.py +22 -0
- adam/columns/memory.py +3 -4
- adam/commands/__init__.py +24 -0
- adam/commands/alter_tables.py +33 -48
- adam/commands/app/__init__.py +0 -0
- adam/commands/app/app.py +38 -0
- adam/commands/{app_ping.py → app/app_ping.py} +7 -13
- adam/commands/app/show_app_actions.py +49 -0
- adam/commands/{show → app}/show_app_id.py +8 -11
- adam/commands/{show → app}/show_app_queues.py +7 -14
- adam/commands/app/utils_app.py +106 -0
- adam/commands/audit/audit.py +21 -40
- adam/commands/audit/audit_repair_tables.py +14 -19
- adam/commands/audit/audit_run.py +14 -22
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +4 -19
- adam/commands/audit/show_slow10.py +4 -18
- adam/commands/audit/show_top10.py +4 -16
- adam/commands/audit/utils_show_top10.py +15 -3
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +7 -104
- adam/commands/bash/utils_bash.py +16 -0
- adam/commands/cat.py +7 -27
- adam/commands/cd.py +7 -11
- adam/commands/check.py +15 -24
- adam/commands/cli_commands.py +8 -4
- adam/commands/clipboard_copy.py +87 -0
- adam/commands/code.py +21 -24
- adam/commands/command.py +207 -42
- adam/commands/commands_utils.py +25 -27
- adam/commands/cql/completions_c.py +28 -0
- adam/commands/cql/cqlsh.py +9 -33
- adam/commands/cql/{cql_utils.py → utils_cql.py} +111 -15
- adam/commands/deploy/code_start.py +7 -10
- adam/commands/deploy/code_stop.py +4 -21
- adam/commands/deploy/code_utils.py +3 -3
- adam/commands/deploy/deploy.py +4 -27
- adam/commands/deploy/deploy_frontend.py +14 -17
- adam/commands/deploy/deploy_pg_agent.py +3 -6
- adam/commands/deploy/deploy_pod.py +64 -68
- adam/commands/deploy/undeploy.py +4 -27
- adam/commands/deploy/undeploy_frontend.py +4 -7
- adam/commands/deploy/undeploy_pg_agent.py +5 -8
- adam/commands/deploy/undeploy_pod.py +9 -12
- adam/commands/devices/device.py +124 -2
- adam/commands/devices/device_app.py +41 -24
- adam/commands/devices/device_auit_log.py +10 -4
- adam/commands/devices/device_cass.py +48 -14
- adam/commands/devices/device_export.py +13 -12
- adam/commands/devices/device_postgres.py +105 -54
- adam/commands/download_file.py +47 -0
- adam/commands/exit.py +1 -4
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +9 -10
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/drop_export_database.py +7 -26
- adam/commands/export/drop_export_databases.py +5 -14
- adam/commands/export/export.py +6 -52
- adam/commands/export/export_databases.py +108 -32
- adam/commands/export/export_select.py +8 -59
- adam/commands/export/export_sessions.py +209 -0
- adam/commands/export/export_use.py +14 -20
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +135 -167
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +11 -35
- adam/commands/export/importer.py +19 -5
- adam/commands/export/importer_athena.py +112 -44
- adam/commands/export/importer_sqlite.py +42 -22
- adam/commands/export/show_column_counts.py +13 -31
- adam/commands/export/show_export_databases.py +7 -7
- adam/commands/export/show_export_session.py +8 -20
- adam/commands/export/show_export_sessions.py +6 -16
- adam/commands/export/utils_export.py +64 -11
- adam/commands/find_files.py +51 -0
- adam/commands/find_processes.py +76 -0
- adam/commands/head.py +36 -0
- adam/commands/help.py +2 -2
- adam/commands/intermediate_command.py +52 -0
- adam/commands/issues.py +11 -43
- adam/commands/kubectl.py +3 -6
- adam/commands/login.py +22 -24
- adam/commands/logs.py +3 -6
- adam/commands/ls.py +9 -10
- adam/commands/medusa/medusa.py +4 -22
- adam/commands/medusa/medusa_backup.py +20 -27
- adam/commands/medusa/medusa_restore.py +49 -46
- adam/commands/medusa/medusa_show_backupjobs.py +16 -18
- adam/commands/medusa/medusa_show_restorejobs.py +13 -18
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +7 -21
- adam/commands/param_get.py +11 -14
- adam/commands/param_set.py +8 -12
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +34 -57
- adam/commands/postgres/postgres_databases.py +270 -0
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/utils_postgres.py +79 -0
- adam/commands/preview_table.py +8 -45
- adam/commands/pwd.py +13 -16
- adam/commands/reaper/reaper.py +4 -27
- adam/commands/reaper/reaper_forward.py +49 -56
- adam/commands/reaper/reaper_forward_session.py +6 -0
- adam/commands/reaper/reaper_forward_stop.py +10 -16
- adam/commands/reaper/reaper_restart.py +7 -14
- adam/commands/reaper/reaper_run_abort.py +8 -33
- adam/commands/reaper/reaper_runs.py +43 -58
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +14 -33
- adam/commands/reaper/reaper_schedule_start.py +9 -33
- adam/commands/reaper/reaper_schedule_stop.py +9 -33
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +203 -0
- adam/commands/repair/repair.py +4 -22
- adam/commands/repair/repair_log.py +5 -11
- adam/commands/repair/repair_run.py +27 -34
- adam/commands/repair/repair_scan.py +32 -40
- adam/commands/repair/repair_stop.py +5 -12
- adam/commands/report.py +27 -29
- adam/commands/restart.py +25 -26
- adam/commands/rollout.py +19 -24
- adam/commands/shell.py +12 -4
- adam/commands/show/show.py +11 -27
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +37 -0
- adam/commands/show/show_cassandra_status.py +47 -51
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_cli_commands.py +56 -0
- adam/commands/show/show_host.py +1 -1
- adam/commands/show/show_login.py +20 -27
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +18 -21
- adam/commands/show/show_storage.py +11 -20
- adam/commands/watch.py +26 -29
- adam/config.py +5 -16
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/pod_exec_result.py +3 -3
- adam/repl.py +45 -39
- adam/repl_commands.py +26 -19
- adam/repl_session.py +8 -1
- adam/repl_state.py +85 -36
- adam/sql/lark_completer.py +284 -0
- adam/sql/lark_parser.py +604 -0
- adam/sql/sql_completer.py +4 -6
- adam/sql/sql_state_machine.py +29 -16
- adam/sso/authn_ad.py +6 -8
- adam/sso/authn_okta.py +4 -6
- adam/sso/cred_cache.py +3 -5
- adam/sso/idp.py +9 -12
- adam/utils.py +484 -37
- adam/utils_athena.py +19 -19
- adam/utils_audits.py +12 -12
- adam/utils_issues.py +32 -0
- adam/utils_k8s/app_clusters.py +14 -19
- adam/utils_k8s/app_pods.py +7 -2
- adam/utils_k8s/cassandra_clusters.py +30 -19
- adam/utils_k8s/cassandra_nodes.py +2 -2
- adam/utils_k8s/custom_resources.py +16 -17
- adam/utils_k8s/ingresses.py +2 -2
- adam/utils_k8s/jobs.py +7 -11
- adam/utils_k8s/k8s.py +96 -0
- adam/utils_k8s/kube_context.py +2 -2
- adam/utils_k8s/pods.py +37 -81
- adam/utils_k8s/secrets.py +4 -4
- adam/utils_k8s/service_accounts.py +5 -4
- adam/utils_k8s/services.py +2 -2
- adam/utils_k8s/statefulsets.py +6 -14
- adam/utils_local.py +4 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/repl_completer.py +128 -2
- adam/utils_repl/state_machine.py +3 -3
- adam/utils_sqlite.py +78 -42
- adam/version.py +1 -1
- {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
- kaqing-2.0.189.dist-info/RECORD +253 -0
- kaqing-2.0.189.dist-info/top_level.txt +2 -0
- teddy/__init__.py +0 -0
- teddy/lark_parser.py +436 -0
- teddy/lark_parser2.py +618 -0
- adam/commands/app.py +0 -67
- adam/commands/cp.py +0 -95
- adam/commands/cql/cql_completions.py +0 -28
- adam/commands/export/clean_up_export_session.py +0 -53
- adam/commands/export/export_select_x.py +0 -54
- adam/commands/postgres/postgres_context.py +0 -248
- adam/commands/postgres/postgres_utils.py +0 -31
- adam/commands/postgres/psql_completions.py +0 -10
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/show/show_app_actions.py +0 -56
- adam/commands/show/show_commands.py +0 -61
- adam/commands/show/show_repairs.py +0 -47
- kaqing-2.0.145.dist-info/RECORD +0 -227
- kaqing-2.0.145.dist-info/top_level.txt +0 -1
- {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
- {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
adam/repl_state.py
CHANGED
|
@@ -3,7 +3,6 @@ from enum import Enum
|
|
|
3
3
|
import re
|
|
4
4
|
from typing import Callable
|
|
5
5
|
|
|
6
|
-
from adam.commands.postgres.postgres_context import PostgresContext
|
|
7
6
|
from adam.utils_k8s.app_clusters import AppClusters
|
|
8
7
|
from adam.utils_k8s.app_pods import AppPods
|
|
9
8
|
from adam.utils_k8s.cassandra_clusters import CassandraClusters
|
|
@@ -20,31 +19,8 @@ class BashSession:
|
|
|
20
19
|
def pwd(self, state: 'ReplState'):
|
|
21
20
|
command = f'cat /tmp/.qing-{self.session_id}'
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
rs = [AppPods.exec(state.app_pod, state.namespace, command, show_out=False)]
|
|
26
|
-
else:
|
|
27
|
-
pods = AppPods.pod_names(state.namespace, state.app_env, state.app_pod)
|
|
28
|
-
rs = AppClusters.exec(pods, state.namespace, command, show_out=False)
|
|
29
|
-
elif state.pod:
|
|
30
|
-
rs = [CassandraNodes.exec(state.pod, state.namespace, command, show_out=False)]
|
|
31
|
-
elif state.sts:
|
|
32
|
-
rs = CassandraClusters.exec(state.sts, state.namespace, command, action='bash', show_out=False)
|
|
33
|
-
|
|
34
|
-
dir = None
|
|
35
|
-
for r in rs:
|
|
36
|
-
if r.exit_code(): # if fails to read the session file, ignore
|
|
37
|
-
continue
|
|
38
|
-
|
|
39
|
-
dir0 = r.stdout.strip(' \r\n')
|
|
40
|
-
if dir:
|
|
41
|
-
if dir != dir0:
|
|
42
|
-
log2('Inconsitent working dir found across multiple pods.')
|
|
43
|
-
return None
|
|
44
|
-
else:
|
|
45
|
-
dir = dir0
|
|
46
|
-
|
|
47
|
-
return dir
|
|
22
|
+
with device(state) as pods:
|
|
23
|
+
return pods.exec(command, action='bash', show_out=False)
|
|
48
24
|
|
|
49
25
|
class RequiredState(Enum):
|
|
50
26
|
CLUSTER = 'cluster'
|
|
@@ -85,6 +61,7 @@ class ReplState:
|
|
|
85
61
|
self.bash_session = bash_session
|
|
86
62
|
self.remote_dir = remote_dir
|
|
87
63
|
self.original_state: ReplState = None
|
|
64
|
+
self.pod_targetted = False
|
|
88
65
|
|
|
89
66
|
self.export_session: str = None
|
|
90
67
|
|
|
@@ -105,11 +82,11 @@ class ReplState:
|
|
|
105
82
|
msg = ''
|
|
106
83
|
if self.device == ReplState.P:
|
|
107
84
|
msg = f'{ReplState.P}:'
|
|
108
|
-
|
|
109
|
-
if
|
|
110
|
-
msg +=
|
|
111
|
-
elif
|
|
112
|
-
msg +=
|
|
85
|
+
host, database = self.pg_host_n_database()
|
|
86
|
+
if database:
|
|
87
|
+
msg += database
|
|
88
|
+
elif host:
|
|
89
|
+
msg += host
|
|
113
90
|
elif self.device == ReplState.A:
|
|
114
91
|
msg = f'{ReplState.A}:'
|
|
115
92
|
if self.app_env:
|
|
@@ -337,8 +314,8 @@ class ReplState:
|
|
|
337
314
|
if self.device != ReplState.P:
|
|
338
315
|
return (False, None)
|
|
339
316
|
|
|
340
|
-
|
|
341
|
-
if not
|
|
317
|
+
_, database = self.pg_host_n_database()
|
|
318
|
+
if not database:
|
|
342
319
|
def error():
|
|
343
320
|
if self.in_repl:
|
|
344
321
|
log2('cd to a database first.')
|
|
@@ -369,7 +346,10 @@ class ReplState:
|
|
|
369
346
|
if not self.export_session:
|
|
370
347
|
def error():
|
|
371
348
|
if self.in_repl:
|
|
372
|
-
|
|
349
|
+
if self.device == ReplState.C:
|
|
350
|
+
log2("Select an export database first with 'use' command.")
|
|
351
|
+
else:
|
|
352
|
+
log2('cd to an export database first.')
|
|
373
353
|
else:
|
|
374
354
|
log2('* export database is missing.')
|
|
375
355
|
log2()
|
|
@@ -413,9 +393,10 @@ class ReplState:
|
|
|
413
393
|
self.pop()
|
|
414
394
|
self.bash_session = None
|
|
415
395
|
|
|
416
|
-
def push(self):
|
|
396
|
+
def push(self, pod_targetted=False):
|
|
417
397
|
if not self.original_state:
|
|
418
398
|
self.original_state = copy(self)
|
|
399
|
+
self.pod_targetted = pod_targetted
|
|
419
400
|
|
|
420
401
|
def pop(self):
|
|
421
402
|
if o := self.original_state:
|
|
@@ -429,4 +410,72 @@ class ReplState:
|
|
|
429
410
|
# self.export_session = o.export_session
|
|
430
411
|
self.namespace = o.namespace
|
|
431
412
|
|
|
432
|
-
self.original_state = None
|
|
413
|
+
self.original_state = None
|
|
414
|
+
self.pod_targetted = False
|
|
415
|
+
|
|
416
|
+
def pg_host_n_database(self):
|
|
417
|
+
host = None
|
|
418
|
+
database = None
|
|
419
|
+
|
|
420
|
+
if self.pg_path:
|
|
421
|
+
host_n_db = self.pg_path.split('/')
|
|
422
|
+
host = host_n_db[0]
|
|
423
|
+
if len(host_n_db) > 1:
|
|
424
|
+
database = host_n_db[1]
|
|
425
|
+
|
|
426
|
+
return host, database
|
|
427
|
+
|
|
428
|
+
def with_no_pod(self):
|
|
429
|
+
state1 = copy(self)
|
|
430
|
+
state1.pod = None
|
|
431
|
+
|
|
432
|
+
return state1
|
|
433
|
+
|
|
434
|
+
class DevicePodService:
|
|
435
|
+
def __init__(self, handler: 'DeviceExecHandler'):
|
|
436
|
+
self.handler = handler
|
|
437
|
+
|
|
438
|
+
def exec(self, command: str, action='bash', show_out = True):
|
|
439
|
+
state = self.handler.state
|
|
440
|
+
|
|
441
|
+
rs = None
|
|
442
|
+
if state.device == ReplState.A and state.app_app:
|
|
443
|
+
if state.app_pod:
|
|
444
|
+
rs = [AppPods.exec(state.app_pod, state.namespace, command, show_out=show_out)]
|
|
445
|
+
else:
|
|
446
|
+
pods = AppPods.pod_names(state.namespace, state.app_env, state.app_app)
|
|
447
|
+
rs = AppClusters.exec(pods, state.namespace, command, show_out=show_out)
|
|
448
|
+
elif state.pod:
|
|
449
|
+
rs = [CassandraNodes.exec(state.pod, state.namespace, command, show_out=show_out)]
|
|
450
|
+
elif state.sts:
|
|
451
|
+
rs = CassandraClusters.exec(state.sts, state.namespace, command, action=action, show_out=show_out)
|
|
452
|
+
# assume that pg-agent or ops pod is single pod
|
|
453
|
+
|
|
454
|
+
dir = None
|
|
455
|
+
if rs:
|
|
456
|
+
for r in rs:
|
|
457
|
+
if r.exit_code(): # if fails to read the session file, ignore
|
|
458
|
+
continue
|
|
459
|
+
|
|
460
|
+
dir0 = r.stdout.strip(' \r\n')
|
|
461
|
+
if dir:
|
|
462
|
+
if dir != dir0:
|
|
463
|
+
log2('Inconsitent working dir found across multiple pods.')
|
|
464
|
+
return None
|
|
465
|
+
else:
|
|
466
|
+
dir = dir0
|
|
467
|
+
|
|
468
|
+
return dir
|
|
469
|
+
|
|
470
|
+
class DeviceExecHandler:
|
|
471
|
+
def __init__(self, state: ReplState):
|
|
472
|
+
self.state = state
|
|
473
|
+
|
|
474
|
+
def __enter__(self):
|
|
475
|
+
return DevicePodService(self)
|
|
476
|
+
|
|
477
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
478
|
+
return False
|
|
479
|
+
|
|
480
|
+
def device(state: ReplState):
|
|
481
|
+
return DeviceExecHandler(state)
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import os
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
from prompt_toolkit.completion import CompleteEvent, Completer, Completion, NestedCompleter, WordCompleter
|
|
5
|
+
from prompt_toolkit.document import Document
|
|
6
|
+
|
|
7
|
+
from adam.config import Config
|
|
8
|
+
from adam.sql.lark_parser import LarkParser
|
|
9
|
+
from adam.utils import debug, log_timing, offload
|
|
10
|
+
from adam.utils_repl.appendable_completer import AppendableCompleter
|
|
11
|
+
from adam.utils_repl.repl_completer import merge_completions, preload
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"LarkCompleter",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def default_columns(x: list[str]):
|
|
18
|
+
return 'id,x.,y.,z.'.split(',')
|
|
19
|
+
|
|
20
|
+
class LarkCompleter(Completer, AppendableCompleter):
|
|
21
|
+
SYSTEM = 'system'
|
|
22
|
+
|
|
23
|
+
def __init__(self,
|
|
24
|
+
dml: str = None,
|
|
25
|
+
expandables: dict = {},
|
|
26
|
+
variant: str = 'c',
|
|
27
|
+
|
|
28
|
+
name: str = None,
|
|
29
|
+
options_lambda: callable = None,
|
|
30
|
+
auto: str = 'lazy',
|
|
31
|
+
debug = False
|
|
32
|
+
) -> None:
|
|
33
|
+
self.nested: NestedCompleter = None
|
|
34
|
+
self.options_lambda = options_lambda
|
|
35
|
+
if options_lambda and auto == 'lazy':
|
|
36
|
+
preload(options_lambda, log_key=name)
|
|
37
|
+
|
|
38
|
+
self.variant = variant
|
|
39
|
+
self.parser = None
|
|
40
|
+
self.dml = dml
|
|
41
|
+
self.expandables = expandables
|
|
42
|
+
|
|
43
|
+
self.display_dict = {}
|
|
44
|
+
self.meta_dict = {}
|
|
45
|
+
self.WORD = None
|
|
46
|
+
self.sentence = False
|
|
47
|
+
self.match_middle = False
|
|
48
|
+
self.pattern = None
|
|
49
|
+
|
|
50
|
+
self.debug = debug
|
|
51
|
+
|
|
52
|
+
if variant:
|
|
53
|
+
self.parser = LarkCompleter.lark_parser(variant)
|
|
54
|
+
self.preload_lazy_auto_completes()
|
|
55
|
+
|
|
56
|
+
def __repr__(self):
|
|
57
|
+
return f"LarkCompleter.{self.variant}"
|
|
58
|
+
|
|
59
|
+
def preload_lazy_auto_completes(self):
|
|
60
|
+
for key, value in self.expandables.items():
|
|
61
|
+
if callable(value):
|
|
62
|
+
if self.auto_complete(key) == 'lazy':
|
|
63
|
+
preload(value, log_key=key)
|
|
64
|
+
|
|
65
|
+
def from_lambda(name: str, options_lambda: callable, auto: str = 'lazy'):
|
|
66
|
+
return LarkCompleter(name=name, options_lambda=options_lambda, auto=auto, variant=None)
|
|
67
|
+
|
|
68
|
+
@functools.lru_cache()
|
|
69
|
+
def lark_parser(variant: str):
|
|
70
|
+
dir_path = os.path.dirname(os.path.realpath(__file__))
|
|
71
|
+
with open(dir_path + f"/qingl.lark") as f:
|
|
72
|
+
grammar: str = None
|
|
73
|
+
with log_timing(f'lark.{variant}.file-read'):
|
|
74
|
+
grammar = f.read()
|
|
75
|
+
|
|
76
|
+
common_contexts = {
|
|
77
|
+
'cd_command.file_name': 'direct-dirs',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if variant in ['a', 'c0', 'p0', 'x0']:
|
|
81
|
+
grammar = grammar.replace('start: statement_sequence', f'start: qing_{variant}_statement')
|
|
82
|
+
contexts_by_path = {
|
|
83
|
+
} | common_contexts
|
|
84
|
+
|
|
85
|
+
debug(f'* GRAMMAR replaced to start: qing_{variant}_statement')
|
|
86
|
+
|
|
87
|
+
return LarkParser(grammar, contexts_by_path)
|
|
88
|
+
elif variant == 'system':
|
|
89
|
+
grammar = grammar.replace('start: statement_sequence', f'start: qing_{variant}_statement')
|
|
90
|
+
contexts_by_path = {
|
|
91
|
+
} | common_contexts
|
|
92
|
+
|
|
93
|
+
return LarkParser(grammar, contexts_by_path)
|
|
94
|
+
|
|
95
|
+
grammar = grammar.replace('qing_statement: qing_p_statement', f'qing_statement: qing_{variant}_statement')
|
|
96
|
+
debug(f'* GRAMMAR replaced to qing_statement: qing_{variant}_statement')
|
|
97
|
+
|
|
98
|
+
bash_contexts = {
|
|
99
|
+
'bash_statement.host_name': 'hosts',
|
|
100
|
+
'bash_statement.bash_command': 'bash-commands',
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
contexts_by_path = {
|
|
104
|
+
'describe_keyspace.keyspace_name': 'keyspaces',
|
|
105
|
+
'keyspace_ref.keyspace_path.namespace_ref.identifier_ref': 'tables',
|
|
106
|
+
'preview_table_statement.path.identifier_ref': 'tables',
|
|
107
|
+
|
|
108
|
+
'insert_statement.insert_select': 'column-names',
|
|
109
|
+
'update_statement.set_clause.path.identifier_ref': 'column-names',
|
|
110
|
+
'update_statement.where_clause.cond.expr.path.identifier_ref': 'column-names',
|
|
111
|
+
'delete_statement.where_clause.cond.expr.path.identifier_ref': 'column-names',
|
|
112
|
+
|
|
113
|
+
'select_clause.projection.result_expr.expr.path.identifier_ref': 'columns',
|
|
114
|
+
'select_from.where_clause.cond.expr.path.identifier_ref': 'columns',
|
|
115
|
+
'select_from.where_clause.cond.expr.logical_term.and_expr.cond.expr.path.identifier_ref': 'columns',
|
|
116
|
+
'select_from.group_by_clause.group_term.expr.path.identifier_ref': 'columns',
|
|
117
|
+
'select_statement.order_by_clause.ordering_term.expr.path.identifier_ref': 'columns',
|
|
118
|
+
'select_from.from_clause.from_terms.join_clause.ansi_join_clause.ansi_join_predicate.expr.path.identifier_ref': 'columns',
|
|
119
|
+
'select_from.from_clause.from_terms.join_clause.ansi_join_clause.ansi_join_predicate.expr.comparison_term.relational_expr.expr.path.identifier_ref': 'columns',
|
|
120
|
+
|
|
121
|
+
'select_from.from_clause.from_terms.from_generic.alias.identifier_ref': 'column-aliases',
|
|
122
|
+
|
|
123
|
+
'select_statement.limit_clause.expr.literal.nbr.digit': 'limits',
|
|
124
|
+
} | common_contexts
|
|
125
|
+
|
|
126
|
+
if variant == 'p':
|
|
127
|
+
contexts_by_path = bash_contexts | contexts_by_path
|
|
128
|
+
elif variant == 'c':
|
|
129
|
+
contexts_by_path = {
|
|
130
|
+
'export_table.path.identifier_ref': 'tables',
|
|
131
|
+
'show_column_counts_command.path.identifier_ref': 'tables',
|
|
132
|
+
'export_statement.export_tables.keyspace_name': 'keyspaces',
|
|
133
|
+
|
|
134
|
+
'alter_cql_table_statement.properties.property.property_name': 'table-props',
|
|
135
|
+
'alter_tables_statement.properties.property.property_name': 'table-props',
|
|
136
|
+
'alter_cql_table_statement.properties.property.property_value.literal': 'table-props-value',
|
|
137
|
+
|
|
138
|
+
'select_clause.projection.result_expr.expr.path.identifier_ref': 'columns',
|
|
139
|
+
'export_statement.export_tables.export_table.column_name_list.column_name': 'columns',
|
|
140
|
+
|
|
141
|
+
'consistency_statement.consistency': 'consistencies',
|
|
142
|
+
'export_statement.export_to.export_database_type': 'export-database-types',
|
|
143
|
+
'drop_export_database.export_database_name': 'export-databases',
|
|
144
|
+
'use_export_db_statement.export_database_name': 'export-databases',
|
|
145
|
+
'clean_up_export_session_statement.clean_up_export_sessions.export_session_name': 'export-sessions',
|
|
146
|
+
'show_export_command.export_session_name': 'export-sessions',
|
|
147
|
+
'import_statement.import_session.export_session_name': 'export-sessions-incomplete',
|
|
148
|
+
'download_session_statement.export_session_name': 'export-sessions-incomplete',
|
|
149
|
+
} | bash_contexts | contexts_by_path
|
|
150
|
+
elif variant == 'l':
|
|
151
|
+
contexts_by_path = {
|
|
152
|
+
'add_partition_action.partition_ref.partition_name': 'partition-columns',
|
|
153
|
+
'show_topn_statement.topn_count': 'topn-counts',
|
|
154
|
+
'show_topn_statement.topn_type': 'topn-types',
|
|
155
|
+
'show_topn_statement.topn_window': 'topn-windows'
|
|
156
|
+
} | contexts_by_path
|
|
157
|
+
elif variant == 'x':
|
|
158
|
+
contexts_by_path = {
|
|
159
|
+
'show_column_counts_command.path.identifier_ref': 'tables',
|
|
160
|
+
'drop_export_database.export_database_name': 'export-databases',
|
|
161
|
+
'use_export_db_statement.export_database_name': 'export-databases',
|
|
162
|
+
} | contexts_by_path
|
|
163
|
+
|
|
164
|
+
grammar = grammar.replace('select_clause: "SELECT"i hint_comment? projection', 'select_clause: ("SELECT"i | "XELECT"i) hint_comment? projection')
|
|
165
|
+
|
|
166
|
+
with offload():
|
|
167
|
+
with open('/tmp/grammar.lark', 'wt') as f:
|
|
168
|
+
f.write(grammar)
|
|
169
|
+
|
|
170
|
+
return LarkParser(grammar, contexts_by_path)
|
|
171
|
+
|
|
172
|
+
def get_completions(
|
|
173
|
+
self, document: Document, complete_event: CompleteEvent
|
|
174
|
+
) -> Iterable[Completion]:
|
|
175
|
+
if not self.nested and self.options_lambda:
|
|
176
|
+
# for lazy completions
|
|
177
|
+
self.nested = NestedCompleter.from_nested_dict(self.options_lambda())
|
|
178
|
+
|
|
179
|
+
nested_words = set()
|
|
180
|
+
|
|
181
|
+
if self.nested:
|
|
182
|
+
# from NestedCompleter
|
|
183
|
+
|
|
184
|
+
# Split document.
|
|
185
|
+
text = document.text_before_cursor.lstrip()
|
|
186
|
+
stripped_len = len(document.text_before_cursor) - len(text)
|
|
187
|
+
|
|
188
|
+
# If there is a space, check for the first term, and use a
|
|
189
|
+
# subcompleter.
|
|
190
|
+
if " " in text:
|
|
191
|
+
first_term = text.split()[0]
|
|
192
|
+
completer = self.nested.options.get(first_term)
|
|
193
|
+
|
|
194
|
+
# If we have a sub completer, use this for the completions.
|
|
195
|
+
if completer is not None:
|
|
196
|
+
remaining_text = text[len(first_term) :].lstrip()
|
|
197
|
+
move_cursor = len(text) - len(remaining_text) + stripped_len
|
|
198
|
+
|
|
199
|
+
new_document = Document(
|
|
200
|
+
remaining_text,
|
|
201
|
+
cursor_position=document.cursor_position - move_cursor,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
for c in completer.get_completions(new_document, complete_event):
|
|
205
|
+
nested_words.add(c.text)
|
|
206
|
+
yield c
|
|
207
|
+
|
|
208
|
+
# No space in the input: behave exactly like `WordCompleter`.
|
|
209
|
+
else:
|
|
210
|
+
completer = WordCompleter(
|
|
211
|
+
list(self.nested.options.keys()), ignore_case=self.nested.ignore_case
|
|
212
|
+
)
|
|
213
|
+
for c in completer.get_completions(document, complete_event):
|
|
214
|
+
nested_words.add(c.text)
|
|
215
|
+
yield c
|
|
216
|
+
|
|
217
|
+
if self.parser:
|
|
218
|
+
full = document.text_before_cursor
|
|
219
|
+
if self.dml:
|
|
220
|
+
full = self.dml + ' ' + full
|
|
221
|
+
|
|
222
|
+
words0 = []
|
|
223
|
+
words1 = []
|
|
224
|
+
context = {}
|
|
225
|
+
for word in self.parser.next_terminals(full, context=context):
|
|
226
|
+
if ex := self.expandable(word):
|
|
227
|
+
if ex in self.expandables:
|
|
228
|
+
e = self.expandables[ex]
|
|
229
|
+
if callable(e):
|
|
230
|
+
if self.auto_complete(ex) != 'off':
|
|
231
|
+
ctx = None
|
|
232
|
+
if 'last-id' in context:
|
|
233
|
+
ctx = context['last-id']
|
|
234
|
+
e = e(ctx)
|
|
235
|
+
words0.extend(e)
|
|
236
|
+
else:
|
|
237
|
+
words0.extend(e)
|
|
238
|
+
else:
|
|
239
|
+
words1.append(word)
|
|
240
|
+
words = words0 + words1
|
|
241
|
+
|
|
242
|
+
word_before_cursor = document.get_word_before_cursor(
|
|
243
|
+
WORD=self.WORD, pattern=self.pattern
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
word_before_cursor = word_before_cursor.lower()
|
|
247
|
+
|
|
248
|
+
def word_matches(word: str) -> bool:
|
|
249
|
+
return word.lower().startswith(word_before_cursor)
|
|
250
|
+
|
|
251
|
+
for word in words:
|
|
252
|
+
if word_matches(word) and word not in nested_words:
|
|
253
|
+
display = self.display_dict.get(word, word)
|
|
254
|
+
display_meta = self.meta_dict.get(word, "")
|
|
255
|
+
yield Completion(
|
|
256
|
+
word,
|
|
257
|
+
-len(word_before_cursor),
|
|
258
|
+
display=display,
|
|
259
|
+
display_meta=display_meta,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
def completions_for_nesting(self, dml: str = None):
|
|
263
|
+
if dml:
|
|
264
|
+
return {dml: LarkCompleter(dml, expandables=self.expandables, variant=self.variant)}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
word.text.lower(): LarkCompleter(word.text, expandables=self.expandables, variant=self.variant)
|
|
268
|
+
for word in self.get_completions(Document(''), None)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
def expandable(self, word: str):
|
|
272
|
+
return word.strip('`') if word.startswith('`') else None
|
|
273
|
+
|
|
274
|
+
def append_completions(self, key: str, value: dict[str, any]):
|
|
275
|
+
if isinstance(value, LarkCompleter) and self.variant == value.variant:
|
|
276
|
+
return
|
|
277
|
+
|
|
278
|
+
if self.nested:
|
|
279
|
+
self.nested = NestedCompleter.from_nested_dict(merge_completions(self.nested.options, value))
|
|
280
|
+
else:
|
|
281
|
+
self.nested = NestedCompleter.from_nested_dict(value)
|
|
282
|
+
|
|
283
|
+
def auto_complete(self, key: str, default = 'lazy'):
|
|
284
|
+
return Config().get(f'auto-complete.{self.variant}.{key}', default=default)
|