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.

Files changed (209) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +9 -12
  3. adam/apps.py +18 -4
  4. adam/batch.py +4 -4
  5. adam/checks/check_utils.py +16 -46
  6. adam/checks/cpu.py +7 -1
  7. adam/checks/cpu_metrics.py +52 -0
  8. adam/checks/disk.py +2 -3
  9. adam/columns/columns.py +3 -1
  10. adam/columns/cpu.py +3 -1
  11. adam/columns/cpu_metrics.py +22 -0
  12. adam/columns/memory.py +3 -4
  13. adam/commands/__init__.py +24 -0
  14. adam/commands/alter_tables.py +33 -48
  15. adam/commands/app/__init__.py +0 -0
  16. adam/commands/app/app.py +38 -0
  17. adam/commands/{app_ping.py → app/app_ping.py} +7 -13
  18. adam/commands/app/show_app_actions.py +49 -0
  19. adam/commands/{show → app}/show_app_id.py +8 -11
  20. adam/commands/{show → app}/show_app_queues.py +7 -14
  21. adam/commands/app/utils_app.py +106 -0
  22. adam/commands/audit/audit.py +21 -40
  23. adam/commands/audit/audit_repair_tables.py +14 -19
  24. adam/commands/audit/audit_run.py +14 -22
  25. adam/commands/audit/completions_l.py +15 -0
  26. adam/commands/audit/show_last10.py +4 -19
  27. adam/commands/audit/show_slow10.py +4 -18
  28. adam/commands/audit/show_top10.py +4 -16
  29. adam/commands/audit/utils_show_top10.py +15 -3
  30. adam/commands/bash/__init__.py +5 -0
  31. adam/commands/bash/bash.py +7 -104
  32. adam/commands/bash/utils_bash.py +16 -0
  33. adam/commands/cat.py +7 -27
  34. adam/commands/cd.py +7 -11
  35. adam/commands/check.py +15 -24
  36. adam/commands/cli_commands.py +8 -4
  37. adam/commands/clipboard_copy.py +87 -0
  38. adam/commands/code.py +21 -24
  39. adam/commands/command.py +207 -42
  40. adam/commands/commands_utils.py +25 -27
  41. adam/commands/cql/completions_c.py +28 -0
  42. adam/commands/cql/cqlsh.py +9 -33
  43. adam/commands/cql/{cql_utils.py → utils_cql.py} +111 -15
  44. adam/commands/deploy/code_start.py +7 -10
  45. adam/commands/deploy/code_stop.py +4 -21
  46. adam/commands/deploy/code_utils.py +3 -3
  47. adam/commands/deploy/deploy.py +4 -27
  48. adam/commands/deploy/deploy_frontend.py +14 -17
  49. adam/commands/deploy/deploy_pg_agent.py +3 -6
  50. adam/commands/deploy/deploy_pod.py +64 -68
  51. adam/commands/deploy/undeploy.py +4 -27
  52. adam/commands/deploy/undeploy_frontend.py +4 -7
  53. adam/commands/deploy/undeploy_pg_agent.py +5 -8
  54. adam/commands/deploy/undeploy_pod.py +9 -12
  55. adam/commands/devices/device.py +124 -2
  56. adam/commands/devices/device_app.py +41 -24
  57. adam/commands/devices/device_auit_log.py +10 -4
  58. adam/commands/devices/device_cass.py +48 -14
  59. adam/commands/devices/device_export.py +13 -12
  60. adam/commands/devices/device_postgres.py +105 -54
  61. adam/commands/download_file.py +47 -0
  62. adam/commands/exit.py +1 -4
  63. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  64. adam/commands/export/clean_up_export_sessions.py +9 -10
  65. adam/commands/export/completions_x.py +11 -0
  66. adam/commands/export/download_export_session.py +40 -0
  67. adam/commands/export/drop_export_database.py +7 -26
  68. adam/commands/export/drop_export_databases.py +5 -14
  69. adam/commands/export/export.py +6 -52
  70. adam/commands/export/export_databases.py +108 -32
  71. adam/commands/export/export_select.py +8 -59
  72. adam/commands/export/export_sessions.py +209 -0
  73. adam/commands/export/export_use.py +14 -20
  74. adam/commands/export/export_x_select.py +48 -0
  75. adam/commands/export/exporter.py +135 -167
  76. adam/commands/export/import_files.py +44 -0
  77. adam/commands/export/import_session.py +11 -35
  78. adam/commands/export/importer.py +19 -5
  79. adam/commands/export/importer_athena.py +112 -44
  80. adam/commands/export/importer_sqlite.py +42 -22
  81. adam/commands/export/show_column_counts.py +13 -31
  82. adam/commands/export/show_export_databases.py +7 -7
  83. adam/commands/export/show_export_session.py +8 -20
  84. adam/commands/export/show_export_sessions.py +6 -16
  85. adam/commands/export/utils_export.py +64 -11
  86. adam/commands/find_files.py +51 -0
  87. adam/commands/find_processes.py +76 -0
  88. adam/commands/head.py +36 -0
  89. adam/commands/help.py +2 -2
  90. adam/commands/intermediate_command.py +52 -0
  91. adam/commands/issues.py +11 -43
  92. adam/commands/kubectl.py +3 -6
  93. adam/commands/login.py +22 -24
  94. adam/commands/logs.py +3 -6
  95. adam/commands/ls.py +9 -10
  96. adam/commands/medusa/medusa.py +4 -22
  97. adam/commands/medusa/medusa_backup.py +20 -27
  98. adam/commands/medusa/medusa_restore.py +49 -46
  99. adam/commands/medusa/medusa_show_backupjobs.py +16 -18
  100. adam/commands/medusa/medusa_show_restorejobs.py +13 -18
  101. adam/commands/medusa/utils_medusa.py +15 -0
  102. adam/commands/nodetool.py +7 -21
  103. adam/commands/param_get.py +11 -14
  104. adam/commands/param_set.py +8 -12
  105. adam/commands/postgres/completions_p.py +22 -0
  106. adam/commands/postgres/postgres.py +34 -57
  107. adam/commands/postgres/postgres_databases.py +270 -0
  108. adam/commands/postgres/postgres_ls.py +4 -8
  109. adam/commands/postgres/postgres_preview.py +5 -9
  110. adam/commands/postgres/utils_postgres.py +79 -0
  111. adam/commands/preview_table.py +8 -45
  112. adam/commands/pwd.py +13 -16
  113. adam/commands/reaper/reaper.py +4 -27
  114. adam/commands/reaper/reaper_forward.py +49 -56
  115. adam/commands/reaper/reaper_forward_session.py +6 -0
  116. adam/commands/reaper/reaper_forward_stop.py +10 -16
  117. adam/commands/reaper/reaper_restart.py +7 -14
  118. adam/commands/reaper/reaper_run_abort.py +8 -33
  119. adam/commands/reaper/reaper_runs.py +43 -58
  120. adam/commands/reaper/reaper_runs_abort.py +29 -49
  121. adam/commands/reaper/reaper_schedule_activate.py +14 -33
  122. adam/commands/reaper/reaper_schedule_start.py +9 -33
  123. adam/commands/reaper/reaper_schedule_stop.py +9 -33
  124. adam/commands/reaper/reaper_schedules.py +4 -14
  125. adam/commands/reaper/reaper_status.py +8 -16
  126. adam/commands/reaper/utils_reaper.py +203 -0
  127. adam/commands/repair/repair.py +4 -22
  128. adam/commands/repair/repair_log.py +5 -11
  129. adam/commands/repair/repair_run.py +27 -34
  130. adam/commands/repair/repair_scan.py +32 -40
  131. adam/commands/repair/repair_stop.py +5 -12
  132. adam/commands/report.py +27 -29
  133. adam/commands/restart.py +25 -26
  134. adam/commands/rollout.py +19 -24
  135. adam/commands/shell.py +12 -4
  136. adam/commands/show/show.py +11 -27
  137. adam/commands/show/show_adam.py +3 -3
  138. adam/commands/show/show_cassandra_repairs.py +37 -0
  139. adam/commands/show/show_cassandra_status.py +47 -51
  140. adam/commands/show/show_cassandra_version.py +5 -18
  141. adam/commands/show/show_cli_commands.py +56 -0
  142. adam/commands/show/show_host.py +1 -1
  143. adam/commands/show/show_login.py +20 -27
  144. adam/commands/show/show_params.py +2 -5
  145. adam/commands/show/show_processes.py +18 -21
  146. adam/commands/show/show_storage.py +11 -20
  147. adam/commands/watch.py +26 -29
  148. adam/config.py +5 -16
  149. adam/embedded_params.py +1 -1
  150. adam/log.py +4 -4
  151. adam/pod_exec_result.py +3 -3
  152. adam/repl.py +45 -39
  153. adam/repl_commands.py +26 -19
  154. adam/repl_session.py +8 -1
  155. adam/repl_state.py +85 -36
  156. adam/sql/lark_completer.py +284 -0
  157. adam/sql/lark_parser.py +604 -0
  158. adam/sql/sql_completer.py +4 -6
  159. adam/sql/sql_state_machine.py +29 -16
  160. adam/sso/authn_ad.py +6 -8
  161. adam/sso/authn_okta.py +4 -6
  162. adam/sso/cred_cache.py +3 -5
  163. adam/sso/idp.py +9 -12
  164. adam/utils.py +484 -37
  165. adam/utils_athena.py +19 -19
  166. adam/utils_audits.py +12 -12
  167. adam/utils_issues.py +32 -0
  168. adam/utils_k8s/app_clusters.py +14 -19
  169. adam/utils_k8s/app_pods.py +7 -2
  170. adam/utils_k8s/cassandra_clusters.py +30 -19
  171. adam/utils_k8s/cassandra_nodes.py +2 -2
  172. adam/utils_k8s/custom_resources.py +16 -17
  173. adam/utils_k8s/ingresses.py +2 -2
  174. adam/utils_k8s/jobs.py +7 -11
  175. adam/utils_k8s/k8s.py +96 -0
  176. adam/utils_k8s/kube_context.py +2 -2
  177. adam/utils_k8s/pods.py +37 -81
  178. adam/utils_k8s/secrets.py +4 -4
  179. adam/utils_k8s/service_accounts.py +5 -4
  180. adam/utils_k8s/services.py +2 -2
  181. adam/utils_k8s/statefulsets.py +6 -14
  182. adam/utils_local.py +4 -0
  183. adam/utils_repl/appendable_completer.py +6 -0
  184. adam/utils_repl/repl_completer.py +128 -2
  185. adam/utils_repl/state_machine.py +3 -3
  186. adam/utils_sqlite.py +78 -42
  187. adam/version.py +1 -1
  188. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
  189. kaqing-2.0.189.dist-info/RECORD +253 -0
  190. kaqing-2.0.189.dist-info/top_level.txt +2 -0
  191. teddy/__init__.py +0 -0
  192. teddy/lark_parser.py +436 -0
  193. teddy/lark_parser2.py +618 -0
  194. adam/commands/app.py +0 -67
  195. adam/commands/cp.py +0 -95
  196. adam/commands/cql/cql_completions.py +0 -28
  197. adam/commands/export/clean_up_export_session.py +0 -53
  198. adam/commands/export/export_select_x.py +0 -54
  199. adam/commands/postgres/postgres_context.py +0 -248
  200. adam/commands/postgres/postgres_utils.py +0 -31
  201. adam/commands/postgres/psql_completions.py +0 -10
  202. adam/commands/reaper/reaper_session.py +0 -159
  203. adam/commands/show/show_app_actions.py +0 -56
  204. adam/commands/show/show_commands.py +0 -61
  205. adam/commands/show/show_repairs.py +0 -47
  206. kaqing-2.0.145.dist-info/RECORD +0 -227
  207. kaqing-2.0.145.dist-info/top_level.txt +0 -1
  208. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
  209. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,11 @@
