kaqing 2.0.103__tar.gz → 2.0.105__tar.gz

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 (193) hide show
  1. {kaqing-2.0.103 → kaqing-2.0.105}/PKG-INFO +1 -1
  2. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/audit/audit.py +10 -9
  3. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/audit/audit_repair_tables.py +14 -37
  4. kaqing-2.0.105/adam/commands/audit/audit_run.py +51 -0
  5. kaqing-2.0.105/adam/commands/bash.py +150 -0
  6. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/ls.py +2 -2
  7. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/preview_table.py +3 -3
  8. kaqing-2.0.105/adam/embedded_params.py +2 -0
  9. {kaqing-2.0.103 → kaqing-2.0.105}/adam/repl_commands.py +3 -2
  10. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sql/sql_completer.py +31 -4
  11. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sql/sql_state_machine.py +92 -8
  12. kaqing-2.0.105/adam/utils_audits.py +167 -0
  13. kaqing-2.0.105/adam/version.py +5 -0
  14. {kaqing-2.0.103 → kaqing-2.0.105}/kaqing.egg-info/PKG-INFO +1 -1
  15. {kaqing-2.0.103 → kaqing-2.0.105}/kaqing.egg-info/SOURCES.txt +2 -3
  16. {kaqing-2.0.103 → kaqing-2.0.105}/setup.py +1 -1
  17. kaqing-2.0.103/adam/commands/bash.py +0 -92
  18. kaqing-2.0.103/adam/embedded_params.py +0 -2
  19. kaqing-2.0.103/adam/sql/automata_completer.py +0 -63
  20. kaqing-2.0.103/adam/sql/state_machine.py +0 -207
  21. kaqing-2.0.103/adam/utils_athena.py +0 -95
  22. kaqing-2.0.103/adam/version.py +0 -5
  23. {kaqing-2.0.103 → kaqing-2.0.105}/README +0 -0
  24. {kaqing-2.0.103 → kaqing-2.0.105}/adam/__init__.py +0 -0
  25. {kaqing-2.0.103 → kaqing-2.0.105}/adam/app_session.py +0 -0
  26. {kaqing-2.0.103 → kaqing-2.0.105}/adam/apps.py +0 -0
  27. {kaqing-2.0.103 → kaqing-2.0.105}/adam/batch.py +0 -0
  28. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/__init__.py +0 -0
  29. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/check.py +0 -0
  30. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/check_context.py +0 -0
  31. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/check_result.py +0 -0
  32. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/check_utils.py +0 -0
  33. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/compactionstats.py +0 -0
  34. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/cpu.py +0 -0
  35. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/disk.py +0 -0
  36. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/gossip.py +0 -0
  37. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/issue.py +0 -0
  38. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/memory.py +0 -0
  39. {kaqing-2.0.103 → kaqing-2.0.105}/adam/checks/status.py +0 -0
  40. {kaqing-2.0.103 → kaqing-2.0.105}/adam/cli.py +0 -0
  41. {kaqing-2.0.103 → kaqing-2.0.105}/adam/cli_group.py +0 -0
  42. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/__init__.py +0 -0
  43. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/column.py +0 -0
  44. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/columns.py +0 -0
  45. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/compactions.py +0 -0
  46. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/cpu.py +0 -0
  47. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/dir_data.py +0 -0
  48. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/dir_snapshots.py +0 -0
  49. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/gossip.py +0 -0
  50. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/host_id.py +0 -0
  51. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/memory.py +0 -0
  52. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/node_address.py +0 -0
  53. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/node_load.py +0 -0
  54. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/node_owns.py +0 -0
  55. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/node_status.py +0 -0
  56. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/node_tokens.py +0 -0
  57. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/node_utils.py +0 -0
  58. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/pod_name.py +0 -0
  59. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/volume_cassandra.py +0 -0
  60. {kaqing-2.0.103 → kaqing-2.0.105}/adam/columns/volume_root.py +0 -0
  61. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/__init__.py +0 -0
  62. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/alter_tables.py +0 -0
  63. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/app.py +0 -0
  64. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/app_ping.py +0 -0
  65. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/audit/__init__.py +0 -0
  66. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/cd.py +0 -0
  67. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/check.py +0 -0
  68. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/cli_commands.py +0 -0
  69. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/command.py +0 -0
  70. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/command_helpers.py +0 -0
  71. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/commands_utils.py +0 -0
  72. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/cp.py +0 -0
  73. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/cql/__init__.py +0 -0
  74. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/cql/cql_completions.py +0 -0
  75. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/cql/cql_utils.py +0 -0
  76. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/cql/cqlsh.py +0 -0
  77. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/__init__.py +0 -0
  78. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/code_start.py +0 -0
  79. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/code_stop.py +0 -0
  80. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/code_utils.py +0 -0
  81. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/deploy.py +0 -0
  82. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/deploy_frontend.py +0 -0
  83. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/deploy_pg_agent.py +0 -0
  84. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/deploy_pod.py +0 -0
  85. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/deploy_utils.py +0 -0
  86. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/undeploy.py +0 -0
  87. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/undeploy_frontend.py +0 -0
  88. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/undeploy_pg_agent.py +0 -0
  89. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/deploy/undeploy_pod.py +0 -0
  90. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/devices.py +0 -0
  91. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/exit.py +0 -0
  92. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/help.py +0 -0
  93. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/issues.py +0 -0
  94. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/login.py +0 -0
  95. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/logs.py +0 -0
  96. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/medusa/__init__.py +0 -0
  97. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/medusa/medusa.py +0 -0
  98. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/medusa/medusa_backup.py +0 -0
  99. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/medusa/medusa_restore.py +0 -0
  100. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/medusa/medusa_show_backupjobs.py +0 -0
  101. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/medusa/medusa_show_restorejobs.py +0 -0
  102. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/nodetool.py +0 -0
  103. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/nodetool_commands.py +0 -0
  104. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/param_get.py +0 -0
  105. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/param_set.py +0 -0
  106. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/postgres/__init__.py +0 -0
  107. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/postgres/postgres.py +0 -0
  108. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/postgres/postgres_context.py +0 -0
  109. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/postgres/postgres_ls.py +0 -0
  110. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/postgres/postgres_preview.py +0 -0
  111. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/postgres/postgres_utils.py +0 -0
  112. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/postgres/psql_completions.py +0 -0
  113. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/pwd.py +0 -0
  114. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/__init__.py +0 -0
  115. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper.py +0 -0
  116. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_forward.py +0 -0
  117. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_forward_stop.py +0 -0
  118. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_restart.py +0 -0
  119. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_run_abort.py +0 -0
  120. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_runs.py +0 -0
  121. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_runs_abort.py +0 -0
  122. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_schedule_activate.py +0 -0
  123. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_schedule_start.py +0 -0
  124. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_schedule_stop.py +0 -0
  125. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_schedules.py +0 -0
  126. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_session.py +0 -0
  127. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/reaper/reaper_status.py +0 -0
  128. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/repair/__init__.py +0 -0
  129. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/repair/repair.py +0 -0
  130. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/repair/repair_log.py +0 -0
  131. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/repair/repair_run.py +0 -0
  132. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/repair/repair_scan.py +0 -0
  133. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/repair/repair_stop.py +0 -0
  134. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/report.py +0 -0
  135. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/restart.py +0 -0
  136. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/rollout.py +0 -0
  137. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/shell.py +0 -0
  138. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/__init__.py +0 -0
  139. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show.py +0 -0
  140. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_adam.py +0 -0
  141. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_app_actions.py +0 -0
  142. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_app_id.py +0 -0
  143. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_app_queues.py +0 -0
  144. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_cassandra_status.py +0 -0
  145. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_cassandra_version.py +0 -0
  146. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_commands.py +0 -0
  147. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_host.py +0 -0
  148. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_login.py +0 -0
  149. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_params.py +0 -0
  150. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_processes.py +0 -0
  151. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_repairs.py +0 -0
  152. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/show/show_storage.py +0 -0
  153. {kaqing-2.0.103 → kaqing-2.0.105}/adam/commands/watch.py +0 -0
  154. {kaqing-2.0.103 → kaqing-2.0.105}/adam/config.py +0 -0
  155. {kaqing-2.0.103 → kaqing-2.0.105}/adam/embedded_apps.py +0 -0
  156. {kaqing-2.0.103 → kaqing-2.0.105}/adam/log.py +0 -0
  157. {kaqing-2.0.103 → kaqing-2.0.105}/adam/pod_exec_result.py +0 -0
  158. {kaqing-2.0.103 → kaqing-2.0.105}/adam/repl.py +0 -0
  159. {kaqing-2.0.103 → kaqing-2.0.105}/adam/repl_session.py +0 -0
  160. {kaqing-2.0.103 → kaqing-2.0.105}/adam/repl_state.py +0 -0
  161. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sql/__init__.py +0 -0
  162. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sql/term_completer.py +0 -0
  163. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/__init__.py +0 -0
  164. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/authenticator.py +0 -0
  165. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/authn_ad.py +0 -0
  166. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/authn_okta.py +0 -0
  167. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/cred_cache.py +0 -0
  168. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/id_token.py +0 -0
  169. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/idp.py +0 -0
  170. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/idp_login.py +0 -0
  171. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/idp_session.py +0 -0
  172. {kaqing-2.0.103 → kaqing-2.0.105}/adam/sso/sso_config.py +0 -0
  173. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils.py +0 -0
  174. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/__init__.py +0 -0
  175. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/cassandra_clusters.py +0 -0
  176. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/cassandra_nodes.py +0 -0
  177. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/config_maps.py +0 -0
  178. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/custom_resources.py +0 -0
  179. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/deployment.py +0 -0
  180. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/ingresses.py +0 -0
  181. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/jobs.py +0 -0
  182. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/kube_context.py +0 -0
  183. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/pods.py +0 -0
  184. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/secrets.py +0 -0
  185. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/service_accounts.py +0 -0
  186. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/services.py +0 -0
  187. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/statefulsets.py +0 -0
  188. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_k8s/volumes.py +0 -0
  189. {kaqing-2.0.103 → kaqing-2.0.105}/adam/utils_net.py +0 -0
  190. {kaqing-2.0.103 → kaqing-2.0.105}/kaqing.egg-info/dependency_links.txt +0 -0
  191. {kaqing-2.0.103 → kaqing-2.0.105}/kaqing.egg-info/entry_points.txt +0 -0
  192. {kaqing-2.0.103 → kaqing-2.0.105}/kaqing.egg-info/top_level.txt +0 -0
  193. {kaqing-2.0.103 → kaqing-2.0.105}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kaqing
