views.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. #!/usr/bin/env python
  2. # Licensed to Cloudera, Inc. under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. Cloudera, Inc. licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. import json
  18. import logging
  19. import urllib
  20. from django.db.models import Q
  21. from django.urls import reverse
  22. from django.shortcuts import redirect
  23. from django.utils.functional import wraps
  24. from django.utils.translation import ugettext as _
  25. from django.views.decorators.http import require_http_methods
  26. from desktop.context_processors import get_app_name
  27. from desktop.lib.django_util import JsonResponse, render
  28. from desktop.lib.exceptions_renderable import PopupException
  29. from desktop.models import Document2, get_cluster_config
  30. from beeswax.design import hql_query
  31. from beeswax.models import SavedQuery
  32. from beeswax.server import dbms
  33. from beeswax.server.dbms import get_query_server_config
  34. from desktop.lib.view_util import location_to_url
  35. from metadata.conf import has_optimizer, has_navigator, get_optimizer_url, get_navigator_url
  36. from notebook.connectors.base import Notebook, QueryError
  37. from notebook.models import make_notebook
  38. from metastore.conf import FORCE_HS2_METADATA
  39. from metastore.forms import LoadDataForm, DbForm
  40. from metastore.settings import DJANGO_APPS
  41. LOG = logging.getLogger(__name__)
  42. SAVE_RESULTS_CTAS_TIMEOUT = 300 # seconds
  43. def check_has_write_access_permission(view_func):
  44. """
  45. Decorator ensuring that the user is not a read only user.
  46. """
  47. def decorate(request, *args, **kwargs):
  48. if not has_write_access(request.user):
  49. raise PopupException(_('You are not allowed to modify the metastore.'), detail=_('You have must have metastore:write permissions'), error_code=301)
  50. return view_func(request, *args, **kwargs)
  51. return wraps(view_func)(decorate)
  52. def index(request):
  53. return redirect(reverse('metastore:show_tables', kwargs={'database': ''}))
  54. """
  55. Database Views
  56. """
  57. def databases(request):
  58. search_filter = request.GET.get('filter', '')
  59. db = _get_db(user=request.user)
  60. databases = db.get_databases(search_filter)
  61. return render("metastore.mako", request, {
  62. 'breadcrumbs': [],
  63. 'database': None,
  64. 'databases': databases,
  65. 'partitions': [],
  66. 'has_write_access': has_write_access(request.user),
  67. 'is_optimizer_enabled': has_optimizer(),
  68. 'is_navigator_enabled': has_navigator(request.user),
  69. 'optimizer_url': get_optimizer_url(),
  70. 'navigator_url': get_navigator_url(),
  71. 'is_embeddable': request.GET.get('is_embeddable', False),
  72. 'source_type': _get_servername(db),
  73. })
  74. @check_has_write_access_permission
  75. def drop_database(request):
  76. source_type = request.POST.get('source_type', 'hive')
  77. db = _get_db(user=request.user, source_type=source_type)
  78. if request.method == 'POST':
  79. databases = request.POST.getlist('database_selection')
  80. try:
  81. if request.POST.get('is_embeddable'):
  82. design = SavedQuery.create_empty(app_name=source_type if source_type != 'hive' else 'beeswax', owner=request.user, data=hql_query('').dumps())
  83. last_executed = json.loads(request.POST.get('start_time'), '-1')
  84. sql = db.drop_databases(databases, design, generate_ddl_only=True)
  85. job = make_notebook(
  86. name=_('Drop database %s') % ', '.join(databases)[:100],
  87. editor_type=source_type,
  88. statement=sql.strip(),
  89. status='ready',
  90. database=None,
  91. on_success_url='assist.db.refresh',
  92. is_task=True,
  93. last_executed=last_executed
  94. )
  95. return JsonResponse(job.execute(request))
  96. else:
  97. design = SavedQuery.create_empty(app_name='beeswax', owner=request.user, data=hql_query('').dumps())
  98. query_history = db.drop_databases(databases, design)
  99. url = reverse('beeswax:watch_query_history', kwargs={'query_history_id': query_history.id}) + '?on_success_url=' + reverse('metastore:databases')
  100. return redirect(url)
  101. except Exception, ex:
  102. error_message, log = dbms.expand_exception(ex, db)
  103. error = _("Failed to remove %(databases)s. Error: %(error)s") % {'databases': ','.join(databases), 'error': error_message}
  104. raise PopupException(error, title=_("DB Error"), detail=log)
  105. else:
  106. title = _("Do you really want to delete the database(s)?")
  107. return render('confirm.mako', request, {'url': request.path, 'title': title})
  108. @check_has_write_access_permission
  109. @require_http_methods(["POST"])
  110. def alter_database(request, database):
  111. response = {'status': -1, 'data': ''}
  112. source_type = request.POST.get('source_type', 'hive')
  113. db = _get_db(user=request.user, source_type=source_type)
  114. try:
  115. properties = request.POST.get('properties')
  116. if not properties:
  117. raise PopupException(_("Alter database requires a properties value of key-value pairs."))
  118. properties = json.loads(properties)
  119. db.alter_database(database, properties=properties)
  120. db_metadata = db.get_database(database)
  121. db_metadata['hdfs_link'] = location_to_url(db_metadata['location'])
  122. response['status'] = 0
  123. response['data'] = db_metadata
  124. except Exception, ex:
  125. response['status'] = 1
  126. response['data'] = _("Failed to alter database `%s`: %s") % (database, ex)
  127. return JsonResponse(response)
  128. def get_database_metadata(request, database, cluster=None):
  129. response = {'status': -1, 'data': ''}
  130. source_type = request.POST.get('source_type', 'hive')
  131. db = _get_db(user=request.user, source_type=source_type, cluster=cluster)
  132. try:
  133. db_metadata = db.get_database(database)
  134. response['status'] = 0
  135. db_metadata['hdfs_link'] = location_to_url(db_metadata['location'])
  136. response['data'] = db_metadata
  137. except Exception, ex:
  138. response['status'] = 1
  139. response['data'] = _("Cannot get metadata for database %s: %s") % (database, ex)
  140. return JsonResponse(response)
  141. def table_queries(request, database, table):
  142. qfilter = Q(data__icontains=table) | Q(data__icontains='%s.%s' % (database, table))
  143. response = {'status': -1, 'queries': []}
  144. try:
  145. queries = [{'doc': d.to_dict(), 'data': Notebook(document=d).get_data()}
  146. for d in Document2.objects.filter(qfilter, owner=request.user, type='query', is_history=False)[:50]]
  147. response['status'] = 0
  148. response['queries'] = queries
  149. except Exception, ex:
  150. response['status'] = 1
  151. response['data'] = _("Cannot get queries related to table %s.%s: %s") % (database, table, ex)
  152. return JsonResponse(response)
  153. """
  154. Table Views
  155. """
  156. def show_tables(request, database=None):
  157. db = _get_db(user=request.user)
  158. if database is None:
  159. database = 'default' # Assume always 'default'
  160. if request.GET.get("format", "html") == "json":
  161. try:
  162. databases = db.get_databases()
  163. if database not in databases:
  164. database = 'default'
  165. if request.method == 'POST':
  166. db_form = DbForm(request.POST, databases=databases)
  167. if db_form.is_valid():
  168. database = db_form.cleaned_data['database']
  169. else:
  170. db_form = DbForm(initial={'database': database}, databases=databases)
  171. search_filter = request.GET.get('filter', '')
  172. tables = db.get_tables_meta(database=database, table_names=search_filter) # SparkSql returns []
  173. table_names = [table['name'] for table in tables]
  174. except Exception, e:
  175. raise PopupException(_('Failed to retrieve tables for database: %s' % database), detail=e)
  176. resp = JsonResponse({
  177. 'status': 0,
  178. 'database_meta': db.get_database(database),
  179. 'tables': tables,
  180. 'table_names': table_names,
  181. 'search_filter': search_filter
  182. })
  183. else:
  184. resp = render("metastore.mako", request, {
  185. 'breadcrumbs': [],
  186. 'database': None,
  187. 'partitions': [],
  188. 'has_write_access': has_write_access(request.user),
  189. 'is_optimizer_enabled': has_optimizer(),
  190. 'is_navigator_enabled': has_navigator(request.user),
  191. 'optimizer_url': get_optimizer_url(),
  192. 'navigator_url': get_navigator_url(),
  193. 'is_embeddable': request.GET.get('is_embeddable', False),
  194. 'source_type': _get_servername(db),
  195. })
  196. return resp
  197. def get_table_metadata(request, database, table):
  198. db = _get_db(user=request.user)
  199. response = {'status': -1, 'data': ''}
  200. try:
  201. table_metadata = db.get_table(database, table)
  202. response['status'] = 0
  203. response['data'] = {
  204. 'comment': table_metadata.comment,
  205. 'hdfs_link': table_metadata.hdfs_link,
  206. 'is_view': table_metadata.is_view
  207. }
  208. except:
  209. msg = "Cannot get metadata for table: `%s`.`%s`"
  210. LOG.exception(msg) % (database, table)
  211. response['status'] = 1
  212. response['data'] = _(msg) % (database, table)
  213. return JsonResponse(response)
  214. def describe_table(request, database, table):
  215. app_name = get_app_name(request)
  216. cluster = request.GET.get('cluster')
  217. db = _get_db(user=request.user, cluster=cluster)
  218. try:
  219. table = db.get_table(database, table)
  220. except Exception, e:
  221. LOG.exception("Describe table error")
  222. raise PopupException(_("DB Error"), detail=e.message if hasattr(e, 'message') and e.message else e)
  223. if request.GET.get("format", "html") == "json":
  224. return JsonResponse({
  225. 'status': 0,
  226. 'name': table.name,
  227. 'partition_keys': [{'name': part.name, 'type': part.type} for part in table.partition_keys],
  228. 'cols': [{'name': col.name, 'type': col.type, 'comment': col.comment} for col in table.cols],
  229. 'path_location': table.path_location,
  230. 'hdfs_link': table.hdfs_link,
  231. 'comment': table.comment,
  232. 'is_view': table.is_view,
  233. 'properties': table.properties,
  234. 'details': table.details,
  235. 'stats': table.stats
  236. })
  237. else: # Render HTML
  238. renderable = "metastore.mako"
  239. partitions = None
  240. if app_name != 'impala' and table.partition_keys:
  241. try:
  242. partitions = [_massage_partition(database, table, partition) for partition in db.get_partitions(database, table)]
  243. except:
  244. LOG.exception('Table partitions could not be retrieved')
  245. return render(renderable, request, {
  246. 'breadcrumbs': [{
  247. 'name': database,
  248. 'url': reverse('metastore:show_tables', kwargs={'database': database})
  249. }, {
  250. 'name': str(table.name),
  251. 'url': reverse('metastore:describe_table', kwargs={'database': database, 'table': table.name})
  252. },
  253. ],
  254. 'table': table,
  255. 'partitions': partitions,
  256. 'database': database,
  257. 'has_write_access': has_write_access(request.user),
  258. 'is_optimizer_enabled': has_optimizer(),
  259. 'is_navigator_enabled': has_navigator(request.user),
  260. 'optimizer_url': get_optimizer_url(),
  261. 'navigator_url': get_navigator_url(),
  262. 'is_embeddable': request.GET.get('is_embeddable', False),
  263. 'source_type': _get_servername(db),
  264. })
  265. @check_has_write_access_permission
  266. @require_http_methods(["POST"])
  267. def alter_table(request, database, table):
  268. response = {'status': -1, 'data': ''}
  269. source_type = request.POST.get('source_type', 'hive')
  270. db = _get_db(user=request.user, source_type=source_type)
  271. try:
  272. new_table_name = request.POST.get('new_table_name', None)
  273. comment = request.POST.get('comment', None)
  274. # Cannot modify both name and comment at same time, name will get precedence
  275. if new_table_name and comment:
  276. LOG.warn('Cannot alter both table name and comment at the same time, will perform rename.')
  277. table_obj = db.alter_table(database, table, new_table_name=new_table_name, comment=comment)
  278. response['status'] = 0
  279. response['data'] = {
  280. 'name': table_obj.name,
  281. 'comment': table_obj.comment,
  282. 'is_view': table_obj.is_view,
  283. 'location': table_obj.path_location,
  284. 'properties': table_obj.properties
  285. }
  286. except Exception, ex:
  287. response['status'] = 1
  288. response['data'] = _("Failed to alter table `%s`.`%s`: %s") % (database, table, str(ex))
  289. return JsonResponse(response)
  290. @check_has_write_access_permission
  291. @require_http_methods(["POST"])
  292. def alter_column(request, database, table):
  293. response = {'status': -1, 'message': ''}
  294. source_type = request.POST.get('source_type', 'hive')
  295. db = _get_db(user=request.user, source_type=source_type)
  296. try:
  297. column = request.POST.get('column', None)
  298. if column is None:
  299. raise PopupException(_('alter_column requires a column parameter'))
  300. column_obj = db.get_column(database, table, column)
  301. if column_obj:
  302. new_column_name = request.POST.get('new_column_name', column_obj.name)
  303. new_column_type = request.POST.get('new_column_type', column_obj.type)
  304. comment = request.POST.get('comment', None)
  305. partition_spec = request.POST.get('partition_spec', None)
  306. column_obj = db.alter_column(database, table, column, new_column_name, new_column_type, comment=comment, partition_spec=partition_spec)
  307. response['status'] = 0
  308. response['data'] = {
  309. 'name': column_obj.name,
  310. 'type': column_obj.type,
  311. 'comment': column_obj.comment
  312. }
  313. else:
  314. raise PopupException(_('Column `%s`.`%s` `%s` not found') % (database, table, column))
  315. except Exception, ex:
  316. response['status'] = 1
  317. response['message'] = _("Failed to alter column `%s`.`%s` `%s`: %s") % (database, table, column, str(ex))
  318. return JsonResponse(response)
  319. @check_has_write_access_permission
  320. def drop_table(request, database):
  321. source_type = request.POST.get('source_type', 'hive')
  322. db = _get_db(user=request.user, source_type=source_type)
  323. if request.method == 'POST':
  324. try:
  325. tables = request.POST.getlist('table_selection')
  326. tables_objects = [db.get_table(database, table) for table in tables]
  327. skip_trash = request.POST.get('skip_trash') == 'on'
  328. if request.POST.get('is_embeddable'):
  329. last_executed = json.loads(request.POST.get('start_time'), '-1')
  330. sql = db.drop_tables(database, tables_objects, design=None, skip_trash=skip_trash, generate_ddl_only=True)
  331. job = make_notebook(
  332. name=_('Drop table %s') % ', '.join([table.name for table in tables_objects])[:100],
  333. editor_type=source_type,
  334. statement=sql.strip(),
  335. status='ready',
  336. database=database,
  337. on_success_url='assist.db.refresh',
  338. is_task=True,
  339. last_executed=last_executed
  340. )
  341. return JsonResponse(job.execute(request))
  342. else:
  343. # Can't be simpler without an important refactoring
  344. design = SavedQuery.create_empty(app_name='beeswax', owner=request.user, data=hql_query('').dumps())
  345. query_history = db.drop_tables(database, tables_objects, design, skip_trash=skip_trash)
  346. url = reverse('beeswax:watch_query_history', kwargs={'query_history_id': query_history.id}) + '?on_success_url=' + reverse('metastore:show_tables', kwargs={'database': database})
  347. return redirect(url)
  348. except Exception, ex:
  349. error_message, log = dbms.expand_exception(ex, db)
  350. error = _("Failed to remove %(tables)s. Error: %(error)s") % {'tables': ','.join(tables), 'error': error_message}
  351. raise PopupException(error, title=_("DB Error"), detail=log)
  352. else:
  353. title = _("Do you really want to delete the table(s)?")
  354. return render('confirm.mako', request, {'url': request.path, 'title': title})
  355. # Deprecated
  356. def read_table(request, database, table):
  357. db = dbms.get(request.user)
  358. table = db.get_table(database, table)
  359. try:
  360. query_history = db.select_star_from(database, table)
  361. url = reverse('beeswax:watch_query_history', kwargs={'query_history_id': query_history.id}) + '?on_success_url=&context=table:%s:%s' % (table.name, database)
  362. return redirect(url)
  363. except Exception, e:
  364. raise PopupException(_('Cannot read table'), detail=e)
  365. @check_has_write_access_permission
  366. def load_table(request, database, table):
  367. response = {'status': -1, 'data': 'None'}
  368. source_type = request.POST.get('source_type', 'hive')
  369. db = _get_db(user=request.user, source_type=source_type)
  370. table = db.get_table(database, table)
  371. if request.method == "POST":
  372. load_form = LoadDataForm(table, request.POST)
  373. if load_form.is_valid():
  374. on_success_url = reverse('metastore:describe_table', kwargs={'database': database, 'table': table.name})
  375. generate_ddl_only = request.POST.get('is_embeddable', 'false') == 'true'
  376. try:
  377. design = SavedQuery.create_empty(app_name=source_type if source_type != 'hive' else 'beeswax', owner=request.user, data=hql_query('').dumps())
  378. form_data = {
  379. 'path': load_form.cleaned_data['path'],
  380. 'overwrite': load_form.cleaned_data['overwrite'],
  381. 'partition_columns': [(column_name, load_form.cleaned_data[key]) for key, column_name in load_form.partition_columns.iteritems()],
  382. }
  383. query_history = db.load_data(database, table.name, form_data, design, generate_ddl_only=generate_ddl_only)
  384. if generate_ddl_only:
  385. last_executed = json.loads(request.POST.get('start_time'), '-1')
  386. job = make_notebook(
  387. name=_('Load data in %s.%s') % (database, table.name),
  388. editor_type=source_type,
  389. statement=query_history.strip(),
  390. status='ready',
  391. database=database,
  392. on_success_url='assist.db.refresh',
  393. is_task=True,
  394. last_executed=last_executed
  395. )
  396. response = job.execute(request)
  397. else:
  398. url = reverse('beeswax:watch_query_history', kwargs={'query_history_id': query_history.id}) + '?on_success_url=' + on_success_url
  399. response['status'] = 0
  400. response['data'] = url
  401. response['query_history_id'] = query_history.id
  402. except QueryError, ex:
  403. response['status'] = 1
  404. response['data'] = _("Can't load the data: ") + ex.message
  405. except Exception, e:
  406. response['status'] = 1
  407. response['data'] = _("Can't load the data: ") + str(e)
  408. else:
  409. load_form = LoadDataForm(table)
  410. if response['status'] == -1:
  411. popup = render('popups/load_data.mako', request, {
  412. 'table': table,
  413. 'load_form': load_form,
  414. 'database': database,
  415. 'app_name': 'beeswax'
  416. }, force_template=True).content
  417. response['data'] = popup
  418. return JsonResponse(response)
  419. def describe_partitions(request, database, table):
  420. db = _get_db(user=request.user)
  421. table_obj = db.get_table(database, table)
  422. if not table_obj.partition_keys:
  423. raise PopupException(_("Table '%(table)s' is not partitioned.") % {'table': table})
  424. reverse_sort = request.GET.get("sort", "desc").lower() == "desc"
  425. if request.method == "POST":
  426. partition_filters = {}
  427. for part in table_obj.partition_keys:
  428. if request.GET.get(part.name):
  429. partition_filters[part.name] = request.GET.get(part.name)
  430. partition_spec = ','.join(["%s='%s'" % (k, v) for k, v in partition_filters.items()])
  431. else:
  432. partition_spec = ''
  433. try:
  434. partitions = db.get_partitions(database, table_obj, partition_spec, reverse_sort=reverse_sort)
  435. except:
  436. LOG.exception('Table partitions could not be retrieved')
  437. partitions = []
  438. massaged_partitions = [_massage_partition(database, table_obj, partition) for partition in partitions]
  439. if request.method == "POST" or request.GET.get('format', 'html') == 'json':
  440. return JsonResponse({
  441. 'partition_keys_json': [partition.name for partition in table_obj.partition_keys],
  442. 'partition_values_json': massaged_partitions,
  443. })
  444. else:
  445. return render("metastore.mako", request, {
  446. 'breadcrumbs': [{
  447. 'name': database,
  448. 'url': reverse('metastore:show_tables', kwargs={'database': database})
  449. }, {
  450. 'name': table,
  451. 'url': reverse('metastore:describe_table', kwargs={'database': database, 'table': table})
  452. },{
  453. 'name': 'partitions',
  454. 'url': reverse('metastore:describe_partitions', kwargs={'database': database, 'table': table})
  455. },
  456. ],
  457. 'database': database,
  458. 'table': table_obj,
  459. 'partitions': partitions,
  460. 'partition_keys_json': json.dumps([partition.name for partition in table_obj.partition_keys]),
  461. 'partition_values_json': json.dumps(massaged_partitions),
  462. 'request': request,
  463. 'has_write_access': has_write_access(request.user),
  464. 'is_optimizer_enabled': has_optimizer(),
  465. 'is_navigator_enabled': has_navigator(request.user),
  466. 'optimizer_url': get_optimizer_url(),
  467. 'navigator_url': get_navigator_url(),
  468. 'is_embeddable': request.GET.get('is_embeddable', False),
  469. 'source_type': _get_servername(db),
  470. })
  471. def _massage_partition(database, table, partition):
  472. return {
  473. 'columns': partition.values,
  474. 'partitionSpec': partition.partition_spec,
  475. 'readUrl': reverse('metastore:read_partition', kwargs={
  476. 'database': database,
  477. 'table': table.name,
  478. 'partition_spec': urllib.quote(partition.partition_spec)
  479. }),
  480. 'browseUrl': reverse('metastore:browse_partition', kwargs={
  481. 'database': database,
  482. 'table': table.name,
  483. 'partition_spec': urllib.quote(partition.partition_spec)
  484. }),
  485. 'notebookUrl': reverse('notebook:browse', kwargs={
  486. 'database': database,
  487. 'table': table.name,
  488. 'partition_spec': urllib.quote(partition.partition_spec)
  489. })
  490. }
  491. def browse_partition(request, database, table, partition_spec):
  492. db = _get_db(user=request.user)
  493. try:
  494. decoded_spec = urllib.unquote(partition_spec)
  495. partition_table = db.describe_partition(database, table, decoded_spec)
  496. uri_path = location_to_url(partition_table.path_location)
  497. if request.GET.get("format", "html") == "json":
  498. return JsonResponse({'uri_path': uri_path})
  499. else:
  500. return redirect(uri_path)
  501. except Exception, e:
  502. raise PopupException(_('Cannot browse partition'), detail=e.message)
  503. # Deprecated
  504. def read_partition(request, database, table, partition_spec):
  505. db = dbms.get(request.user)
  506. try:
  507. decoded_spec = urllib.unquote(partition_spec)
  508. query = db.get_partition(database, table, decoded_spec)
  509. url = reverse('beeswax:watch_query_history', kwargs={'query_history_id': query.id}) + '?on_success_url=&context=table:%s:%s' % (table, database)
  510. return redirect(url)
  511. except Exception, e:
  512. raise PopupException(_('Cannot read partition'), detail=e.message)
  513. @require_http_methods(["GET", "POST"])
  514. @check_has_write_access_permission
  515. def drop_partition(request, database, table):
  516. source_type = request.POST.get('source_type', 'hive')
  517. db = _get_db(user=request.user, source_type=source_type)
  518. if request.method == 'POST':
  519. partition_specs = request.POST.getlist('partition_selection')
  520. partition_specs = [spec for spec in partition_specs]
  521. try:
  522. if request.GET.get("format", "html") == "json":
  523. last_executed = json.loads(request.POST.get('start_time'), '-1')
  524. sql = db.drop_partitions(database, table, partition_specs, design=None, generate_ddl_only=True)
  525. job = make_notebook(
  526. name=_('Drop partition %s') % ', '.join(partition_specs)[:100],
  527. editor_type=source_type,
  528. statement=sql.strip(),
  529. status='ready',
  530. database=None,
  531. on_success_url='assist.db.refresh',
  532. is_task=True,
  533. last_executed=last_executed
  534. )
  535. return JsonResponse(job.execute(request))
  536. else:
  537. design = SavedQuery.create_empty(app_name='beeswax', owner=request.user, data=hql_query('').dumps())
  538. query_history = db.drop_partitions(database, table, partition_specs, design)
  539. url = reverse('beeswax:watch_query_history', kwargs={'query_history_id': query_history.id}) + '?on_success_url=' + \
  540. reverse('metastore:describe_partitions', kwargs={'database': database, 'table': table})
  541. return redirect(url)
  542. except Exception, ex:
  543. error_message, log = dbms.expand_exception(ex, db)
  544. error = _("Failed to remove %(partition)s. Error: %(error)s") % {'partition': '\n'.join(partition_specs), 'error': error_message}
  545. raise PopupException(error, title=_("DB Error"), detail=log)
  546. else:
  547. title = _("Do you really want to delete the partition(s)?")
  548. return render('confirm.mako', request, {'url': request.path, 'title': title})
  549. def has_write_access(user):
  550. return user.is_superuser or user.has_hue_permission(action="write", app=DJANGO_APPS[0])
  551. def _get_db(user, source_type=None, cluster=None):
  552. if source_type is None:
  553. cluster_config = get_cluster_config(user)
  554. if FORCE_HS2_METADATA.get() and cluster_config['app_config'].get('editor') and 'hive' in cluster_config['app_config'].get('editor')['interpreter_names']:
  555. source_type = 'hive'
  556. else:
  557. source_type = cluster_config['default_sql_interpreter']
  558. name = source_type if source_type != 'hive' else 'beeswax'
  559. query_server = get_query_server_config(name=name, cluster=cluster)
  560. return dbms.get(user, query_server)
  561. def _get_servername(db):
  562. return 'hive' if db.server_name == 'beeswax' else db.server_name