1
+ from adam.commands.export.export_databases import ExportDatabases
2
+ from adam.repl_state import ReplState
3
+ from adam.sql.lark_completer import LarkCompleter
4
+ from adam.utils_athena import Athena
5
+
6
+ def completions_x(state: ReplState):
7
+ return LarkCompleter(expandables={
8
+ 'tables': lambda x: ExportDatabases.table_names(state.export_session),
9
+ 'export-databases': lambda x: ExportDatabases.database_names(),
10
+ 'columns': lambda x: Athena.column_names(database=state.export_session, function='export'),
11
+ }, variant=ReplState.X).completions_for_nesting()
@@ -0,0 +1,40 @@
1
+ from adam.commands import validate_args
2
+ from adam.commands.command import Command
3
+ from adam.commands.export.export_sessions import ExportSessions, export_session
4
+ from adam.repl_state import ReplState, RequiredState
5
+
6
+ class DownloadExportSession(Command):
7
+ COMMAND = 'download export session'
8
+
9
+ # the singleton pattern
10
+ def __new__(cls, *args, **kwargs):
11
+ if not hasattr(cls, 'instance'): cls.instance = super(DownloadExportSession, cls).__new__(cls)
12
+
13
+ return cls.instance
14
+
15
+ def __init__(self, successor: Command=None):
16
+ super().__init__(successor)
17
+
18
+ def command(self):
19
+ return DownloadExportSession.COMMAND
20
+
21
+ def required(self):
22
+ return RequiredState.CLUSTER_OR_POD
23
+
24
+ def run(self, cmd: str, state: ReplState):
25
+ if not(args := self.args(cmd)):
26
+ return super().run(cmd, state)
27
+
28
+ with self.validate(args, state) as (args, state):
29
+ with validate_args(args, state, name='export session') as session:
30
+ with export_session(state) as sessions:
31
+ sessions.download_session(session)
32
+
33
+ return state
34
+
35
+ def completion(self, state: ReplState):
36
+ return {}
37
+ # return super().completion(state, {session: None for session in ExportSessions.export_session_names(state.sts, state.pod, state.namespace, export_state='pending_import')})
38
+
39
+ def help(self, _: ReplState):
40
+ return f'{DownloadExportSession.COMMAND} <export-session-name>\t download csv files in export session'
@@ -1,9 +1,7 @@
1
+ from adam.commands import validate_args
1
2
  from adam.commands.command import Command