3
- Version: 2.0.103
3
+ Version: 2.0.105
4
4
  Summary: UNKNOWN
5
5
  Home-page: UNKNOWN
6
6
  License: UNKNOWN
@@ -1,12 +1,13 @@
1
1
  import click
2
2
 
3
3
  from adam.commands.audit.audit_repair_tables import AuditRepairTables
4
+ from adam.commands.audit.audit_run import AuditRun
4
5
  from adam.commands.command import Command
5
6
  from adam.config import Config
6
7
  from adam.repl_state import ReplState
7
8
  from adam.sql.sql_completer import SqlCompleter
8
9
  from adam.utils import log2
9
- from adam.utils_athena import audit_column_names, audit_table_names, run_audit_query
10
+ from adam.utils_audits import Audits
10
11
 
11
12
  class Audit(Command):
12
13
  COMMAND = 'audit'
@@ -41,7 +42,7 @@ class Audit(Command):
41
42
  else:
42
43
  log2(sql)
43
44
 
44
- run_audit_query(sql)
45
+ Audits.run_audit_query(sql)
45
46
 
46
47
  return state
47
48
 
@@ -51,23 +52,23 @@ class Audit(Command):
51
52
  Config().wait_log(f'Inspecting audit database schema...')
52
53
  self.schema_read = True
