metastore.mako 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275
  1. ## Licensed to Cloudera, Inc. under one
  2. ## or more contributor license agreements. See the NOTICE file
  3. ## distributed with this work for additional information
  4. ## regarding copyright ownership. Cloudera, Inc. licenses this file
  5. ## to you under the Apache License, Version 2.0 (the
  6. ## "License"); you may not use this file except in compliance
  7. ## with the License. You may obtain a copy of the License at
  8. ##
  9. ## http://www.apache.org/licenses/LICENSE-2.0
  10. ##
  11. ## Unless required by applicable law or agreed to in writing, software
  12. ## distributed under the License is distributed on an "AS IS" BASIS,
  13. ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. ## See the License for the specific language governing permissions and
  15. ## limitations under the License.
  16. <%!
  17. from django.utils.html import escape
  18. from django.utils.translation import ugettext as _
  19. from desktop import conf
  20. from desktop.conf import USE_NEW_EDITOR, IS_EMBEDDED
  21. from desktop.lib.i18n import smart_unicode
  22. from desktop.views import commonheader, commonfooter, _ko
  23. from beeswax.conf import LIST_PARTITIONS_LIMIT
  24. from metastore.conf import ENABLE_NEW_CREATE_TABLE
  25. %>
  26. <%namespace name="actionbar" file="actionbar.mako" />
  27. <%namespace name="assist" file="/assist.mako" />
  28. <%namespace name="components" file="components.mako" />
  29. <%
  30. MAIN_SCROLLABLE = is_embeddable and ".page-content" or ".content-panel"
  31. if conf.CUSTOM.BANNER_TOP_HTML.get():
  32. TOP_SNAP = is_embeddable and "82px" or "108px"
  33. else:
  34. TOP_SNAP = is_embeddable and "52px" or "78px"
  35. %>
  36. % if not is_embeddable:
  37. ${ commonheader(_("Metastore"), app_name, user, request) | n,unicode }
  38. <script src="${ static('desktop/ext/js/jquery/plugins/jquery-ui-1.10.4.custom.min.js') }"></script>
  39. <script src="${ static('desktop/ext/js/jquery/plugins/jquery.mousewheel.min.js') }"></script>
  40. <script src="${ static('desktop/ext/js/selectize.min.js') }"></script>
  41. <script src="${ static('desktop/ext/js/knockout-sortable.min.js') }"></script>
  42. <script src="${ static('desktop/ext/js/bootstrap-editable.min.js') }"></script>
  43. <script src="${ static('desktop/js/ko.editable.js') }"></script>
  44. ${ assist.assistJSModels() }
  45. <link rel="stylesheet" href="${ static('notebook/css/notebook-layout.css') }">
  46. <style type="text/css">
  47. % if conf.CUSTOM.BANNER_TOP_HTML.get():
  48. .show-assist {
  49. top: 110px!important;
  50. }
  51. .main-content {
  52. top: 112px!important;
  53. }
  54. % endif
  55. </style>
  56. ${ assist.assistPanel() }
  57. % endif
  58. <script src="${ static('desktop/ext/js/wysihtml5-0.3.0.min.js') }"></script>
  59. <script src="${ static('desktop/js/bootstrap-wysihtml5-0.0.2.js') }"></script>
  60. <script src="${ static('desktop/ext/js/bootstrap-editable.wysihtml5.js') }"></script>
  61. <script src="${ static('beeswax/js/stats.utils.js') }"></script>
  62. <link rel="stylesheet" href="${ static('desktop/ext/css/bootstrap-editable.css') }">
  63. <link rel="stylesheet" href="${ static('desktop/ext/css/bootstrap-wysihtml5-0.0.2.css') }">
  64. <link rel="stylesheet" href="${ static('notebook/css/notebook.css') }">
  65. <span class="notebook">
  66. ${ components.menubar(is_embeddable) }
  67. <script src="${ static('metastore/js/metastore.ko.js') }"></script>
  68. <link rel="stylesheet" href="${ static('metastore/css/metastore.css') }" type="text/css">
  69. <script type="text/html" id="metastore-breadcrumbs">
  70. <div style="font-size: 14px; margin: 0 12px; line-height: 27px;">
  71. <div data-bind="component: { name: 'hue-drop-down', params: { value: source, entries: sources, onChanged: sourceChanged, labelAttribute: 'name', searchable: true, linkTitle: '${ _ko('Source') }' } }" style="display: inline-block"></div>
  72. <!-- ko with: source -->
  73. <!-- ko if: namespaces().length > 1 || namespaceRefreshEnabled() -->
  74. <div class="margin-left-10" data-bind="component: { name: 'hue-drop-down', params: { value: namespace, entries: namespaces, onChanged: namespaceChanged, labelAttribute: 'name', searchable: true, linkTitle: '${ _ko('Namespace') }' } }" style="display: inline-block"></div>
  75. <!-- /ko -->
  76. <!-- /ko -->
  77. </div>
  78. <ul style="padding-top: 0" class="nav nav-pills hue-breadcrumbs-bar" id="breadcrumbs">
  79. <!-- ko with: source -->
  80. <!-- ko with: namespace -->
  81. <li>
  82. <a href="javascript:void(0);" data-bind="click: $root.databasesBreadcrumb">${_('Databases')}
  83. <!-- ko if: database -->
  84. <span class="divider">&gt;</span>
  85. <!-- /ko -->
  86. </a>
  87. </li>
  88. <!-- ko with: database -->
  89. <li>
  90. <a href="javascript:void(0);" data-bind="click: $root.tablesBreadcrumb">
  91. <span data-bind="text: catalogEntry.name"></span>
  92. <!-- ko with: table -->
  93. <span class="divider">&gt;</span>
  94. <!-- /ko -->
  95. </a>
  96. </li>
  97. <!-- ko with: table -->
  98. <li class="editable-breadcrumbs" title="${_('Edit path')}" data-bind="click: function(){ $parent.editingTable(true); }, visible: !$parent.editingTable()">
  99. <a href="javascript:void(0)" data-bind="text: catalogEntry.name"></a>
  100. </li>
  101. <!-- /ko -->
  102. <!-- ko if: editingTable -->
  103. <!-- ko with: table -->
  104. <li class="editable-breadcrumb-input">
  105. <input type="text" data-bind="hivechooser: { data: catalogEntry.name, database: $parent.catalogEntry.name, namespace: $parent.catalogEntry.namespace, compute: $parent.catalogEntry.compute, skipColumns: true, searchEverywhere: true, onChange: function(val) { $parent.setTableByName(val); $parent.editingTable(false); }, apiHelperUser: '${ user }', apiHelperType: $root.source().type }" autocomplete="off" />
  106. </li>
  107. <!-- /ko -->
  108. <!-- /ko -->
  109. <!-- /ko -->
  110. <!-- /ko -->
  111. <!-- /ko -->
  112. </ul>
  113. </script>
  114. <script type="text/html" id="metastore-partition-columns-table">
  115. <div style="overflow-x: auto; overflow-y: hidden">
  116. <table class="table table-condensed table-nowrap metastore-table">
  117. <thead>
  118. <tr>
  119. <th style="width: 1%">&nbsp;</th>
  120. <th>${_('Name')}</th>
  121. <th>${_('Type')}</th>
  122. </tr>
  123. </thead>
  124. <tbody>
  125. <!-- ko foreach: detailedKeys -->
  126. <tr>
  127. <td data-bind="text: $index() + 1"></td>
  128. <td data-bind="text: $data.name"></td>
  129. <td data-bind="text: $data.type"></td>
  130. </tr>
  131. <!-- /ko -->
  132. </tbody>
  133. </table>
  134. </div>
  135. </script>
  136. <script type="text/html" id="metastore-partition-values-table">
  137. <div style="overflow-x: auto; overflow-y: hidden">
  138. <table id="partitionsTable" class="table table-condensed table-nowrap metastore-table">
  139. <thead>
  140. <tr>
  141. <th style="width: 1%" class="vertical-align-middle">
  142. <!-- ko ifnot: $data.withDrop -->
  143. &nbsp;
  144. <!-- /ko -->
  145. <!-- ko if: $data.withDrop -->
  146. <div class="hue-checkbox fa" data-bind="hueCheckAll: { allValues: $data.values, selectedValues: $data.selectedValues }"></div>
  147. <!-- /ko -->
  148. </th>
  149. <th>${_('Values')}</th>
  150. <th>${_('Spec')}</th>
  151. <th data-bind="visible: $root.source().type !== 'impala'">${_('Browse')}</th>
  152. </tr>
  153. </thead>
  154. <tbody>
  155. <!-- ko foreach: $data.values -->
  156. <tr>
  157. <td>
  158. <!-- ko ifnot: $parent.withDrop -->
  159. <span data-bind="text: $index() + 1"></span>
  160. <!-- /ko -->
  161. <!-- ko if: $parent.withDrop -->
  162. <div class="hue-checkbox fa" data-bind="multiCheck: '#partitionsTable', value: $data, hueChecked: $parent.selectedValues"></div>
  163. <!-- /ko -->
  164. </td>
  165. <td title="${_('Query partition data')}">
  166. <!-- ko if: IS_HUE_4 -->
  167. <a data-bind="click: function() { queryAndWatch(notebookUrl, $root.source().type); }, text: '[\'' + columns.join('\',\'') + '\']'" href="javascript:void(0)"></a>
  168. <!-- /ko -->
  169. <!-- ko if: ! IS_HUE_4 -->
  170. <a data-bind="attr: { 'href': readUrl }, text: '[\'' + columns.join('\',\'') + '\']'"></a>
  171. <!-- /ko -->
  172. </td>
  173. <td data-bind="text: partitionSpec"></td>
  174. <td data-bind="visible: $root.source().type != 'impala'">
  175. <!-- ko if: IS_HUE_4 -->
  176. <a data-bind="click: function () { browsePartitionFolder(browseUrl); }" href="javascript:void(0)" title="${_('Browse partition files')}">
  177. ${_('Files')}
  178. </a>
  179. <!-- /ko -->
  180. <!-- ko if: ! IS_HUE_4 -->
  181. <a data-bind="attr: { 'href': browseUrl }" title="${_('Browse partition files')}">${_('Files')}</a>
  182. <!-- /ko -->
  183. </td>
  184. </tr>
  185. <!-- /ko -->
  186. </tbody>
  187. </table>
  188. </div>
  189. </script>
  190. <script type="text/html" id="metastore-samples-table">
  191. <div data-bind="delayedOverflow">
  192. <table id="sampleTable" class="table table-condensed table-nowrap sample-table old-datatable metastore-table">
  193. <thead>
  194. <tr>
  195. <th style="width: 1%">&nbsp;</th>
  196. <!-- ko foreach: headers -->
  197. <th data-bind="text: $data"></th>
  198. <!-- /ko -->
  199. </tr>
  200. </thead>
  201. <tbody>
  202. <!-- ko foreach: rows -->
  203. <tr>
  204. <td data-bind="text: $index() + 1"></td>
  205. <!-- ko foreach: $data -->
  206. <td data-bind="html: $data"></td>
  207. <!-- /ko -->
  208. </tr>
  209. <!-- /ko -->
  210. </tbody>
  211. </table>
  212. </div>
  213. <div id="jumpToColumnAlert" class="alert hide" style="margin-top: 12px;">
  214. <button type="button" class="close" data-dismiss="alert">&times;</button>
  215. <strong>${_('Did you know?')}</strong>
  216. <ul>
  217. <li>${ _('If the sample contains a large number of columns, click a row to select a column to jump to') }</li>
  218. </ul>
  219. </div>
  220. </script>
  221. <script type="text/html" id="metastore-table-properties">
  222. <!-- ko with: tableDetails -->
  223. <h4>${ _('Properties') } <i data-bind="visible: $parent.loadingDetails()" class="fa fa-spinner fa-spin" style="display: none;"></i></h4>
  224. <div class="row-fluid">
  225. <div class="metastore-property">
  226. <!-- ko if: is_view -->
  227. ${ _('View') }
  228. <!-- /ko -->
  229. <!-- ko ifnot: is_view -->
  230. <span data-bind="visible: partition_keys.length" style="display: none;">
  231. <a class="pointer" data-bind="click: function() { $root.currentTab('partitions'); $('.page-content').scrollTop(0); }">
  232. ${ _("Partitioned") }
  233. </a>
  234. </span>
  235. ${ _('Table') }
  236. <!-- /ko -->
  237. </div>
  238. <!-- ko ifnot: is_view -->
  239. <div class="metastore-property">
  240. <!-- ko if: details.properties.table_type == 'MANAGED_TABLE' -->
  241. ${_('Managed')}
  242. <!-- /ko -->
  243. <!-- ko if: details.properties.table_type == 'EXTERNAL_TABLE' -->
  244. ${_('External')}
  245. <!-- /ko -->
  246. ${_('and stored in')}
  247. <!-- ko if: details.properties.format === 'kudu' -->
  248. <div>${_('Kudu')}</div>
  249. <!-- /ko -->
  250. <!-- ko if: details.properties.format !== 'kudu' -->
  251. <div>
  252. % if IS_EMBEDDED.get():
  253. <span data-bind="attr: {'title': path_location}">${_('location')}</span>
  254. % else:
  255. <a href="javascript: void(0);" data-bind="storageContextPopover: { path: hdfs_link.replace('/filebrowser/view=', ''), offset: { left: 5 } }"> ${_('location')}</a>
  256. % endif
  257. </div>
  258. <!-- /ko -->
  259. </div>
  260. <!-- /ko -->
  261. <div class="metastore-property">
  262. ${ _('Created by') }
  263. <span data-bind="text: details.properties.owner"></span>
  264. ${ _('on') }
  265. <span data-bind="text: localeFormat(details.properties.create_time) != 'Invalid Date' ? localeFormat(details.properties.create_time) : details.properties.create_time"></span>
  266. </div>
  267. </div>
  268. <!-- /ko -->
  269. </script>
  270. <script type="text/html" id="metastore-table-stats">
  271. <!-- ko if: tableDetails() && ! tableDetails().is_view -->
  272. <!-- ko with: tableDetails -->
  273. <h4>${ _('Stats') }
  274. <!-- ko ifnot: partition_keys.length -->
  275. % if has_write_access:
  276. <!-- ko if: $parent.refreshingTableStats -->
  277. <i class="fa fa-refresh fa-spin"></i>
  278. <!-- /ko -->
  279. <!-- ko ifnot: $parent.refreshingTableStats() || is_view -->
  280. <a class="pointer" href="javascript: void(0);" data-bind="click: $parent.refreshTableStats"><i class="fa fa-refresh"></i></a>
  281. <!-- /ko -->
  282. % endif
  283. <span data-bind="visible: details.stats.COLUMN_STATS_ACCURATE == 'false' && ! is_view" rel="tooltip" data-placement="top" title="${ _('The column stats for this table are not accurate') }"><i class="fa fa-exclamation-triangle"></i></span>
  284. <!-- /ko -->
  285. </h4>
  286. <div class="row-fluid">
  287. <!-- ko with: $parent.tableStats -->
  288. <!-- ko if: typeof numFiles !== 'undefined' && typeof last_modified_by === 'undefined' -->
  289. <span class="metastore-property">
  290. <div>${ _('Files') }</div>
  291. <div data-bind="text: numFiles"></div>
  292. </span>
  293. <!-- /ko -->
  294. <!-- ko if: typeof numRows !== 'undefined' -->
  295. <span class="metastore-property">
  296. <div>${ _('Rows') }</div>
  297. <div data-bind="text: numRows"></div>
  298. </span>
  299. <!-- /ko -->
  300. <!-- ko if: typeof totalSize !== 'undefined' && typeof last_modified_by === 'undefined' -->
  301. <span class="metastore-property">
  302. <div>${ _('Total size') }</div>
  303. <div data-bind="text: filesize(totalSize)"></div>
  304. </span>
  305. <!-- /ko -->
  306. <!-- ko if: typeof transient_lastDdlTime !== 'undefined' -->
  307. <div class="metastore-property">
  308. ${ _('Data last updated on') }
  309. <span data-bind="text: localeFormat(transient_lastDdlTime * 1000)"></span>
  310. </div>
  311. <!-- /ko -->
  312. <!-- ko if: typeof last_modified_time !== 'undefined' -->
  313. <div class="metastore-property">
  314. ${ _('Schema last modified on') }
  315. <span data-bind="text: localeFormat(last_modified_time*1000)"></span>
  316. ${ _('by') }
  317. <span data-bind="text: last_modified_by"></span>
  318. </div>
  319. <!-- /ko -->
  320. <!-- /ko -->
  321. </div>
  322. <!-- /ko -->
  323. <!-- /ko -->
  324. </script>
  325. <script type="text/html" id="metastore-databases">
  326. <div class="entries-table-container">
  327. <div class="actionbar-actions">
  328. % if has_write_access:
  329. <button class="btn toolbarBtn margin-left-20" title="${_('Drop the selected databases')}" data-bind="click: function () { $('#dropDatabase').modal('show'); }, disable: selectedDatabases().length === 0"><i class="fa fa-times"></i> ${_('Drop')}</button>
  330. <div id="dropDatabase" class="modal hide fade">
  331. % if is_embeddable:
  332. <form action="/metastore/databases/drop" data-bind="submit: dropAndWatch" method="POST">
  333. <input type="hidden" name="is_embeddable" value="true"/>
  334. <input type="hidden" name="start_time" value=""/>
  335. <input type="hidden" name="source_type" data-bind="value: $root.source().type"/>
  336. % else:
  337. <form id="dropDatabaseForm" action="/metastore/databases/drop" method="POST">
  338. % endif
  339. ${ csrf_token(request) | n,unicode }
  340. <div class="modal-header">
  341. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  342. <h2 id="dropDatabaseMessage" class="modal-title">${ _('Do you really want to delete the following database(s)?') }</h2>
  343. </div>
  344. <div class="modal-body">
  345. <ul data-bind="foreach: selectedDatabases">
  346. <li>
  347. <span data-bind="text: catalogEntry.name"></span>
  348. </li>
  349. </ul>
  350. </div>
  351. <div class="modal-footer">
  352. <div class="label label-important margin-top-5 pull-left">${ _('Warning: This will drop all tables and objects within the database.') }</div>
  353. <input type="button" class="btn" data-dismiss="modal" value="${_('No')}">
  354. <input type="submit" class="btn btn-danger" value="${_('Yes')}"/>
  355. </div>
  356. <!-- ko foreach: selectedDatabases -->
  357. <input type="hidden" name="database_selection" data-bind="value: catalogEntry.name" />
  358. <!-- /ko -->
  359. </form>
  360. </div>
  361. % if is_embeddable:
  362. <button href="javascript: void(0);" class="btn btn-default" data-bind="publish: { 'open.link': '${ url('indexer:importer_prefill', source_type='manual', target_type='database') }' + '/?sourceType=' + catalogEntry.getSourceType() + '&namespace=' + catalogEntry.namespace.id + '&compute=' + catalogEntry.compute.id }" title="${_('Create a new database')}"><i class="fa fa-plus"></i> ${_('New')}</button>
  363. % elif ENABLE_NEW_CREATE_TABLE.get():
  364. <button class="btn btn-default" data-bind="attr: { 'href': '${ url('indexer:importer_prefill', source_type='manual', target_type='database') }' + '/?sourceType=' + catalogEntry.getSourceType() + '&namespace=' + catalogEntry.namespace.id + '&compute=' + catalogEntry.compute.id }" title="${_('Create a new database')}"><i class="fa fa-plus"></i> ${_('New')}</button>
  365. % else:
  366. <button href="${ url('beeswax:create_database') }" class="btn btn-default" title="${_('Create a new database')}"><i class="fa fa-plus"></i> ${_('New')}</button>
  367. % endif
  368. % endif
  369. </div>
  370. <div class="entries-table-no-header">
  371. <!-- ko if: catalogEntry() -->
  372. <!-- ko component: { name: 'catalog-entries-list', params: { catalogEntry: catalogEntry(), contextPopoverEnabled: true, onClick: onDatabaseClick.bind($data), selectedEntries: selectedDatabases, editableDescriptions: /true/i.test('${ has_write_access }') } } --><!-- /ko -->
  373. <!-- /ko -->
  374. </div>
  375. </div>
  376. </script>
  377. <script type="text/html" id="metastore-td-description">
  378. % if has_write_access:
  379. <div data-bind="visibleOnHover: { selector: '.editable-inline-action' }">
  380. <div data-bind="editable: comment, editableOptions: {
  381. mode: 'inline',
  382. enabled: true,
  383. type: 'textarea',
  384. showbuttons: 'bottom',
  385. inputclass: 'hue-table-browser-desc-input',
  386. toggle: 'manual',
  387. toggleElement: '.toggle-editable',
  388. placeholder: '${ _ko('Add a description...') }',
  389. emptytext: '${ _ko('Add a description...') }',
  390. inputclass: 'hue-table-browser-desc-input',
  391. rows: 6,
  392. inlineEditAction: { editClass: 'toggle-editable editable-inline-action' },
  393. multiLineEllipsis: { overflowHeight: '40px', expandable: true, expandActionClass: 'editable-inline-action' }
  394. }">
  395. ${ _('Add a description...') }</div>
  396. </div>
  397. % else:
  398. <span style="white-space: pre;" data-bind="text: comment"></span>
  399. % endif
  400. </script>
  401. <script type="text/html" id="metastore-main-description">
  402. % if has_write_access:
  403. <!-- ko if: $root.navigatorEnabled() -->
  404. <div class="hue-table-browser-desc-container" data-bind="visibleOnHover: { selector: '.editable-inline-action' }">
  405. <div class="hue-table-browser-desc">
  406. <div data-bind="editable: comment, editableOptions: {
  407. mode: 'inline',
  408. enabled: true,
  409. type: 'textarea',
  410. showbuttons: 'bottom',
  411. inputclass: 'hue-table-browser-desc-input',
  412. toggle: 'manual',
  413. toggleElement: '.toggle-editable',
  414. placeholder: '${ _ko('Add a description...') }',
  415. emptytext: '${ _ko('No description available') }',
  416. rows: 8,
  417. inlineEditAction: { editClass: 'toggle-editable editable-inline-action' },
  418. multiLineEllipsis: { overflowHeight: '120px', expandable: true, expandActionClass: 'editable-inline-action', inlineEditAction: true },
  419. }" class="inline-block">
  420. ${ _('Add a description...') }
  421. </div>
  422. </div>
  423. </div>
  424. <!-- /ko -->
  425. <!-- ko ifnot: $root.navigatorEnabled() -->
  426. <div data-bind="text: comment, attr: { title: comment }" class="table-description"></div>
  427. <!-- /ko -->
  428. % else:
  429. <div data-bind="text: comment, attr: { title: comment }" class="table-description"></div>
  430. %endif
  431. </script>
  432. <script type="text/html" id="metastore-nav-tags">
  433. <!-- ko if: $root.navigatorEnabled() -->
  434. <div class="metastore-nav-tags" data-bind="component: { name: 'nav-tags', params: { catalogEntry: catalogEntry }}"></div>
  435. <!-- /ko -->
  436. </script>
  437. <script type="text/html" id="metastore-nav-properties">
  438. <!-- ko if: $root.navigatorEnabled() -->
  439. <div data-bind="component: { name: 'nav-properties', params: { catalogEntry: catalogEntry }}"></div>
  440. <!-- /ko -->
  441. </script>
  442. <script type="text/html" id="metastore-tables">
  443. <div class="row-fluid">
  444. <!-- ko if: $root.navigatorEnabled() -->
  445. <!-- ko template: 'metastore-main-description' --><!-- /ko -->
  446. <!-- ko template: 'metastore-nav-tags' --><!-- /ko -->
  447. <!-- ko template: 'metastore-nav-properties' --><!-- /ko -->
  448. <!-- /ko -->
  449. <!-- ko with: stats -->
  450. <div class="span12 tile" style="margin: 5px 10px;">
  451. <div class="span6 tile">
  452. <h4>${ _('Properties') }</h4>
  453. <div>
  454. <div class="metastore-property">
  455. <div>${ _('Owner') }</div>
  456. <div><span data-bind="text: owner_name ? owner_name : '${ _ko('None') }'"></span> <span data-bind="visible: owner_type">(<span data-bind="text: owner_type"></span>)</span></div>
  457. </div>
  458. <div class="metastore-property">
  459. <div>${ _('Location') }</div>
  460. <div>
  461. % if IS_EMBEDDED.get():
  462. <span data-bind="attr: { 'title': location }"> ${_('Location')}</span>
  463. % else:
  464. <a href="javascript: void(0);" data-bind="storageContextPopover: { path: hdfs_link.replace('/filebrowser/view=', ''), offset: { left: 5 } }"> ${_('Location')}</a>
  465. % endif
  466. </div>
  467. </div>
  468. </div>
  469. </div>
  470. <!-- ko with: parameters -->
  471. <div class="row-fluid">
  472. <div class="span12">
  473. <div title="${ _('Parameters') }">
  474. <!-- ko template: { name: 'metastore-databases-parameters', data: hueUtils.parseHivePseudoJson($data) }--><!-- /ko -->
  475. </div>
  476. </div>
  477. </div>
  478. <!-- /ko -->
  479. </div>
  480. <!-- /ko -->
  481. </div>
  482. <div class="row-fluid">
  483. <div class="span12 tile entries-table-container">
  484. <h4 class="entries-table-header">${ _('Tables') }</h4>
  485. <div class="actionbar-actions" data-bind="visible: tables().length > 0">
  486. <button class="btn toolbarBtn margin-left-20" title="${_('Browse the selected table')}" data-bind="click: function () { onTableClick(selectedTables()[0].catalogEntry); selectedTables([]); }, disable: selectedTables().length !== 1"><i class="fa fa-eye"></i> ${_('View')}</button>
  487. <button class="btn toolbarBtn" title="${_('Query the selected table')}" data-bind="click: function () { IS_HUE_4 ? queryAndWatch('/notebook/browse/' + selectedTables()[0].catalogEntry.path.join('/') + '/', $root.source().type) : location.href = '/notebook/browse/' + selectedTables()[0].catalogEntry.path.join('/'); }, disable: selectedTables().length !== 1">
  488. <i class="fa fa-play fa-fw"></i> ${_('Query')}
  489. </button>
  490. % if has_write_access:
  491. <button id="dropBtn" class="btn toolbarBtn" title="${_('Drop the selected tables')}" data-bind="click: function () { $('#dropTable').modal('show'); }, disable: selectedTables().length === 0"><i class="fa fa-times"></i> ${_('Drop')}</button>
  492. % if is_embeddable:
  493. <button href="javascript: void(0);" class="btn btn-default" data-bind="publish: { 'open.link': '${ url('indexer:importer_prefill', source_type='all', target_type='table') }' + catalogEntry.name + '/?sourceType=' + catalogEntry.getSourceType() + '&namespace=' + catalogEntry.namespace.id + '&compute=' + catalogEntry.compute.id }" title="${_('Create a new table')}"><i class="fa fa-plus"></i> ${_('New')}</button>
  494. % elif ENABLE_NEW_CREATE_TABLE.get():
  495. <button class="btn btn-default" data-bind="attr: { 'href': '${ url('indexer:importer_prefill', source_type='all', target_type='table') }' + catalogEntry.name + '/?sourceType=' + catalogEntry.getSourceType() + '&namespace=' + catalogEntry.namespace.id + '&compute=' + catalogEntry.compute.id }" title="${_('Create a new table')}"><i class="fa fa-plus"></i> ${_('New')}</button>
  496. % else:
  497. <button class="btn btn-default" data-bind="attr: { 'href': '/beeswax/create/import_wizard/' + catalogEntry.name }" title="${_('Create a new table from a file')}"><i class="fa fa-stack"></i> ${_('New from file')}</button>
  498. <button class="btn btn-default" data-bind="attr: { 'href': '/beeswax/create/create_table/' + catalogEntry.name }" title="${_('Create a new table manually')}"><i class="fa fa-plus"></i> ${_('New manually')}</button>
  499. % endif
  500. % endif
  501. </div>
  502. <!-- ko component: { name: 'catalog-entries-list', params: { catalogEntry: catalogEntry, contextPopoverEnabled: true, onClick: onTableClick.bind($data), selectedEntries: selectedTables, editableDescriptions: /true/i.test('${ has_write_access }') } } --><!-- /ko -->
  503. </div>
  504. </div>
  505. % if has_write_access:
  506. <div id="dropTable" class="modal hide fade">
  507. % if is_embeddable:
  508. <form data-bind="attr: { 'action': '/metastore/tables/drop/' + catalogEntry.name }, submit: dropAndWatch" method="POST">
  509. <input type="hidden" name="is_embeddable" value="true"/>
  510. <input type="hidden" name="start_time" value=""/>
  511. <input type="hidden" name="source_type" data-bind="value: $root.source().type"/>
  512. % else:
  513. <form data-bind="attr: { 'action': '/metastore/tables/drop/' +catalogEntry. name }" method="POST">
  514. % endif
  515. ${ csrf_token(request) | n,unicode }
  516. <div class="modal-header">
  517. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  518. <h2 id="dropTableMessage" class="modal-title">${_('Do you really want to drop the selected table(s)?')}</h2>
  519. </div>
  520. <div class="modal-body">
  521. <ul data-bind="foreach: selectedTables">
  522. <!-- ko if: $index() <= 9 -->
  523. <li>
  524. <span data-bind="text: catalogEntry.name"></span>
  525. </li>
  526. <!-- /ko -->
  527. </ul>
  528. <!-- ko if: selectedTables().length > 10 -->
  529. ${_('and')} <span data-bind="text: selectedTables().length - 10"></span> ${_('others')}.<br>
  530. <!-- /ko -->
  531. <label class="checkbox" style="display: inline-block; margin-top: 5px">
  532. <input type="checkbox" name="skip_trash" /> ${ _('Skip the trash') }
  533. </label>
  534. </div>
  535. <div class="modal-footer">
  536. <input type="button" class="btn" data-dismiss="modal" value="${_('No')}" />
  537. <input type="submit" class="btn btn-danger" value="${_('Yes')}"/>
  538. </div>
  539. <!-- ko foreach: selectedTables -->
  540. <input type="hidden" name="table_selection" data-bind="value: catalogEntry.name" />
  541. <!-- /ko -->
  542. </form>
  543. </div>
  544. % endif
  545. </script>
  546. <script type="text/html" id="metastore-databases-parameters">
  547. <div data-bind="toggleOverflow: {height: 24}" style="word-break: break-all">
  548. <div class="inline margin-right-20"><i class="fa fa-fw fa-cog muted"></i></div>
  549. <!-- ko foreach: Object.keys($data) -->
  550. <div class="inline margin-right-20"><strong data-bind="text: $data"></strong>: <span data-bind="text: $parent[$data]"></span></div>
  551. <!-- /ko -->
  552. </div>
  553. </script>
  554. <script type="text/html" id="metastore-overview-tab">
  555. <div class="row-fluid margin-top-10">
  556. <div class="span4 tile">
  557. <!-- ko template: 'metastore-table-properties' --><!-- /ko -->
  558. </div>
  559. <div class="span4 tile">
  560. <!-- ko template: 'metastore-table-stats' --><!-- /ko -->
  561. </div>
  562. </div>
  563. <div class="tile">
  564. <h4 style="margin-bottom: 5px;">${ _('Schema') } <i data-bind="visible: loadingColumns" class="fa fa-spinner fa-spin" style="display: none;"></i></h4>
  565. <!-- ko component: { name: 'catalog-entries-list', params: { catalogEntry: catalogEntry, contextPopoverEnabled: true, editableDescriptions: /true/i.test('${ has_write_access }') } } --><!-- /ko -->
  566. </div>
  567. </script>
  568. <script type="text/html" id="metastore-partitions-tab">
  569. <!-- ko with: partitions -->
  570. <div class="tile" data-bind="visible: true" style="display: none;">
  571. <h4>${ _('Columns') }</h4>
  572. <!-- ko template: 'metastore-partition-columns-table' --><!-- /ko -->
  573. </div>
  574. <div class="tile" data-bind="visible: true" style="display: none;">
  575. <h4>${ _('Partitions') } <i data-bind="visible: loading" class='fa fa-spinner fa-spin' style="display: none;"></i></h4>
  576. <!-- ko if: loaded -->
  577. <div class="row-fluid">
  578. <div class="span10">
  579. <div data-bind="visible: filters().length > 0">
  580. <div data-bind="foreach: filters">
  581. <div class="filter-box">
  582. <a href="javascript:void(0)" class="pull-right" data-bind="click: $parent.removeFilter">
  583. <i class="fa fa-times"></i>
  584. </a>
  585. <select class="input-small" data-bind="options: $parent.keys, value: column"></select>
  586. &nbsp;
  587. <input class="input-small" type="text" data-bind="value: value, typeahead: { target: value, source: $parent.typeaheadValues(column), triggerOnFocus: true, forceUpdateSource: true}" placeholder="${ _('Value to filter...') }" />
  588. </div>
  589. </div>
  590. <div class="pull-left" style="margin-top: 4px; margin-bottom: 10px">
  591. <a class="add-filter" href="javascript: void(0)" data-bind="click: addFilter">
  592. <i class="fa fa-plus"></i> ${ _('Add') }
  593. </a>
  594. <label class="checkbox inline pulled">${ _('Sort Desc') } <input type="checkbox" data-bind="checked: sortDesc" /></label>
  595. <button class="btn" data-bind="click: filter"><i class="fa fa-filter"></i> ${ _('Filter') }</button>
  596. </div>
  597. </div>
  598. <a class="add-filter" href="javascript: void(0)" data-bind="click: addFilter, visible: values().length > 0 && filters().length == 0" style="margin-bottom: 20px; margin-left: 14px">
  599. <i class="fa fa-plus"></i> ${ _('Add a filter') }
  600. </a>
  601. <div class="clearfix"></div>
  602. </div>
  603. <div class="span2">
  604. % if has_write_access:
  605. <div class="pull-right">
  606. <button class="btn" title="${_('Delete the selected partitions')}" data-bind="click: function () { $('#dropPartition').modal('show'); }, disable: selectedValues().length === 0"><i class="fa fa-trash-o"></i> ${_('Drop partition(s)')}</button>
  607. </div>
  608. % endif
  609. </div>
  610. </div>
  611. <!-- ko template: { if: values().length, name: 'metastore-partition-values-table', data: { values: values, selectedValues: selectedValues, withDrop: true } } --><!-- /ko -->
  612. <span data-bind="visible: !values().length" style="display: none;">${ _('The partition does not contain any values') }</span>
  613. <!-- /ko -->
  614. </div>
  615. % if has_write_access:
  616. <div id="dropPartition" class="modal hide fade">
  617. % if is_embeddable:
  618. <form data-bind="attr: { 'action': '/metastore/table/' + $parent.catalogEntry.path.join('/') + '/partitions/drop' }, submit: dropAndWatch" method="POST">
  619. <input type="hidden" name="is_embeddable" value="true"/>
  620. <input type="hidden" name="format" value="json"/>
  621. <input type="hidden" name="start_time" value=""/>
  622. <input type="hidden" name="source_type" data-bind="value: $root.source().type"/>
  623. % else:
  624. <form data-bind="attr: { 'action': '/metastore/table/' + $parent.catalogEntry.path.join('/')+ '/partitions/drop' }" method="POST">
  625. % endif
  626. ${ csrf_token(request) | n,unicode }
  627. <div class="modal-header">
  628. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  629. <h2 id="dropPartitionMessage" class="modal-title">${_('Confirm action')}</h2>
  630. </div>
  631. <div class="modal-footer">
  632. <input type="button" class="btn" data-dismiss="modal" value="${_('Cancel')}" />
  633. <input type="submit" class="btn btn-danger" value="${_('Yes')}"/>
  634. </div>
  635. <select class="hide" name="partition_selection" data-bind="options: valuesFlat, selectedOptions: selectedValuesFlat" size="5" multiple="true"></select>
  636. </form>
  637. </div>
  638. % endif
  639. <!-- /ko -->
  640. </script>
  641. <script type="text/html" id="metastore-sample-tab">
  642. <!-- ko with: samples -->
  643. <!-- ko if: loading -->
  644. <div class="empty-message">
  645. <i data-bind="visible: loading" class='fa fa-spinner fa-spin' style="display: none;"></i>
  646. </div>
  647. <!-- /ko -->
  648. <!-- ko if: loaded() && !hasErrors() -->
  649. <!-- ko template: { if: rows().length, name: 'metastore-samples-table' } --><!-- /ko -->
  650. <div data-bind="visible: !rows().length && metastoreTable.tableDetails().is_view" style="display: none;" class="empty-message">${ _('The view does not contain any data.') }</div>
  651. <div data-bind="visible: !rows().length && !metastoreTable.tableDetails().is_view" style="display: none;" class="empty-message">${ _('The table does not contain any data.') }</div>
  652. <!-- /ko -->
  653. <!-- ko if: hasErrors() -->
  654. <div class="empty-message alert" data-bind="text: errorMessage() || '${ _ko('Could not load the sample, see the server log for details.') }'"></div>
  655. <!-- /ko -->
  656. <!-- /ko -->
  657. </script>
  658. <script type="text/html" id="metastore-permissions-tab">
  659. <div class="acl-panel-content" style="height: 988px;">
  660. <div class="pull-right">
  661. <input class="input-medium no-margin" type="text" placeholder="Search privileges..."> &nbsp;
  662. <a class="btn pointer">
  663. <i class="fa fa-plus-circle"></i> Add role
  664. </a>
  665. </div>
  666. <h4 style="margin-top: 4px;">Privileges &nbsp;</h4>
  667. <div class="acl-block-title">
  668. <i class="fa fa-cube muted"></i> <a class="pointer"><span>customerFraud</span></a>
  669. </div>
  670. <div>
  671. <div class="acl-block acl-block-airy">
  672. <span class="muted" title="3 months ago">TABLE</span>
  673. <span>
  674. <a class="muted" style="margin-left: 4px" title="Open in Sentry" href="/security/hive"><i class="fa fa-external-link"></i></a>
  675. </span>
  676. <br>
  677. server=<span>server1</span>
  678. <span>
  679. <i class="fa fa-long-arrow-right"></i> db=<a class="pointer" title="Browse db privileges"><span data-bind="text: $root.database().catalogEntry.name"></span></a>
  680. </span>
  681. <span>
  682. <i class="fa fa-long-arrow-right"></i> table=<a class="pointer" title="Browse table privileges"><span data-bind="text: catalogEntry.name"></span></a>
  683. </span>
  684. <span style="display: none;">
  685. <i class="fa fa-long-arrow-right"></i> column=<a class="pointer" title="Browse column privileges"><span></span></a>
  686. </span>
  687. <i class="fa fa-long-arrow-right"></i> action=INSERT
  688. </div>
  689. <div class="acl-block acl-block-airy">
  690. <span class="muted" title="3 months ago">TABLE</span>
  691. <span>
  692. <a class="muted" style="margin-left: 4px" title="Open in Sentry" href="/security/hive"><i class="fa fa-external-link"></i></a>
  693. </span>
  694. <br>
  695. server=server1
  696. <span>
  697. <i class="fa fa-long-arrow-right"></i> db=<a class="pointer" title="Browse db privileges"><span data-bind="text: $root.database().catalogEntry.name"></span></a>
  698. </span>
  699. <span>
  700. <i class="fa fa-long-arrow-right"></i> table=<a class="pointer" title="Browse table privileges"><span data-bind="text: catalogEntry.name"></span></a>
  701. </span>
  702. <span style="display: none;">
  703. <i class="fa fa-long-arrow-right"></i> column=<a class="pointer" title="Browse column privileges"><span></span></a>
  704. </span>
  705. <i class="fa fa-long-arrow-right"></i> action=<span>SELECT</span>
  706. </div>
  707. </div>
  708. <div class="acl-block acl-actions">
  709. <span class="pointer" title="Show 50 more..." style="display: none;"><i class="fa fa-ellipsis-h"></i></span>
  710. <span class="pointer" title="Add privilege"><i class="fa fa-plus"></i></span>
  711. <span class="pointer" title="Undo" style="display: none;"> &nbsp; <i class="fa fa-undo"></i></span>
  712. <span class="pointer" title="Save" style="display: none;"> &nbsp; <i class="fa fa-save"></i></span>
  713. </div>
  714. <div class="acl-block-title">
  715. <i class="fa fa-cube muted"></i> <a class="pointer"><span>customerAccess</span></a>
  716. </div>
  717. <div>
  718. <div class="acl-block acl-block-airy">
  719. <span class="muted" title="3 months ago">TABLE</span>
  720. <span>
  721. <a class="muted" style="margin-left: 4px" title="Open in Sentry" href="/security/hive"><i class="fa fa-external-link"></i></a>
  722. </span>
  723. <br>
  724. server=server1
  725. <span>
  726. <i class="fa fa-long-arrow-right"></i> db=<a class="pointer" title="Browse db privileges"><span data-bind="text: $root.database().catalogEntry.name"></span></a>
  727. </span>
  728. <span>
  729. <i class="fa fa-long-arrow-right"></i> table=<a class="pointer" title="Browse table privileges"><span data-bind="text: catalogEntry.name"></span></a>
  730. </span>
  731. <span style="display: none;">
  732. <i class="fa fa-long-arrow-right"></i> column=<a class="pointer" title="Browse column privileges"><span></span></a>
  733. </span>
  734. <i class="fa fa-long-arrow-right"></i> action=<span>ALL</span>
  735. </div>
  736. <div class="acl-block acl-actions">
  737. <span class="pointer" title="Show 50 more..." style="display: none;"><i class="fa fa-ellipsis-h"></i></span>
  738. <span class="pointer" title="Add privilege"><i class="fa fa-plus"></i></span>
  739. <span class="pointer" title="Undo" style="display: none;"> &nbsp; <i class="fa fa-undo"></i></span>
  740. <span class="pointer" title="Save" style="display: none;"> &nbsp; <i class="fa fa-save"></i></span>
  741. </div>
  742. </div>
  743. </div>
  744. </script>
  745. <script type="text/html" id="metastore-queries-tab">
  746. <br/>
  747. <i class="fa fa-spinner fa-spin" data-bind="visible: $root.loadingQueries"></i>
  748. <table data-bind="visible: ! loadingQueries() && $data.optimizerDetails().queryList().length > 0" class="table table-condensed">
  749. <thead>
  750. <tr>
  751. <th width="10%">${ _('Id') }</th>
  752. <th width="10%">${ _('Popularity') }</th>
  753. <th>${ _('Character') }</th>
  754. <th>${ _('Query') }</th>
  755. <th width="20%">${ _('Complexity') }</th>
  756. <th width="10%">${ _('Hive Compatible') }</th>
  757. <th width="10%">${ _('Impala Compatible') }</th>
  758. </tr>
  759. </thead>
  760. <tbody data-bind="hueach: { data: $data.optimizerDetails().queryList(), itemHeight: 32, scrollable: '${ MAIN_SCROLLABLE }', scrollableOffset: 200 }">
  761. <tr>
  762. <td data-bind="text: qid"></td>
  763. <td style="height: 10px; width: 70px; margin-top:5px;" data-bind="attr: {'title': queryCount()}">
  764. <div class="progress bar" style="background-color: #0B7FAD" data-bind="style: { 'width' : Math.round(queryCount() / $parent.optimizerDetails().queryCount() * 100) + '%' }"></div>
  765. </td>
  766. <td><code data-bind="text: queryChar"></code></td>
  767. <td><code data-bind="text: query().substring(0, 100) + '...'"></code></td>
  768. <td data-bind="text: complexity, css: {'alert-success': complexity() == 'Low', 'alert-warning': complexity() == 'Medium', 'alert-danger': complexity() == 'High'}" class="alert"></td>
  769. <td data-bind="text: hiveCompatible"></td>
  770. <td data-bind="text: impalaCompatible"></td>
  771. </tr>
  772. </tbody>
  773. </table>
  774. <div data-bind="visible: ! loadingQueries() && $data.optimizerDetails().queryList().length == 0" class="empty-message">
  775. ${ _('No queries found for the current table.') }
  776. </div>
  777. </script>
  778. <script type="text/html" id="metastore-view-sql-tab">
  779. <div style="padding: 5px 15px">
  780. <!-- ko hueSpinner: { spin: loadingViewSql, inline: true } --><!-- /ko -->
  781. <!-- ko ifnot: loadingViewSql -->
  782. <div data-bind="highlight: { value: viewSql, formatted: true, dialect: catalogEntry.getSourceType() }"></div>
  783. <!-- /ko -->
  784. </div>
  785. </script>
  786. <script type="text/html" id="metastore-details-tab">
  787. <!-- ko with: tableDetails -->
  788. <table class="properties-table">
  789. <tbody data-bind="foreach: properties">
  790. <!-- ko if: col_name.indexOf('#') === 0 -->
  791. <tr>
  792. <td colspan="3"><h4 data-bind="text: col_name.substring(1)"></h4></td>
  793. </tr>
  794. <!-- /ko -->
  795. <!-- ko ifnot: col_name.indexOf('#') === 0 -->
  796. <tr>
  797. <td class="property-name" data-bind="text: col_name || '&nbsp;'"></td>
  798. <td data-bind="text: data_type || '&nbsp;'"></td>
  799. <td data-bind="text: comment || '&nbsp;'"></td>
  800. </tr>
  801. <!-- /ko -->
  802. </tbody>
  803. </table>
  804. <!-- /ko -->
  805. </script>
  806. <script type="text/html" id="metastore-relationships-tab">
  807. <!-- ko hueSpinner: { spin: loadingTopJoins, inline: true } --><!-- /ko -->
  808. <table data-bind="visible: !loadingTopJoins()" class="table table-condensed">
  809. <thead>
  810. <tr>
  811. <th>${ _('Table') }</th>
  812. <th>${ _('Foreign keys') }</th>
  813. </tr>
  814. </thead>
  815. <tbody>
  816. <!-- ko if: topJoins().length === 0 -->
  817. <tr>
  818. <td colspan="2" style="font-style: italic;">${ _('No related tables found.') }</td>
  819. </tr>
  820. <!-- /ko -->
  821. <!-- ko foreach: topJoins -->
  822. <tr>
  823. <td><a href="javascript:void(0);" data-bind="text: tableName, sqlContextPopover: { sourceType: $parents[1].catalogEntry.getSourceType(), namespace: parents[1].catalogEntry.namespace, compute: parents[1].catalogEntry.compute, path: tablePath, offset: { top: -3, left: 3 }}"></a></td>
  824. <td>
  825. <table class="metastore-join-column-table">
  826. <tbody data-bind="foreach: joinCols">
  827. <tr>
  828. <td><a href="javascript:void(0);" data-bind="text: target, sqlContextPopover: { sourceType: $parents[2].catalogEntry.getSourceType(), namespace: $parents[2].catalogEntry.namespace, compute: parents[2].catalogEntry.compute, path: targetPath, offset: { top: -3, left: 3 }}"></a></td>
  829. <td class="metastore-join-arrow"><i class="fa fa-arrows-h"></i></td>
  830. <td><a href="javascript:void(0);" data-bind="text: source, sqlContextPopover: { sourceType: $parents[2].catalogEntry.getSourceType(), namespace: $parents[2].catalogEntry.namespace, compute: parents[2].catalogEntry.compute, path: sourcePath, offset: { top: -3, left: 3 }}"></a></td>
  831. </tr>
  832. </tbody>
  833. </table>
  834. </td>
  835. </tr>
  836. <!-- /ko -->
  837. </tbody>
  838. </table>
  839. </script>
  840. <script type="text/html" id="metastore-describe-table">
  841. <div class="clearfix"></div>
  842. <!-- ko template: 'metastore-main-description' --><!-- /ko -->
  843. <!-- ko if: $root.navigatorEnabled() -->
  844. <!-- ko template: 'metastore-nav-tags' --><!-- /ko -->
  845. <!-- ko template: 'metastore-nav-properties' --><!-- /ko -->
  846. <!-- /ko -->
  847. <ul class="nav nav-tabs nav-tabs-border margin-top-10">
  848. <li data-bind="css: { 'active': $root.currentTab() === 'overview' }"><a href="javascript: void(0);" data-bind="click: function() { $root.currentTab('overview'); }">${_('Overview')}</a></li>
  849. <!-- ko if: $root.optimizerEnabled() -->
  850. <li data-bind="css: { 'active': $root.currentTab() === 'relationships' }"><a href="javascript: void(0);" data-bind="click: function() { $root.currentTab('relationships'); }">${_('Relationships')} (<span data-bind="text: topJoins().length"></span>)</a></li>
  851. ## <!-- ko if: $root.database().table().optimizerDetails() -->
  852. ## <li data-bind="css: { 'active': $root.currentTab() === 'permissions' }"><a href="javascript: void(0);" data-bind="click: function(){ $root.currentTab('permissions'); }">${_('Permissions')}</a></li>
  853. ## <li data-bind="css: { 'active': $root.currentTab() === 'queries' }"><a href="javascript: void(0);" data-bind="click: function(){ $root.currentTab('queries'); }">${_('Queries')} (<span data-bind="text: $root.database().table().optimizerDetails().queryCount"></span>)</a></li>
  854. ## <li data-bind="css: { 'active': $root.currentTab() === 'joins' }"><a href="javascript: void(0);" data-bind="click: function(){ $root.currentTab('joins'); }">${_('Joins')} (<span data-bind="text: $root.database().table().optimizerDetails().joinCount"></span>)</a></li>
  855. ## <!-- /ko -->
  856. ## <!-- ko if: $root.database().table().relationshipsDetails() -->
  857. ## <!-- /ko -->
  858. <!-- /ko -->
  859. <!-- ko if: tableDetails() && tableDetails().partition_keys.length -->
  860. <li data-bind="css: { 'active': $root.currentTab() === 'partitions' }"><a href="javascript: void(0);" data-bind="click: function() { $root.currentTab('partitions'); }">${_('Partitions')} (<span data-bind="text: partitionsCountLabel"></span>)</a></li>
  861. <!-- /ko -->
  862. <li data-bind="css: { 'active': $root.currentTab() === 'sample' }"><a href="javascript: void(0);" data-bind="click: function() { $root.currentTab('sample'); }">${_('Sample')} (<span data-bind="text: samples.rows().length"></span>)</a></li>
  863. <!-- ko if: catalogEntry.isView() -->
  864. <li data-bind="css: { 'active' : $root.currentTab() === 'viewSql' }"><a href="javascript: void(0);" data-bind="click: function() { $root.currentTab('viewSql'); }">${ _('View SQL') }</a></li>
  865. <!-- /ko -->
  866. <li data-bind="css: { 'active' : $root.currentTab() === 'details' }"><a href="javascript: void(0);" data-bind="click: function() { $root.currentTab('details'); }">${ _('Details') }</a></li>
  867. <li data-bind="css: { 'active' : $root.currentTab() === 'privileges' }"><a href="javascript: void(0);" data-bind="click: function() { $root.currentTab('privileges'); }">${ _('Privileges') }</a></li>
  868. </ul>
  869. <div class="tab-content margin-top-10" style="border: none; overflow: hidden">
  870. <div>
  871. <!-- ko if: $root.currentTab() === 'overview' -->
  872. <!-- ko template: 'metastore-overview-tab' --><!-- /ko -->
  873. <!-- /ko -->
  874. <!-- ko if: $root.currentTab() === 'relationships' -->
  875. <!-- ko template: { name: 'metastore-relationships-tab' } --><!-- /ko -->
  876. <!-- /ko -->
  877. <!-- ko if: $root.currentTab() === 'partitions' -->
  878. <!-- ko template: 'metastore-partitions-tab' --><!-- /ko -->
  879. <!-- /ko -->
  880. <!-- ko if: $root.currentTab() === 'sample' -->
  881. <!-- ko template: 'metastore-sample-tab' --><!-- /ko -->
  882. <!-- /ko -->
  883. <!-- ko if: $root.currentTab() === 'permissions' -->
  884. <!-- ko template: 'metastore-permissions-tab' --><!-- /ko -->
  885. <!-- /ko -->
  886. <!-- ko if: $root.optimizerEnabled() && $root.currentTab() === 'queries' -->
  887. <!-- ko template: { name: 'metastore-queries-tab', data: $root.database().table() } --><!-- /ko -->
  888. <!-- /ko -->
  889. <!-- ko if: $root.currentTab() === 'viewSql' -->
  890. <!-- ko template: 'metastore-view-sql-tab' --><!-- /ko -->
  891. <!-- /ko -->
  892. <!-- ko if: $root.currentTab() === 'details' -->
  893. <!-- ko template: 'metastore-details-tab' --><!-- /ko -->
  894. <!-- /ko -->
  895. <!-- ko if: $root.currentTab() === 'privileges' -->
  896. <div data-bind="component: { name: 'hue-sentry-privileges', params: { isSentryAdmin: false, readOnly: true, server: 'server1', path: catalogEntry.path.join('.')}}"></div>
  897. <!-- /ko -->
  898. </div>
  899. </div>
  900. </script>
  901. <span id="metastoreComponents">
  902. % if not is_embeddable:
  903. <a title="${_('Toggle Assist')}" class="pointer show-assist" data-bind="visible: !$root.isLeftPanelVisible() && $root.assistAvailable(), click: function () { $root.isLeftPanelVisible(true); }">
  904. <i class="fa fa-chevron-right"></i>
  905. </a>
  906. % endif
  907. <div class="main-content">
  908. <div class="vertical-full container-fluid" data-bind="style: { 'padding-left' : $root.isLeftPanelVisible() ? '0' : '20px' }">
  909. <div class="vertical-full row-fluid panel-container">
  910. % if not is_embeddable:
  911. <div class="assist-container left-panel" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable()">
  912. <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { $root.isLeftPanelVisible(false) }">
  913. <i class="fa fa-chevron-left"></i>
  914. </a>
  915. <div class="assist" data-bind="component: {
  916. name: 'assist-panel',
  917. params: {
  918. user: '${user.username}',
  919. sql: {
  920. navigationSettings: {
  921. openItem: true,
  922. showStats: true
  923. }
  924. },
  925. visibleAssistPanels: ['sql']
  926. }
  927. }"></div>
  928. </div>
  929. <div class="resizer" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable(), splitDraggable : { appName: 'notebook', leftPanelVisible: $root.isLeftPanelVisible }"><div class="resize-bar">&nbsp;</div></div>
  930. % endif
  931. <div class="content-panel">
  932. <div class="metastore-main">
  933. <!-- ko hueSpinner: { spin: loading, center: true, size: 'xlarge' } --><!-- /ko -->
  934. <!-- ko ifnot: loading -->
  935. <h1>
  936. <div class="inline-block pull-right">
  937. <!-- ko with: source -->
  938. <!-- ko with: namespace -->
  939. <!-- ko with: database -->
  940. <!-- ko with: table -->
  941. % if USE_NEW_EDITOR.get():
  942. <!-- ko if: IS_HUE_4 -->
  943. <a href="javascript: void(0);" class="btn btn-default" data-bind="click: function() { queryAndWatch('/notebook/browse/' + catalogEntry.path.join('/') + '/', $root.source().type); }" title="${_('Query')}"><i class="fa fa-play fa-fw"></i> ${_('Query')}</a>
  944. <!-- /ko -->
  945. <!-- ko if: ! IS_HUE_4 -->
  946. <a class="btn btn-default" data-bind="attr: { 'href': '/notebook/browse/' + catalogEntry.path.join('/') }" title="${_('Query')}"><i class="fa fa-play fa-fw"></i> ${_('Query')}</a>
  947. <!-- /ko -->
  948. % else:
  949. <a class="btn btn-default" data-bind="attr: { 'href': '/metastore/table/'+ catalogEntry.path.join('/') + '/read' }" title="${_('Browse Data')}"><i class="fa fa-play fa-fw"></i> ${_('Browse Data')}</a>
  950. % endif
  951. % if has_write_access:
  952. <a href="javascript: void(0);" class="btn btn-default" data-bind="click: showImportData, visible: tableDetails() && !tableDetails().is_view" title="${_('Import Data')}"><i class="fa fa-upload fa-fw"></i> ${_('Import')}</a>
  953. % endif
  954. % if has_write_access:
  955. <a href="#dropSingleTable" data-toggle="modal" class="btn btn-default" data-bind="attr: { 'title' : tableDetails() && tableDetails().is_view ? '${_('Drop View')}' : '${_('Drop Table')}' }"><i class="fa fa-times fa-fw"></i> ${_('Drop')}</a>
  956. % endif
  957. <a href="javascript: void(0);" class="btn btn-default" data-bind="click: reload" title="${_('Refresh the table')}"><i class="fa fa-refresh" data-bind="css: { 'fa-spin blue' : refreshing }"></i> ${_('Refresh')}</a>
  958. <!-- /ko -->
  959. <!-- ko if: !table() -->
  960. <a href="javascript: void(0);" class="btn btn-default" data-bind="click: reload" title="${_('Refresh the database')}"><i class="fa fa-refresh" data-bind="css: { 'fa-spin blue' : refreshing }"></i> ${_('Refresh')}</a>
  961. <!-- /ko -->
  962. <!-- /ko -->
  963. <!-- ko if: !database() -->
  964. <a href="javascript: void(0);" class="btn btn-default" data-bind="click: reload" title="${_('Refresh')}"><i class="fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }"></i> ${_('Refresh')}</a>
  965. <!-- /ko -->
  966. <!-- /ko -->
  967. <!-- /ko -->
  968. </div>
  969. <!-- ko template: 'metastore-breadcrumbs' --><!-- /ko -->
  970. </h1>
  971. <!-- ko with: source -->
  972. <!-- ko with: namespace -->
  973. <!-- ko template: { if: !loading() && !database(), name: 'metastore-databases' } --><!-- /ko -->
  974. <!-- ko with: database -->
  975. <i data-bind="visible: loading" class="fa fa-spinner fa-spin fa-2x margin-left-10" style="color: #999; display: none;"></i>
  976. <!-- ko template: { if: !loading() && !table(), name: 'metastore-tables' } --><!-- /ko -->
  977. <!-- ko with: table -->
  978. <!-- ko template: 'metastore-describe-table' --><!-- /ko -->
  979. <!-- /ko -->
  980. <!-- /ko -->
  981. <!-- /ko -->
  982. <!-- /ko -->
  983. <!-- /ko -->
  984. </div>
  985. </div>
  986. </div>
  987. </div>
  988. <!-- ko with: source -->
  989. <!-- ko with: namespace -->
  990. <div id="dropSingleTable" class="modal hide fade">
  991. % if is_embeddable:
  992. <form data-bind="submit: dropAndWatch" method="POST">
  993. <input type="hidden" name="is_embeddable" value="true"/>
  994. <input type="hidden" name="start_time" value=""/>
  995. <input type="hidden" name="source_type" data-bind="value: $root.source().type"/>
  996. % else:
  997. <form method="POST">
  998. % endif
  999. ${ csrf_token(request) | n,unicode }
  1000. <div class="modal-header">
  1001. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  1002. <h2 class="modal-title">${_('Drop Table')}</h2>
  1003. </div>
  1004. <div class="modal-body">
  1005. <div>${_('Do you really want to drop the table')} <span style="font-weight: bold;" data-bind="text: database() && database().table() ? database().table().catalogEntry.name : ''"></span>?</div>
  1006. </div>
  1007. <div class="modal-footer">
  1008. <input type="hidden" name="table_selection" data-bind="value: database() && database().table() ? database().table().catalogEntry.name : ''" />
  1009. <input type="button" class="btn" data-dismiss="modal" value="${_('No')}"/>
  1010. <input type="submit" data-bind="click: function (vm, e) { var $form = $(e.target).parents('form'); $form.attr('action', '/metastore/tables/drop/' + vm.database().catalogEntry.name); return true; }" class="btn btn-danger" value="${_('Yes, drop this table')}"/>
  1011. </div>
  1012. </form>
  1013. </div>
  1014. <!-- /ko -->
  1015. <!-- /ko -->
  1016. <div id="import-data-modal" class="modal hide fade" style="display: block;width: 680px;margin-left: -340px!important;"></div>
  1017. </div>
  1018. </span>
  1019. <script type="text/javascript">
  1020. function dropAndWatch(formElement) {
  1021. $(formElement).find('input[name=start_time]').val(ko.mapping.toJSON(new Date().getTime()));
  1022. $(formElement).ajaxSubmit({
  1023. dataType: 'json',
  1024. success: function(resp) {
  1025. if (resp.history_uuid) {
  1026. huePubSub.publish('notebook.task.submitted', resp.history_uuid);
  1027. huePubSub.publish('metastore.clear.selection');
  1028. } else if (resp && resp.message) {
  1029. $(document).trigger("error", resp.message);
  1030. }
  1031. $("#dropTable").modal('hide');
  1032. $("#dropSingleTable").modal('hide');
  1033. $("#dropDatabase").modal('hide');
  1034. $("#dropPartition").modal('hide');
  1035. },
  1036. error: function (xhr) {
  1037. $(document).trigger("error", xhr.responseText);
  1038. }
  1039. });
  1040. }
  1041. function browsePartitionFolder(url) {
  1042. $.get(url, {
  1043. format: "json"
  1044. },function(resp) {
  1045. if (resp.uri_path) {
  1046. huePubSub.publish('open.link', resp.uri_path);
  1047. } else if (resp.message) {
  1048. $(document).trigger("error", resp.message);
  1049. }
  1050. }).fail(function (xhr) {
  1051. $(document).trigger("error", xhr.responseText);
  1052. });
  1053. }
  1054. function queryAndWatch(url, sourceType) {
  1055. $.post(url, {
  1056. format: "json",
  1057. sourceType: sourceType
  1058. },function(resp) {
  1059. if (resp.history_uuid) {
  1060. huePubSub.publish('open.editor.query', resp.history_uuid);
  1061. } else if (resp.message) {
  1062. $(document).trigger("error", resp.message);
  1063. }
  1064. }).fail(function (xhr) {
  1065. $(document).trigger("error", xhr.responseText);
  1066. });
  1067. }
  1068. (function () {
  1069. if (ko.options) {
  1070. ko.options.deferUpdates = true;
  1071. }
  1072. $(document).ready(function () {
  1073. var options = {
  1074. user: '${ user.username }',
  1075. % if is_embeddable:
  1076. hue4: true,
  1077. % endif
  1078. optimizerEnabled: '${ is_optimizer_enabled }' === 'True',
  1079. navigatorEnabled: '${ is_navigator_enabled }' === 'True',
  1080. optimizerUrl: '${ optimizer_url }',
  1081. navigatorUrl: '${ navigator_url }',
  1082. sourceType: '${ source_type }'
  1083. };
  1084. var viewModel = new MetastoreViewModel(options);
  1085. huePubSub.subscribe('metastore.scroll.to.top', function () {
  1086. $('${ MAIN_SCROLLABLE }').scrollTop(0);
  1087. });
  1088. huePubSub.subscribe('metastore.clear.selection', function () {
  1089. viewModel.selectedDatabases.removeAll();
  1090. if (viewModel.database()) {
  1091. viewModel.database().selectedTables.removeAll();
  1092. }
  1093. }, 'metastore');
  1094. viewModel.currentTab.subscribe(function(tab){
  1095. if (tab === 'relationships') {
  1096. // viewModel.database().table().getRelationships();
  1097. } else if (tab === 'sample') {
  1098. var selector = 'samplesTable';
  1099. % if conf.CUSTOM.BANNER_TOP_HTML.get():
  1100. var bannerTopHeight = 30;
  1101. % else:
  1102. var bannerTopHeight = 0;
  1103. % endif
  1104. if ($(selector).parents('.dataTables_wrapper').length == 0){
  1105. hueUtils.waitForRendered(selector, function(el){ return el.find('td').length > 0 }, function(){
  1106. $(selector).dataTable({
  1107. "bPaginate": false,
  1108. "bLengthChange": false,
  1109. "bInfo": false,
  1110. "bDestroy": true,
  1111. "bFilter": false,
  1112. "bAutoWidth": false,
  1113. "oLanguage": {
  1114. "sEmptyTable": "${_('No data available')}",
  1115. "sZeroRecords": "${_('No matching records')}"
  1116. },
  1117. "fnDrawCallback": function (oSettings) {
  1118. $(selector).parents('.dataTables_wrapper').css('overflow-x', 'hidden');
  1119. $(selector).jHueTableExtender2({
  1120. fixedHeader: true,
  1121. fixedFirstColumn: true,
  1122. includeNavigator: false,
  1123. lockSelectedRow: false,
  1124. parentId: 'sample',
  1125. classToRemove: 'sample-table',
  1126. mainScrollable: '${ MAIN_SCROLLABLE }',
  1127. % if is_embeddable:
  1128. stickToTopPosition: 51 + bannerTopHeight,
  1129. % else:
  1130. stickToTopPosition: 76 + bannerTopHeight,
  1131. % endif
  1132. clonedContainerPosition: 'fixed',
  1133. app: 'metastore'
  1134. });
  1135. $(selector).jHueHorizontalScrollbar();
  1136. },
  1137. "aoColumnDefs": [
  1138. {
  1139. "sType": "numeric",
  1140. "aTargets": [ "sort-numeric" ]
  1141. },
  1142. {
  1143. "sType": "string",
  1144. "aTargets": [ "sort-string" ]
  1145. },
  1146. {
  1147. "sType": "date",
  1148. "aTargets": [ "sort-date" ]
  1149. }
  1150. ]
  1151. });
  1152. });
  1153. }
  1154. }
  1155. });
  1156. viewModel.scrollToColumn = function (col) {
  1157. if (!col.table.samples.loading()) {
  1158. $('.page-content').scrollTop(0);
  1159. viewModel.currentTab('sample');
  1160. hueUtils.waitForRendered('#sampleTable', function (el) {
  1161. return el.parent().hasClass('dataTables_wrapper')
  1162. }, function () {
  1163. var sampleTable = $('#sampleTable');
  1164. var sampleCol = sampleTable.find('th').filter(function () {
  1165. return $.trim($(this).text()).indexOf(col.catalogEntry.name) > -1;
  1166. });
  1167. sampleTable.find('.columnSelected').removeClass('columnSelected');
  1168. sampleTable.find('tr td:nth-child(' + (sampleCol.index() + 1) + ')').addClass('columnSelected');
  1169. var scrollLeft = 0;
  1170. sampleTable.find('th:lt(' + sampleCol.index() + ')').each(function () {
  1171. scrollLeft += $(this).outerWidth();
  1172. });
  1173. scrollLeft = Math.max(0, scrollLeft - 40);
  1174. sampleTable.parent().scrollLeft(scrollLeft);
  1175. sampleTable.parent().trigger('scroll_update');
  1176. });
  1177. }
  1178. };
  1179. ko.applyBindings(viewModel, $('#metastoreComponents')[0]);
  1180. if (location.getParameter('refresh') === 'true') {
  1181. DataCatalog.getEntry({ namespace: viewModel.source().namespace().namespace, compute: viewModel.source().namespace().compute, sourceType: viewModel.source().type, path: [], definition: { type: 'source' }}).done(function (entry) {
  1182. entry.clearCache({ invalidate: viewMode.source().type === 'impala' ? 'invalidate' : 'cache', silenceErrors: true });
  1183. hueUtils.replaceURL('?');
  1184. });
  1185. }
  1186. });
  1187. })();
  1188. </script>
  1189. </span>
  1190. % if not is_embeddable:
  1191. ${ commonfooter(request, messages) | n,unicode }
  1192. % endif