2
- from adam.commands.export.exporter import Exporter
3
+ from adam.commands.export.export_databases import export_db
3
4
  from adam.repl_state import ReplState
4
- from adam.utils import log2
5
- from adam.utils_athena import Athena
6
- from adam.utils_sqlite import SQLite
7
5
 
8
6
  class DropExportDatabase(Command):
9
7
  COMMAND = 'drop export database'
@@ -27,29 +25,12 @@ class DropExportDatabase(Command):
27
25
  if not(args := self.args(cmd)):
28
26
  return super().run(cmd, state)
29
27
 
30
- state, args = self.apply_state(args, state)
31
- if not self.validate_state(state):
32
- return state
28
+ with self.validate(args, state) as (args, state):
29
+ with validate_args(args, state, name='database name') as db:
30
+ with export_db(state) as dbs:
31
+ dbs.drop(args[0])
33
32
 
34
- if not len(args):
35
- if state.in_repl:
36
- log2('Database name is required.')
37
- log2()
38
- else:
39
- log2('* Database name is missing.')
40
- Command.display_help()
41
-
42
- return 'command-missing'
43
-
44
- Exporter.drop_databases(state.sts, state.pod, state.namespace, args[0])
45
-
46
- SQLite.clear_cache()
47
- Athena.clear_cache()
48
-
49
- if state.export_session == args[0]:
50
- state.export_session = None
51
-
52
- return state
33
+ return state
53
34
 