53
54
  # warm up the caches first time when l: drive is accessed
54
- audit_column_names()
55
- audit_column_names(partition_cols_only=True)
55
+ Audits.audit_column_names()
56
+ Audits.audit_column_names(partition_cols_only=True)
56
57
 
57
58
  return super().completion(state) | SqlCompleter(
58
- lambda: audit_table_names(),
59
- columns=lambda table: audit_column_names(),
60
- partition_columns=lambda table: audit_column_names(partition_cols_only=True),
59
+ lambda: Audits.audit_table_names(),
60
+ columns=lambda table: Audits.audit_column_names(),
61
+ partition_columns=lambda table: Audits.audit_column_names(partition_cols_only=True),
61
62
  variant='athena'
62
63
  ).completions_for_nesting()
63
64
 
64
65
  return {}
65
66
 
66
67
  def cmd_list():
67
- return [AuditRepairTables()]
68
+ return [AuditRepairTables(), AuditRun()]
68
69
 
69
70
  def help(self, _: ReplState):
70
- return f'[{Audit.COMMAND}] <sql-statements>\t run SQL queries on Authena audit database'
71
+ return f'[{Audit.COMMAND}] [<sql-statements>]\t run SQL queries on Authena audit database'
71
72
 
72
73
  class AuditCommandHelper(click.Command):
73
74
  def get_help(self, ctx: click.Context):
@@ -1,12 +1,11 @@
1
1
  import concurrent
2
2
  import time
3
- import requests
4
3
 
5
4
  from adam.commands.command import Command
6
5
  from adam.config import Config
7
6
  from adam.repl_state import ReplState
8
7
  from adam.utils import log, log2
9
- from adam.utils_athena import audit_query, run_audit_query
8
+ from adam.utils_audits import AuditMeta, Audits
10
9
 
11
10
  class AuditRepairTables(Command):
12
11
  COMMAND = 'audit repair'
@@ -30,11 +29,12 @@ class AuditRepairTables(Command):
30
29
 
31
30
  state, args = self.apply_state(args, state)
32
31
 
33
- tables = Config().get('audit.tables', 'audit').split(',')
32
+ tables = Config().get('audit.athena.repair-partition-tables', 'audit').split(',')
34
33
  if args:
35
34
  tables = args
36
35
 
37
- self.repair(tables)
36
+ meta = Audits.get_meta()
37
+ self.repair(tables, meta)
38
38
 
39
39
  return state
40
40
 
@@ -52,43 +52,20 @@ class AuditRepairTables(Command):
52
52
  def auto_repair(self, hours: int):
53
53
  self.auto_repaired = True
54
54
 
55
- state, _, rs = audit_query(f'select * from meta')
56
- if state == 'SUCCEEDED':
57
- do_repair = True
58
- if len(rs) > 1:
59
- try:
60
- checked_in = float(rs[1]['Data'][0]['VarCharValue'])
61
- do_repair = checked_in + hours * 60 * 60 < time.time()
62
- except:
63
- pass
64
-
65
- if do_repair:
66
- tables = Config().get('audit.athena.tables', 'audit').split(',')
67
- self.repair(tables, show_sql=True)
68
- log2(f'Audit tables have been auto-repaired.')
69
-
70
- def repair(self, tables: list[str], show_sql = False):
55
+ meta: AuditMeta = Audits.get_meta()
56
+ if meta.partitions_last_checked + hours * 60 * 60 < time.time():
57
+ tables = Config().get('audit.athena.repair-partition-tables', 'audit').split(',')
58
+ self.repair(tables, meta, show_sql=True)
59
+ log2(f'Audit tables have been auto-repaired.')
60
+
61
+ def repair(self, tables: list[str], meta: AuditMeta, show_sql = False):
71
62
  with concurrent.futures.ThreadPoolExecutor(max_workers=Config().get('audit.workers', 3)) as executor:
72
63
  for table in tables:
73
64
  if show_sql:
74
65
  log(f'MSCK REPAIR TABLE {table}')
75
66
 
76
- executor.submit(run_audit_query, f'MSCK REPAIR TABLE {table}', None,)
77
- executor.submit(self.check_in,)
67
+ executor.submit(Audits.run_audit_query, f'MSCK REPAIR TABLE {table}', None,)
68
+ executor.submit(Audits.put_meta, Audits.PARTITIONS_ADDED, meta,)
78
69
 
79
70
  def help(self, _: ReplState):
80
- return f"{AuditRepairTables.COMMAND} \t run MSCK REPAIR command for new partition discovery"
81
-
82
- def check_in(self):
83
- payload = {
84
- 'action': 'check-in'
85
- }
86
- audit_endpoint = Config().get("audit.endpoint", "https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/")
87
- try:
88
- response = requests.post(audit_endpoint, json=payload, timeout=Config().get("audit.timeout", 10))
89
- if response.status_code in [200, 201]:
90
- Config().debug(response.text)
91
- else:
92
- log2(f"Error: {response.status_code} {response.text}")
93
- except requests.exceptions.Timeout as e:
94
- log2(f"Timeout occurred: {e}")
71
+ return f"{AuditRepairTables.COMMAND} \t run MSCK REPAIR command for new partition discovery"
@@ -0,0 +1,51 @@
1
+ import concurrent
2
+ from adam.commands.command import Command
3
+ from adam.config import Config
4
+ from adam.repl_state import ReplState
5
+ from adam.utils import log2
6
+ from adam.utils_audits import AuditMeta, Audits
7
+
8
+ class AuditRun(Command):
9
+ COMMAND = 'audit run'
10
+
11
+ # the singleton pattern
12
+ def __new__(cls, *args, **kwargs):
13
+ if not hasattr(cls, 'instance'): cls.instance = super(AuditRun, cls).__new__(cls)
14
+
15
+ return cls.instance
16
+
17
+ def __init__(self, successor: Command=None):
18
+ super().__init__(successor)
19
+ self.auto_repaired = False
20
+
21
+ def command(self):
22
+ return AuditRun.COMMAND
23
+
24
+ def run(self, cmd: str, state: ReplState):
25
+ if not(args := self.args(cmd)):
26
+ return super().run(cmd, state)
27
+
28
+ state, args = self.apply_state(args, state)
29
+
30
+ meta: AuditMeta = Audits.get_meta()
31
+ clusters = Audits.find_new_clusters(meta.cluster_last_checked)
32
+ Audits.put_meta(Audits.ADD_CLUSTERS, meta, clusters=clusters)
33
+ if clusters:
34
+ log2(f'Added {len(clusters)} new clusters.')
35
+ tables = Config().get('audit.athena.repair-cluster-tables', 'cluster').split(',')
36
+ with concurrent.futures.ThreadPoolExecutor(max_workers=Config().get('audit.workers', 3)) as executor:
37
+ for table in tables:
38
+ Audits.run_audit_query(f'MSCK REPAIR TABLE {table}')
39
+ else:
40
+ log2(f'No new clusters were found.')
41
+
42
+ return state
43
+
44
+ def completion(self, state: ReplState):
45
+ if state.device == ReplState.L:
46
+ return super().completion(state)
47
+
48
+ return {}
49
+
50
+ def help(self, _: ReplState):
51
+ return f"{AuditRun.COMMAND} \t run"
@@ -0,0 +1,150 @@
1
+ from adam.commands.command import Command
2
+ from adam.utils_k8s.cassandra_clusters import CassandraClusters
3
+ from adam.utils_k8s.cassandra_nodes import CassandraNodes
4
+ from adam.pod_exec_result import PodExecResult
5
+ from adam.repl_state import BashSession, ReplState, RequiredState
6
+ from adam.utils_repl.automata_completer import AutomataCompleter
7
+ from adam.utils_repl.state_machine import StateMachine
8
+
9
+ class Bash(Command):
10
+ COMMAND = 'bash'
11
+
12
+ # the singleton pattern
13
+ def __new__(cls, *args, **kwargs):
14
+ if not hasattr(cls, 'instance'): cls.instance = super(Bash, cls).__new__(cls)
15
+
16
+ return cls.instance
17
+
18
+ def __init__(self, successor: Command=None):
19
+ super().__init__(successor)
20
+
21
+ def command(self):
22
+ return Bash.COMMAND
23
+
24
+ def required(self):
25
+ return RequiredState.CLUSTER_OR_POD
26
+
27
+ def run(self, cmd: str, s0: ReplState):
28
+ if not(args := self.args(cmd)):
29
+ return super().run(cmd, s0)
30
+
31
+ state, args = self.apply_state(args, s0, args_to_check=2)
32
+ if not self.validate_state(state):
33
+ return state
34
+
35
+ if state.in_repl:
36
+ if s0.sts != state.sts or s0.pod != state.pod:
37
+ r = self.exec_with_dir(state, args)
38
+ else:
39
+ r = self.exec_with_dir(s0, args)
40
+
41
+ if not r:
42
+ state.exit_bash()
43
+
44
+ return 'inconsistent pwd'
45
+
46
+ return r
47
+ else:
48
+ command = ' '.join(args)
49
+
50
+ if state.pod:
51
+ CassandraNodes.exec(state.pod, state.namespace, command, show_out=True)
52
+ elif state.sts:
53
+ CassandraClusters.exec(state.sts, state.namespace, command, action='bash', show_out=True)
54
+
55
+ return state
56
+
57
+ def exec_with_dir(self, state: ReplState, args: list[str]) -> list[PodExecResult]:
58
+ session_just_created = False
59
+ if not args:
60
+ session_just_created = True
61
+ session = BashSession(state.device)
62
+ state.enter_bash(session)
63
+
64
+ if state.bash_session:
65
+ if args != ['pwd']:
66
+ if args:
67
+ args.append('&&')
68
+ args.extend(['pwd', '>', f'/tmp/.qing-{state.bash_session.session_id}'])
69
+
70
+ if not session_just_created:
71
+ if pwd := state.bash_session.pwd(state):
72
+ args = ['cd', pwd, '&&'] + args
73
+
74
+ command = ' '.join(args)
75
+
76
+ rs = []
77
+
78
+ if state.pod:
79
+ rs = [CassandraNodes.exec(state.pod, state.namespace, command,
80
+ show_out=not session_just_created, shell='bash')]
81
+ elif state.sts:
82
+ rs = CassandraClusters.exec(state.sts, state.namespace, command, action='bash',
83
+ show_out=not session_just_created, shell='bash')
84
+
85
+ return rs
86
+
87
+ def completion(self, state: ReplState):
88
+ if state.pod or state.sts:
89
+ return {Bash.COMMAND: AutomataCompleter(BashStateMachine())}
90
+
91
+ return {}
92
+
93
+ def help(self, _: ReplState):
94
+ return f'{Bash.COMMAND} [bash-commands]\t run bash on the Cassandra nodes'
95
+
96
+ BASH_SPEC = [
97
+ # <command> ::= <simple_command> | <pipeline> | <conditional_command>
98
+ # <simple_command> ::= <word> <argument>* <redirection>*
99
+ # <pipeline> ::= <command> '|' <command>
100
+ # <conditional_command> ::= <command> '&&' <command> | <command> '||' <command>
101
+ # <word> ::= <letter> <letter_or_digit>*
102
+ # <argument> ::= <word>
103
+ # <redirection> ::= '>' <filename> | '<' <filename>
104
+ # <filename> ::= <word>
105
+ # <letter> ::= 'a' | 'b' | ... | 'z' | 'A' | 'B' | ... | 'Z'
106
+ # <digit> ::= '0' | '1' | ... | '9'
107
+ # <letter_or_digit> ::= <letter> | <digit>
108
+
109
+ ' > word > word',
110
+ 'word > word > word ^ |,>,2>,<,&,&&,||',
111
+ '- > pipe > word_pipe',
112
+ '- > _rdr0_ > word_rdr0',
113
+ '- > _rdr1_ > word_rdr1',
114
+ '- > _rdr2_ > word_rdr2',
115
+ '- > & > word_bg ^ |,>,2>,<,&,&&,||',
116
+ '- > &&|_or_ > word',
117
+ 'word_a > word > word',
118
+ 'word_pipe > word > word',
119
+ 'word_rdr0 > word > word_rdr0_f',
120
+ 'word_rdr1 > word > word_rdr1_f',
121
+ 'word_rdr2 > word > word_rdr2_f',
122
+ 'word_rdr1_f > pipe > word_pipe ^ |,2>,<,&,&&,||',
123
+ '- > _rdr2_ > word_rdr2',
124
+ '- > _rdr0_ > word_rdr0',
125
+ 'word_rdr2_f > pipe > word_pipe ^ |,<,&,&&,||',
126
+ '- > _rdr0_ > word_rdr0',
127
+ '- > & > word_bg ^ |,>,2>,<,&,&&,||',
128
+ '- > &&|_or_ > word',
129
+ 'word_rdr0_f > pipe > word_pipe ^ |,&,&&,||',
130
+ '- > & > word_bg ^ |,>,2>,<,&,&&,||',
131
+ '- > &&|_or_ > word',
132
+ 'word_bg > &&|_or_ > ^ &&,||',
133
+ ]
134
+
135
+ BASH_KEYWORDS = [
136
+ '&',
137
+ '&&',
138
+ '|',
139
+ '||',
140
+ '>',
141
+ '2>',
142
+ '>>',
143
+ '<'
144
+ ]
145
+ class BashStateMachine(StateMachine[str]):
146
+ def spec(self) -> str:
147
+ return BASH_SPEC
148
+
149
+ def keywords(self) -> list[str]:
150
+ return BASH_KEYWORDS
@@ -14,7 +14,7 @@ from adam.pod_exec_result import PodExecResult
14
14
  from adam.repl_state import ReplState