54
35
  def completion(self, _: ReplState):
55
36
  return {}
@@ -1,8 +1,6 @@
1
1
  from adam.commands.command import Command
2
- from adam.commands.export.exporter import Exporter
2
+ from adam.commands.export.export_databases import export_db
3
3
  from adam.repl_state import ReplState
4
- from adam.utils_athena import Athena
5
- from adam.utils_sqlite import SQLite
6
4
 
7
5
  class DropExportDatabases(Command):
8
6
  COMMAND = 'drop all export databases'
@@ -26,18 +24,11 @@ class DropExportDatabases(Command):
26
24
  if not(args := self.args(cmd)):
27
25
  return super().run(cmd, state)
28
26
 
29
- state, args = self.apply_state(args, state)
30
- if not self.validate_state(state):
31
- return state
32
-
33
- Exporter.drop_databases(state.sts, state.pod, state.namespace)
34
-
35
- SQLite.clear_cache()
36
- Athena.clear_cache()
27
+ with self.validate(args, state) as (args, state):
28
+ with export_db(state) as dbs:
29
+ dbs.drop_all()
37
30
 
38
- state.export_session = None
39
-
40
- return state
31
+ return state
41
32
 
42
33
  def completion(self, _: ReplState):
43
34
  return {}
@@ -1,12 +1,7 @@
1
+ from adam.commands import extract_options
1
2
  from adam.commands.command import Command
2
- from adam.commands.cql.cql_utils import cassandra_keyspaces, cassandra_table_names
3
- from adam.commands.export.export_databases import ExportDatabases
4
- from adam.commands.export.exporter import Exporter
5
- from adam.commands.export.utils_export import ExportSpec
3
+ from adam.commands.export.exporter import export
6
4
  from adam.repl_state import ReplState, RequiredState
7
- from adam.sql.sql_completer import SqlCompleter, SqlVariant
8
- from adam.utils import log
9
- from adam.utils_k8s.statefulsets import StatefulSets
10
5
 
11
6
  class ExportTables(Command):
12
7
  COMMAND = 'export'
@@ -30,53 +25,12 @@ class ExportTables(Command):
30
25
  if not(args := self.args(cmd)):
31
26
  return super().run(cmd, state)
32
27
 
33
- state, args = self.apply_state(args, state)
34
- if not self.validate_state(state):
35
- return state
36
-
37
- # --export-only for testing only
38
- args, export_only = Command.extract_options(args, ['--export-only'])
39
-
40
- if not state.pod:
41
- state.push()
42
- state.pod = StatefulSets.pod_names(state.sts, state.namespace)[0]
43
-
44
- export_session = state.export_session
45
- spec: ExportSpec = None
46
- try:
47
- statuses, spec = Exporter.export_tables(args, state, export_only=export_only)
48
- if not statuses:
49
- return state
50
-
51
- Exporter.clear_export_session_cache()
52
-
53
- if spec.importer == 'csv' or export_only:
54
- ExportDatabases.disply_export_session(state.sts, state.pod, state.namespace, spec.session)
55
- else:
56
- log()
57
- ExportDatabases.display_export_db(state.export_session)
58
-
59
- finally:
60
- state.pop()
61
-
62
- # if exporting to csv, do not bind the new session id to repl state
63
- if spec and spec.importer == 'csv':
64
- state.export_session = export_session
65
-
66
- return state
28
+ with self.validate(args, state) as (args, state):
29
+ with extract_options(args, '--export-only') as (args, export_only):
30
+ with export(state) as exporter:
31
+ return exporter.export(args, export_only=export_only)
67
32
 
68
33
  def completion(self, state: ReplState):
69
- def sc():
70
- return SqlCompleter(lambda: cassandra_table_names(state), expandables={
71
- 'dml':'export',
72
- 'columns': lambda table: ['id', '*'],
73
- 'keyspaces': lambda: cassandra_keyspaces(state),
74
- 'export-dbs': lambda: ExportDatabases.database_names(),
75
- }, variant=SqlVariant.CQL)
76
-
77
- if state.sts:
78
- return {f'@{p}': {ExportTables.COMMAND: sc()} for p in StatefulSets.pod_names(state.sts, state.namespace)}
79
-
80
34
  return {}
81
35
 
82
36
  def help(self, _: ReplState):
@@ -1,26 +1,37 @@
1
+ from collections.abc import Callable
2
+ from datetime import datetime
1
3
  import os