15
15
  from adam.utils import lines_to_tabular, log, log2
16
16
  from adam.apps import Apps
17
- from adam.utils_athena import audit_table_names
17
+ from adam.utils_audits import Audits
18
18
 
19
19
  class Ls(Command):
20
20
  COMMAND = 'ls'
@@ -131,7 +131,7 @@ class Ls(Command):
131
131
  log(lines_to_tabular(pg_table_names(pg.namespace, pg.path()), 'NAME', separator=','))
132
132
 
133
133
  def show_audit_log_tables(self):
134
- log(lines_to_tabular(audit_table_names(), 'NAME', separator=','))
134
+ log(lines_to_tabular(Audits.audit_table_names(), 'NAME', separator=','))
135
135
 
136
136
  def completion(self, state: ReplState):
137
137
  if state.pod:
@@ -4,7 +4,7 @@ from adam.commands.postgres.postgres_context import PostgresContext
4
4
  from adam.config import Config
5
5
  from adam.repl_state import ReplState, RequiredState
6
6
  from adam.utils import lines_to_tabular, log, log2
7
- from adam.utils_athena import audit_table_names, run_audit_query
7
+ from adam.utils_audits import Audits
8
8
 
9
9
  class PreviewTable(Command):
10
10
  COMMAND = 'preview'