2
4
  import boto3
3
5
 
4
- from adam.commands.export.utils_export import ExportTableStatus
6
+ from adam.commands.export.export_sessions import ExportSessions
7
+ from adam.commands.export.importer import Importer
5
8
  from adam.config import Config
6
- from adam.utils import lines_to_tabular, log, log2, ing
9
+ from adam.repl_session import ReplSession
10
+ from adam.repl_state import ReplState
11
+ from adam.utils import debug, log_timing, tabulize, log2, ing, log_exc
7
12
  from adam.utils_athena import Athena
8
- from adam.utils_k8s.statefulsets import StatefulSets
9
13
  from adam.utils_sqlite import SQLite
10
14
 
11
15
  LIKE = 'e%_%'
12
16
 
13
17
  class ExportDatabases:
14
- def run_query(query: str, database: str, show_query=False) -> int:
18
+ def run_query(query: str, database: str, show_query=False, output: Callable[[str], str] = None) -> int:
15
19
  cnt: int = 0
16
20
 
17
21
  if show_query:
18
22
  log2(query)
19
23
 
24
+ log_file = None
20
25
  if database.startswith('s'):
21
- cnt += SQLite.run_query(query, database=database)
26
+ cnt0, log_file = SQLite.run_query(query, database=database, output=output)
27
+ cnt += cnt0
22
28
  else:
23
- cnt += Athena.run_query(query, database=database)
29
+ cnt0, log_file = Athena.run_query(query, database=database, output=output)
30
+ cnt += cnt0
31
+
32
+ if Config().get('repl.history.push-cat-log-file', True):
33
+ if log_file and ReplSession().prompt_session:
34
+ ReplSession().prompt_session.history.append_string(f':sh cat {log_file}')
24
35
 
25
36
  return cnt
26
37
 
@@ -47,15 +58,13 @@ class ExportDatabases:
47
58
  dbs = SQLite.database_names(db)
48
59
  if dbs:
49
60
  with ing(f'Droping {len(dbs)} SQLite databases'):
50
- try:
61
+ with log_exc():
51
62
  for db in dbs:
52
63
  file_path = f'{SQLite.local_db_dir()}/{db}'
53
64
  try:
54
65
  os.remove(file_path)
55
66
  except OSError as e:
56
67
  pass
57
- except:
58
- pass
59
68
 
60
69
  return dbs
61
70
 
@@ -65,49 +74,35 @@ class ExportDatabases:
65
74
  with ing(f'Droping {len(dbs)} Athena databases'):
66
75
  for db in dbs:
67
76
  query = f'DROP DATABASE {db} CASCADE'
68
- if Config().is_debug():
69
- log2(query)
77
+ debug(query)
70
78
  Athena.query(query)
71
79
 
72
80
  with ing(f'Deleting s3 folder: export'):
73
- try:
81
+ with log_exc():
74
82
  if not db:
75
83
  db = ''
76
84
 
77
85
  s3 = boto3.resource('s3')
78
86
  bucket = s3.Bucket(Config().get('export.bucket', 'c3.ops--qing'))
79
87
  bucket.objects.filter(Prefix=f'export/{db}').delete()
80
- except:
81
- pass
82
88
 
83
89
  return dbs
84
90
 
85
- def display_export_db(export_session: str):
86
- if not export_session:
91
+ def show_database(database: str):
92
+ if not database:
87
93
  return
88
94
 
89
- Athena.clear_cache()
95
+ ExportDatabases.clear_cache()
90
96
 
91
97
  keyspaces = {}
92
- for table in ExportDatabases.table_names(export_session):
98
+ for table in ExportDatabases.table_names(database):
93
99
  keyspace = table.split('.')[0]
94
100
  if keyspace in keyspaces:
95
101
  keyspaces[keyspace] += 1
96
102
  else:
97
103
  keyspaces[keyspace] = 1
98
104
 
99
- log(lines_to_tabular([f'{k},{v}' for k, v in keyspaces.items()], header='SCHEMA,# of TABLES', separator=','))
100
-
101
- def disply_export_session(sts: str, pod: str, namespace: str, session: str):
102
- if not pod:
103
- pod = StatefulSets.pod_names(sts, namespace)[0]
104
-
105
- if not pod:
106
- return
107
-
108
- tables, _ = ExportTableStatus.from_session(sts, pod, namespace, session)
109
- log()
110
- log(lines_to_tabular([f'{table.keyspace}\t{table.table}\t{table.target_table}\t{"export_completed_pending_import" if table.status == "pending_import" else table.status}' for table in tables], header='KEYSPACE\tTABLE\tTARGET_TABLE\tSTATUS', separator='\t'))
105
+ tabulize(keyspaces.items(), lambda a: f'{a[0]},{a[1]}', header='SCHEMA,# of TABLES', separator=',')
111
106
 
112
107
  def database_names():
113
108
  return ExportDatabases.copy_database_names() + ExportDatabases.export_database_names()
@@ -116,7 +111,8 @@ class ExportDatabases:
116
111
  return list({n.split('_')[0] for n in SQLite.database_names()})
117
112
 
118
113
  def export_database_names():
119
- return list({n.split('_')[0] for n in Athena.database_names(LIKE)})
114
+ with log_timing('ExportDatabases.Athena.database_names'):
115
+ return list({n.split('_')[0] for n in Athena.database_names(LIKE)})
120
116
 
121
117
  def database_names_with_keyspace_cnt(importer: str = None):
122
118
  r = {}
@@ -167,4 +163,84 @@ class ExportDatabases:
167
163
  if db.startswith('s'):
168
164
  return SQLite.database_names(prefix=f'{eprefix}_')
169
165
  else:
170
- return Athena.database_names(like=f'{eprefix}_%')
166
+ return Athena.database_names(like=f'{eprefix}_%')
167
+
168
+ def drop_databases(sts: str, pod: str, namespace: str, database: str = None):
169
+ importer = None
170
+ if database:
171
+ importer = Importer.importer_from_session(database)
172
+
173
+ sessions_done = ExportSessions.export_session_names(sts, pod, namespace, importer=importer, export_state='done')
174
+ sessions = ExportDatabases.sessions_from_dbs(ExportDatabases.drop_export_dbs(database))
175
+ if sessions_done and sessions:
176
+ intersects = list(set(sessions_done) & set(sessions))
177
+ with ing(f'Cleaning up {len(intersects)} completed sessions'):
178
+ ExportSessions.clean_up_sessions(sts, pod, namespace, list(intersects))
179
+ ExportSessions.clear_export_session_cache()
180
+
181
+ def clear_cache(database: str = None):
182
+ if not database or database.startswith('s'):
183
+ SQLite.clear_cache()
184
+ if not database or database.startswith('e'):
185
+ Athena.clear_cache()
186
+
187
+ def show_databases(importer: str = None):
188
+ lines = [f'{k}\t{v}' for k, v in ExportDatabases.database_names_with_keyspace_cnt(importer).items()]
189
+ tabulize(lines, header='NAME\tKEYSPACES', separator='\t')
190
+
191
+ class ExportDatabaseService:
192
+ def __init__(self, handler: 'ExportDatabaseHandler'):
193
+ self.handler = handler
194
+
195
+ def sql(self, query: str, database: str = None, backgrounded = False):
196
+ if not database:
197
+ database = self.handler.state.export_session
198
+
199
+ def output(out: str):
200
+ log_prefix = Config().get('export.log-prefix', '/tmp/qing')
201
+ log_file = f'{log_prefix}-{datetime.now().strftime("%d%H%M%S")}-export.log'
202
+
203
+ with open(log_file, 'w') as f:
204
+ f.write(out)
205
+
206
+ return log_file
207
+
208
+ ExportDatabases.run_query(query, database, output = output if backgrounded else None, show_query = not backgrounded)
209
+
210
+ def drop(self, database: str):
211
+ state = self.handler.state
212
+
213
+ ExportDatabases.drop_databases(state.sts, state.pod, state.namespace, database)
214
+ ExportDatabases.clear_cache(database)
215
+ if state.export_session == database:
216
+ state.export_session = None
217
+
218
+ def drop_all(self):
219
+ state = self.handler.state
220
+
221
+ ExportDatabases.drop_databases(state.sts, state.pod, state.namespace)
222
+ ExportDatabases.clear_cache()
223
+
224
+ state.export_session = None
225
+
226
+ def show_databases(self, importer: str = None):
227
+ ExportDatabases.show_databases(importer)
228
+
229
+ def show_database(self, database: str = None):
230
+ if not database:
231
+ database = self.handler.state.export_session
232
+
233
+ ExportDatabases.show_database(database)
234
+
235
+ class ExportDatabaseHandler:
236
+ def __init__(self, state: ReplState = None):
237
+ self.state = state
238
+
239
+ def __enter__(self):
240
+ return ExportDatabaseService(self)
241
+
242
+ def __exit__(self, exc_type, exc_val, exc_tb):
243
+ return False
244
+
245
+ def export_db(state: ReplState = None):
246
+ return ExportDatabaseHandler(state)
@@ -1,13 +1,10 @@
1
1
  from adam.commands.command import Command