@@ -43,7 +43,7 @@ class PreviewTable(Command):
43
43
  lines = [db["name"] for db in pg.tables() if db["schema"] == PostgresContext.default_schema()]
44
44
  log(lines_to_tabular(lines, separator=','))
45
45
  elif state.device == ReplState.L:
46
- log(lines_to_tabular(audit_table_names(), separator=','))
46
+ log(lines_to_tabular(Audits.audit_table_names(), separator=','))
47
47
  else:
48
48
  log(lines_to_tabular(cassandra_table_names(state), separator=','))
49
49
 
@@ -66,7 +66,7 @@ class PreviewTable(Command):
66
66
  if state.device == ReplState.P:
67
67
  PostgresContext.apply(state.namespace, state.pg_path).run_sql(f'select * from {table} limit {rows}')
68
68
  elif state.device == ReplState.L:
69
- run_audit_query(f'select * from {table} limit {rows}')
69
+ Audits.run_audit_query(f'select * from {table} limit {rows}')
70
70
  else:
71
71
  run_cql(state, f'select * from {table} limit {rows}', show_out=True, use_single_quotes=True, on_any=True)
72
72
 
@@ -0,0 +1,2 @@
1
+ def config():
2
+ return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'audit'}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'medusa': {'restore-auto-complete': False}, 'nodetool': {'workers': 32, 'samples': 3, 'commands_in_line': 40}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops-pg-agent', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing-cloud', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,CPU,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.14', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'a', 'a': {'auto-enter': 'c3/c3'}, 'c': {'auto-enter': 'cluster'}, 'history': {'push-cat-remote-log-file': True}, 'background-process': {'auto-nohup': True}}, 'status': {'columns': 'status,address,load,tokens,owns,host_id,gossip,compactions', 'header': '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS'}, 'storage': {'columns': 'pod,volume_root,volume_cassandra,snapshots,data,compactions', 'header': 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS'}, 'watch': {'auto': 'rollout', 'timeout': 3600, 'interval': 10}, 'debug': False, 'debugs': {'timings': False, 'exit-on-error': False, 'show-parallelism': False}}
@@ -3,6 +3,7 @@ from adam.commands.app import App
3
3
  from adam.commands.app_ping import AppPing
4
4
  from adam.commands.audit.audit import Audit
5
5
  from adam.commands.audit.audit_repair_tables import AuditRepairTables
6
+ from adam.commands.audit.audit_run import AuditRun
6
7
  from adam.commands.deploy.code_start import CodeStart
7
8
  from adam.commands.deploy.code_stop import CodeStop
8
9
  from adam.commands.deploy.deploy import Deploy
@@ -57,7 +58,7 @@ class ReplCommands:
57
58
  cmds: list[Command] = ReplCommands.navigation() + ReplCommands.cassandra_check() + ReplCommands.cassandra_ops() + \
58
59
  ReplCommands.tools() + ReplCommands.app() + ReplCommands.exit()
59
60
 