2
- from adam.commands.export.export_databases import ExportDatabases
2
+ from adam.commands.export.completions_x import completions_x
3
3
  from adam.repl_state import ReplState, RequiredState
4
- from adam.sql.sql_completer import SqlCompleter, SqlVariant
5
- from adam.utils import log2
6
- from adam.utils_athena import Athena
7
- from adam.utils_sqlite import SQLite
8
4
 
5
+ # No action body, only for a help entry and auto-completion
9
6
  class ExportSelect(Command):
10
- COMMAND = '.select'
7
+ COMMAND = 'select_on_x'
11
8
 
12
9
  # the singleton pattern
13
10
  def __new__(cls, *args, **kwargs):
@@ -24,62 +21,14 @@ class ExportSelect(Command):
24
21
  def required(self):
25
22
  return RequiredState.EXPORT_DB
26
23
 
27
- def run(self, cmd: str, state: ReplState):
28
- if not(args := self.args(cmd)):
29
- return super().run(cmd, state)
30
-
31
- state, args = self.apply_state(args, state)
32
- if not self.validate_state(state):
33
- return state
34
-
35
- if not state.export_session:
36
- if state.in_repl:
37
- if state.device == ReplState.C:
38
- log2("Select an export database first with 'use' command.")
39
- else:
40
- log2('cd to an export database first.')
41
- else:
42
- log2('* export database is missing.')
43
-
44
- Command.display_help()
45
-
46
- return 'command-missing'
47
-
48
- if not args:
49
- if state.in_repl:
50
- log2('Use a SQL statement.')
51
- else:
52
- log2('* SQL statement is missing.')
53
-
54
- Command.display_help()
55
-
56
- return 'command-missing'
57
-
58
- query = ' '.join(args)
59
-
60
- ExportDatabases.run_query(f'select {query}', database=state.export_session)
61
-
62
- return state
63
-
64
24
  def completion(self, state: ReplState):
65
- if not state.export_session:
25
+ if state.device != ReplState.X:
66
26
  return {}
67
27
 
68
- db = state.export_session
69
-
70
- # warm up the caches first time when x: drive is accessed
71
- ExportDatabases.table_names(db)
72
- Athena.column_names(database=db, function='export')
73
- Athena.column_names(partition_cols_only=True, database=db, function='export')
28
+ if state.export_session:
29
+ return completions_x(state)
74
30
 
75
- return {ExportSelect.COMMAND: SqlCompleter(
76
- lambda: ExportDatabases.table_names(db),
77
- dml='select',
78
- expandables={
79
- 'columns':lambda table: Athena.column_names(database=db, function='export'),
80
- },
81
- variant=SqlVariant.ATHENA
82
- )}
31
+ return {}
83
32
 
84
33
  def help(self, _: ReplState):
85
- return f'.<sql-select-statements>\t run queries on export database'
34
+ return f'<sql-select-statements>\t run queries on export database'