60
- intermediate_cmds: list[Command] = [App(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
61
+ intermediate_cmds: list[Command] = [App(), Audit(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
61
62
  ic = [c.command() for c in intermediate_cmds]
62
63
  # 1. dedup commands
63
64
  deduped = []
@@ -85,7 +86,7 @@ class ReplCommands:
85
86
 
86
87
  def tools() -> list[Command]:
87
88
  return [Cqlsh(), Postgres(), Bash(), Shell(), CodeStart(), CodeStop(), DeployFrontend(), UndeployFrontend(),
88
- DeployPod(), UndeployPod(), DeployPgAgent(), UndeployPgAgent(), AuditRepairTables(), Audit()]
89
+ DeployPod(), UndeployPod(), DeployPgAgent(), UndeployPgAgent(), Audit()] + Audit.cmd_list()
89
90
 
90
91
  def app() -> list[Command]:
91
92
  return [ShowAppActions(), ShowAppId(), ShowAppQueues(), AppPing(), App()]
@@ -1,16 +1,33 @@
1
1
  from typing import Callable
2
2
 
3
- from adam.sql.automata_completer import AutomataCompleter
3
+ import sqlparse
4
+ from sqlparse.sql import Statement, Token
5
+
6
+ from adam.sql.term_completer import TermCompleter
7
+ from adam.utils_repl.automata_completer import AutomataCompleter
4
8
  from adam.sql.sql_state_machine import AthenaStateMachine, CqlStateMachine, SqlStateMachine
9
+ from adam.utils_repl.state_machine import State
5
10
 
6
11
  __all__ = [
7
12
  "SqlCompleter",
8
13
  ]
9
14
 
10
- def default_columns(tables: list[str]):
15
+ def default_columns(_: list[str]):
11
16
  return 'id,x.,y.,z.'.split(',')
12
17
 
13
- class SqlCompleter(AutomataCompleter):
18
+ class SqlCompleter(AutomataCompleter[Token]):
19
+ def tokens(self, text: str) -> list[Token]:
20
+ tokens = []
21
+
22
+ stmts = sqlparse.parse(text)
23
+ if not stmts:
24
+ tokens = []
25
+ else:
26
+ statement: Statement = stmts[0]
27
+ tokens = statement.tokens
28
+
29
+ return tokens
30
+
14
31
  def __init__(self,
15
32
  tables: Callable[[], list[str]],
16
33
  dml: str = None,
@@ -33,7 +50,17 @@ class SqlCompleter(AutomataCompleter):
33
50
  self.variant = variant
34
51
  self.debug = debug
35
52
 
36
- def terms(self, state: str, word: str) -> list[str]:
53
+ def suggestions_completer(self, state: State, suggestions: str) -> list[str]:
54
+ if not suggestions:
55
+ return None
56
+
57
+ terms = []
58
+ for suggestion in suggestions.split(','):
59
+ terms.extend(self._terms(state, suggestion))
60
+
61
+ return TermCompleter(terms)
62
+
63
+ def _terms(self, state: State, word: str) -> list[str]:
37
64
  terms = []
38
65
 
39
66
  if word == 'tables':
@@ -1,4 +1,8 @@
1
- from adam.sql.state_machine import StateMachine
1
+ from typing import Callable
2
+ from sqlparse.sql import Token
3
+ from sqlparse import tokens as TOKEN
4
+
5
+ from adam.utils_repl.state_machine import StateMachine, State
2
6
 
3
7
  __all__ = [
4
8
  'SqlStateMachine', 'CqlStateMachine', 'AthenaStateMachine'
@@ -70,7 +74,7 @@ SQL_SPEC = [
70
74
  'select_from_lp_ > select > select',
71
75
  'select_from_x > , > select_from_x_comma_ ^ (select,tables',
72
76
  'select_from_sq_ > as > select_from_x_as ^ as',
73
- 'select_from_x_comma_ > name > select_from_x ^ tables',
77
+ 'select_from_x_comma_ > name|audit > select_from_x ^ tables',
74
78
  'select_from_x_ ^ as,where,inner join,left outer join,right outer join,full outer join,group by,order by,limit',
75
79
  'select_from_x_as_x_ > , > select_from_x_comma_ ^ where,inner join,left outer join,right outer join,full outer join,group by,order by,limit',
76
80
  '- > as > select_from_x_as',
@@ -92,7 +96,7 @@ SQL_SPEC = [
92
96
  '- > full outer join > select_join',
93
97
  'select_from_x_as_ > name > select_from_x_as_x ^ x,y,z',
94
98
  'select_from_x_as_x > , > select_from_x_as_x_comma_',
95
- 'select_from_x_as_x_comma_ > name > select_from_x ^ tables',
99
+ 'select_from_x_as_x_comma_ > name|audit > select_from_x ^ tables',
96
100
  'select_where_ > name > select_where_a ^ columns',
97
101
  'select_where_a > name > select_where_a ^ columns,=,<,<=,>,>=,<>',
98
102
  '- > comparison > select_where_a_op',
@@ -134,7 +138,7 @@ SQL_SPEC = [
134
138
  'select_where_sc_limit_ > num > select_where_sc_limit_num ^ 1',
135
139
  'select_where_sc_limit_num_rp__ > as > select_from_x_as ^ as',
136
140
  'select_where_x_inner_ > join > select_join',
137
- 'select_join_ > name > select_x_join_y ^ tables',
141
+ 'select_join_ > name|audit > select_x_join_y ^ tables',
138
142
  'select_from_x_left_ > join > select_join ^ outer join',
139
143
  '- > outer > select_from_x_left_outer',
140
144
  'select_from_x_left_outer_ > join > select_join ^ join',
@@ -171,7 +175,7 @@ SQL_SPEC = [
171
175
  # <query_expression> ::= SELECT <select_list> FROM <table_reference_list> [ WHERE <search_condition> ] [ GROUP BY <grouping_column_list> ] [ HAVING <search_condition> ] [ ORDER BY <sort_specification_list> ]
172
176
  ' > insert > insert',
173
177
  'insert_ > into > insert_into ^ into',
174
- 'insert_into_ > name > insert_into_x ^ tables',
178
+ 'insert_into_ > name|audit > insert_into_x ^ tables',
175
179
  'insert_into_x > ( > insert_into_x_lp_',
176
180
  'insert_into_x_ > ( > insert_into_x_lp_ ^ (,values(',
177
181
  '- > values > insert_values',
@@ -198,7 +202,7 @@ SQL_SPEC = [
198
202
 
199
203
  # <search_condition> ::= <boolean_expression>
200
204
  ' > update > update',
201
- 'update_ > name > update_x ^ tables',
205
+ 'update_ > name|audit > update_x ^ tables',
202
206
  'update_x_ > set > update_set ^ set',
203
207
  'update_set_ > name > update_set_a ^ id',
204
208
  'update_set_a > comparison > update_set_a_op',
@@ -267,7 +271,7 @@ SQL_SPEC = [
267
271
  # <expression_list> ::= <expression> { , <expression> }...
268
272
  ' > delete > delete',
269
273
  'delete_ > from > delete_from ^ from',
270
- 'delete_from_ > name > delete_from_x ^ tables',
274
+ 'delete_from_ > name|audit > delete_from_x ^ tables',
271
275
  'delete_from_x_ > where > update_where ^ where',
272
276
 
273
277
  # <alter table action> ::=
@@ -381,10 +385,90 @@ ATHENA_KEYWORDS = SQL_KEYWORDS + [
381
385
  'partition'
382
386
  ]
383
387
 
384
- class SqlStateMachine(StateMachine):
388
+ class SqlStateMachine(StateMachine[Token]):
385
389
  def __init__(self, indent=0, push_level = 0, debug = False):
386
390
  super().__init__(indent, push_level, debug)
387
391
 
392
+ def traverse_tokens(self, tokens: list[Token], state: State = State('')):
393
+ def handle_push():
394
+ if f'{state.state} > {it}' in self.states:
395
+ state_test = self.states[f'{state.state} > {it}']
396
+ if state_test.comeback_token:
397
+ self.comebacks[self.push_level] = state_test.comeback_state
398
+
399
+ def handle_pop():
400
+ if self.push_level in self.comebacks:
401
+ try:
402
+ return State(self.comebacks[self.push_level])
403
+ finally:
404
+ del self.comebacks[self.push_level]
405
+
406
+ return None
407
+
408
+ for token in tokens:
409
+ if self.debug:
410
+ if token.ttype == TOKEN.Whitespace:
411
+ print('_ ', end='')
412
+ elif token.ttype in [TOKEN.DML, TOKEN.Wildcard, TOKEN.Punctuation, TOKEN.CTE]:
413
+ print(f'{token.value} ', end='')
414
+ elif token.ttype:
415
+ tks = str(token.ttype).split('.')
416
+ typ = tks[len(tks) - 1]
417
+ if ' ' in token.value:
418
+ print(f'"{token.value}:{typ}" ', end='')
419
+ else:
420
+ print(f'{token.value}:{typ} ', end='')
421
+ # print(" " * self.indent + f"Token: {token.value}, Type: {token.ttype}@{token.ttype.__class__}")
422
+
423
+ last_name = None
424
+ if token.is_group:
425
+ state = self.traverse_tokens(token.tokens, state)
426
+ else:
427
+ comeback_state = None
428
+
429
+ it = ''
430
+ if (t := token.value.lower()) in self.keywords():
431
+ it = t
432
+ elif token.ttype == TOKEN.Text.Whitespace:
433
+ it = '_'
434
+ elif token.ttype == TOKEN.Name:
435
+ it = 'name'
436
+ last_name = token.value
437
+ elif token.ttype == TOKEN.Literal.String.Single:
438
+ it = 'single'
439
+ elif token.ttype in [TOKEN.Literal.Number.Integer, TOKEN.Literal.Number.Float]:
440
+ it = 'num'
441
+ elif token.ttype == TOKEN.Wildcard:
442
+ it = '*'
443
+ elif token.ttype == TOKEN.Punctuation:
444
+ it = token.value
445
+
446
+ if it == '(':
447
+ handle_push()
448
+ self.push_level += 1
449
+ elif it == ')':
450
+ self.push_level -= 1
451
+ comeback_state = handle_pop()
452
+
453
+ elif token.ttype == TOKEN.Operator.Comparison:
454
+ it = 'comparison'
455
+
456
+ try:
457
+ # print(f'\n{state.to_s} > {it} > ', end='')
458
+ if comeback_state:
459
+ state = comeback_state
460
+ else:
461
+ context = state.context
462
+ state = self.states[f'{state.state} > {it}']
463
+ state.context = context
464
+
465
+ if last_name:
466
+ state.context['last_name'] = last_name
467
+ except:
468
+ pass
469
+
470
+ return state
471
+
388
472
  def spec(self):
389
473
  return SQL_SPEC
390
474