var editor = null; var connected = false; var bookmarks = {}; var default_rows_limit = 100; var currentObject = null; var filterOptions = { "equal": "= 'DATA'", "not_equal": "!= 'DATA'", "greater": "> 'DATA'" , "greater_eq": ">= 'DATA'", "less": "< 'DATA'", "less_eq": "<= 'DATA'", "like": "LIKE 'DATA'", "ilike": "ILIKE 'DATA'", "null": "IS NULL", "not_null": "IS NOT NULL" }; function guid() { function s4() { return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); } return [s4(), s4(), "-", s4(), "-", s4(), "-", s4(), "-", s4(), s4(), s4()].join(""); } function getSessionId() { var id = sessionStorage.getItem("session_id"); if (!id) { id = guid(); sessionStorage.setItem("session_id", id); } return id; } function setRowsLimit(num) { localStorage.setItem("rows_limit", num); } function getRowsLimit() { return parseInt(localStorage.getItem("rows_limit") || default_rows_limit); } function getPaginationOffset() { var page = $(".current-page").data("page"); var limit = getRowsLimit(); return (page - 1) * limit; } function getPagesCount(rowsCount) { var limit = getRowsLimit(); var num = parseInt(rowsCount / limit); if ((num * limit) < rowsCount) { num++; } return num; } function apiCall(method, path, params, cb) { var timeout = 300000; // 5 mins is enough $.ajax({ timeout: timeout, url: "api" + path, method: method, cache: false, data: params, headers: { "x-session-id": getSessionId() }, success: function(data) { cb(data); }, error: function(xhr, status, data) { if (status == "timeout") { return cb({ error: "Query timeout after " + (timeout / 1000) + "s" }); } cb(jQuery.parseJSON(xhr.responseText)); } }); } function getObjects(cb) { apiCall("get", "/objects", {}, cb); } function getTables(cb) { apiCall("get", "/tables", {}, cb); } function getTableRows(table, opts, cb) { apiCall("get", "/tables/" + table + "/rows", opts, cb); } function getTableStructure(table, opts, cb) { apiCall("get", "/tables/" + table, opts, cb); } function getTableIndexes(table, cb) { apiCall("get", "/tables/" + table + "/indexes", {}, cb); } function getTableConstraints(table, cb) { apiCall("get", "/tables/" + table + "/constraints", {}, cb); } function getHistory(cb) { apiCall("get", "/history", {}, cb); } function getBookmarks(cb) { apiCall("get", "/bookmarks", {}, cb); } function executeQuery(query, cb) { apiCall("post", "/query", { query: query }, cb); } function explainQuery(query, cb) { apiCall("post", "/explain", { query: query }, cb); } function disconnect(cb) { apiCall("post", "/disconnect", {}, cb); } function encodeQuery(query) { return window.btoa(query).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "."); } function buildSchemaSection(name, objects) { var section = ""; var titles = { "table": "Tables", "view": "Views", "materialized_view": "Materialized Views", "sequence": "Sequences" }; var icons = { "table": '', "view": '', "materialized_view": '', "sequence": '' }; var klass = ""; if (name == "public") klass = "expanded"; section += "
"; section += "
" + name + "
"; section += "
"; for (group of ["table", "view", "materialized_view", "sequence"]) { if (objects[group].length == 0) continue; group_klass = ""; if (name == "public" && group == "table") group_klass = "expanded"; section += "
"; section += "
" + titles[group] + " (" + objects[group].length + ")
"; section += "
    " for (item of objects[group]) { var id = name + "." + item; section += "
  • " + icons[group] + " " + item + "
  • "; } section += "
"; } section += "
"; return section; } function loadSchemas() { $("#objects").html(""); getObjects(function(data) { for (schema in data) { $(buildSchemaSection(schema, data[schema])).appendTo("#objects"); } if (Object.keys(data).length == 1) { $(".schema").addClass("expanded"); } bindContextMenus(); }); } function escapeHtml(str) { if (str != null || str != undefined) { return jQuery("
").text(str).html(); } return "null"; } function unescapeHtml(str){ var e = document.createElement("div"); e.innerHTML = str; return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue; } function getCurrentObject() { return currentObject || { name: "", type: "" }; } function resetTable() { $("#results"). attr("data-mode", ""). text(""). removeClass("empty"). removeClass("no-crop"); } function performTableAction(table, action, el) { if (action == "truncate" || action == "delete") { var message = "Are you sure you want to " + action + " table " + table + " ?"; if (!confirm(message)) return; } switch(action) { case "truncate": executeQuery("TRUNCATE TABLE " + table, function(data) { if (data.error) alert(data.error); resetTable(); }); break; case "delete": executeQuery("DROP TABLE " + table, function(data) { if (data.error) alert(data.error); loadSchemas(); resetTable(); }); break; case "export": var format = el.data("format"); var filename = table + "." + format; var query = window.encodeURI("SELECT * FROM " + table); var url = window.location.href.split('#')[0] + "api/query?format=" + format + "&filename=" + filename + "&query=" + query + "&_session_id=" + getSessionId(); var win = window.open(url, "_blank"); win.focus(); break; } } function sortArrow(direction) { switch (direction) { case "ASC": return "▲"; case "DESC": return "▼"; default: return ""; } } function buildTable(results, sortColumn, sortOrder) { resetTable(); if (results.error) { $("ERROR: " + results.error + "").appendTo("#results"); $("#results").addClass("empty"); return; } if (results.rows.length == 0) { $("No records found").appendTo("#results"); $("#results").addClass("empty"); return; } var cols = ""; var rows = ""; results.columns.forEach(function(col) { if (col === sortColumn) { cols += "" + col + " " + sortArrow(sortOrder) + ""; } else { cols += "" + col + ""; } }); results.rows.forEach(function(row) { var r = ""; for (i in row) { r += "
" + escapeHtml(row[i]) + "
"; } rows += "" + r + ""; }); $("" + cols + "" + rows + "").appendTo("#results"); } function setCurrentTab(id) { $("#nav ul li.selected").removeClass("selected"); $("#" + id).addClass("selected"); } function showQueryHistory() { getHistory(function(data) { var rows = []; for(i in data) { rows.unshift([parseInt(i) + 1, data[i].query, data[i].timestamp]); } buildTable({ columns: ["id", "query", "timestamp"], rows: rows }); setCurrentTab("table_history"); $("#input").hide(); $("#body").prop("class", "full"); $("#results").addClass("no-crop"); }); } function showTableIndexes() { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } getTableIndexes(name, function(data) { setCurrentTab("table_indexes"); buildTable(data); $("#input").hide(); $("#body").prop("class", "full"); $("#results").addClass("no-crop"); }); } function showTableConstraints() { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } getTableConstraints(name, function(data) { setCurrentTab("table_constraints"); buildTable(data); $("#input").hide(); $("#body").prop("class", "full"); $("#results").addClass("no-crop"); }); } function showTableInfo() { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } apiCall("get", "/tables/" + name + "/info", {}, function(data) { $(".table-information ul").show(); $("#table_total_size").text(data.total_size); $("#table_data_size").text(data.data_size); $("#table_index_size").text(data.index_size); $("#table_rows_count").text(data.rows_count); $("#table_encoding").text("Unknown"); }); buildTableFilters(name, getCurrentObject().type); } function updatePaginator(pagination) { if (!pagination) { $(".current-page").data("page", 1).data("pages", 1); $("button.page").text("1 of 1"); $(".prev-page, .next-page").prop("disabled", "disabled"); return; } $(".current-page"). data("page", pagination.page). data("pages", pagination.pages_count); if (pagination.page > 1) { $(".prev-page").prop("disabled", ""); } else { $(".prev-page").prop("disabled", "disabled"); } if (pagination.pages_count > 1 && pagination.page < pagination.pages_count) { $(".next-page").prop("disabled", ""); } else { $(".next-page").prop("disabled", "disabled"); } $("#total_records").text(pagination.rows_count); if (pagination.pages_count == 0) pagination.pages_count = 1; $("button.page").text(pagination.page + " of " + pagination.pages_count); } function showTableContent(sortColumn, sortOrder) { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } var opts = { limit: getRowsLimit(), offset: getPaginationOffset(), sort_column: sortColumn, sort_order: sortOrder }; var filter = { column: $(".filters select.column").val(), op: $(".filters select.filter").val(), input: $(".filters input").val() }; // Apply filtering only if column is selected if (filter.column && filter.op) { var where = [ '"' + filter.column + '"', filterOptions[filter.op].replace("DATA", filter.input) ].join(" "); opts["where"] = where; } getTableRows(name, opts, function(data) { $("#results").attr("data-mode", "browse"); $("#input").hide(); $("#body").prop("class", "with-pagination"); buildTable(data, sortColumn, sortOrder); setCurrentTab("table_content"); updatePaginator(data.pagination); }); } function showTableStructure() { var name = getCurrentObject().name; if (name.length == 0) { alert("Please select a table!"); return; } setCurrentTab("table_structure"); $("#input").hide(); $("#body").prop("class", "full"); console.log(getCurrentObject()); getTableStructure(name, { type: getCurrentObject().type }, function(data) { buildTable(data); $("#results").addClass("no-crop"); }); } function showQueryPanel() { setCurrentTab("table_query"); editor.focus(); $("#input").show(); $("#body").prop("class", "") } function showConnectionPanel() { setCurrentTab("table_connection"); apiCall("get", "/connection", {}, function(data) { var rows = []; for(key in data) { rows.push([key, data[key]]); } buildTable({ columns: ["attribute", "value"], rows: rows }); $("#input").hide(); $("#body").addClass("full"); }); } function showActivityPanel() { setCurrentTab("table_activity"); apiCall("get", "/activity", {}, function(data) { buildTable(data); $("#input").hide(); $("#body").addClass("full"); }); } function runQuery() { setCurrentTab("table_query"); $("#run, #explain, #csv, #json, #xml").prop("disabled", true); $("#query_progress").show(); var query = $.trim(editor.getSelectedText() || editor.getValue()); if (query.length == 0) { $("#run, #explain, #csv, #json, #xml").prop("disabled", false); $("#query_progress").hide(); return; } executeQuery(query, function(data) { buildTable(data); $("#run, #explain, #csv, #json, #xml").prop("disabled", false); $("#query_progress").hide(); $("#input").show(); $("#body").removeClass("full"); if (query.toLowerCase().indexOf("explain") != -1) { $("#results").addClass("no-crop"); } var re = /(create|drop)\s/i; // Reload objects list if anything was created/deleted if (query.match(re)) { loadSchemas(); } }); } function runExplain() { setCurrentTab("table_query"); $("#run, #explain, #csv, #json, #xml").prop("disabled", true); $("#query_progress").show(); var query = $.trim(editor.getValue()); if (query.length == 0) { $("#run, #explain, #csv, #json, #xml").prop("disabled", false); $("#query_progress").hide(); return; } explainQuery(query, function(data) { buildTable(data); $("#run, #explain, #csv, #json, #xml").prop("disabled", false); $("#query_progress").hide(); $("#input").show(); $("#body").removeClass("full"); $("#results").addClass("no-crop"); }); } function exportTo(format) { var query = $.trim(editor.getValue()); if (query.length == 0) { return; } var url = window.location.href.split('#')[0] + "api/query?format=" + format + "&query=" + encodeQuery(query) + "&_session_id=" + getSessionId(); var win = window.open(url, '_blank'); setCurrentTab("table_query"); win.focus(); } function buildTableFilters(name, type) { getTableStructure(name, { type: type }, function(data) { if (data.rows.length == 0) { $("#pagination .filters").hide(); } else { $("#pagination .filters").show(); } $("#pagination select.column").html(""); for (row of data.rows) { var el = $("").appendTo("#connection_bookmarks"); // Add all available bookmarks for (key in data) { $("").appendTo("#connection_bookmarks"); } $(".bookmarks").show(); } else { $(".bookmarks").hide(); } }); $("#connection_window").show(); } function getConnectionString() { var url = $.trim($("#connection_url").val()); var mode = $(".connection-group-switch button.active").attr("data"); var ssl = $("#connection_ssl").val(); if (mode == "standard" || mode == "ssh") { var host = $("#pg_host").val(); var port = $("#pg_port").val(); var user = $("#pg_user").val(); var pass = encodeURIComponent($("#pg_password").val()); var db = $("#pg_db").val(); if (port.length == 0) { port = "5432"; } url = "postgres://" + user + ":" + pass + "@" + host + ":" + port + "/" + db + "?sslmode=" + ssl; } else { var local = url.indexOf("localhost") != -1 || url.indexOf("127.0.0.1") != -1; if (local && url.indexOf("sslmode") == -1) { url += "?sslmode=" + ssl; } } return url; } function bindContextMenus() { $(".schema-group ul").each(function(id, el) { $(el).contextmenu({ target: "#tables_context_menu", scopes: "li.schema-table", onItem: function(context, e) { var el = $(e.target); var table = $(context[0]).data("id"); var action = el.data("action"); performTableAction(table, action, el); } }); }); } $(document).ready(function() { $("#table_content").on("click", function() { showTableContent(); }); $("#table_structure").on("click", function() { showTableStructure(); }); $("#table_indexes").on("click", function() { showTableIndexes(); }); $("#table_constraints").on("click", function() { showTableConstraints(); }); $("#table_history").on("click", function() { showQueryHistory(); }); $("#table_query").on("click", function() { showQueryPanel(); }); $("#table_connection").on("click", function() { showConnectionPanel(); }); $("#table_activity").on("click", function() { showActivityPanel(); }); $("#run").on("click", function() { runQuery(); }); $("#explain").on("click", function() { runExplain(); }); $("#csv").on("click", function() { exportTo("csv"); }); $("#json").on("click", function() { exportTo("json"); }); $("#xml").on("click", function() { exportTo("xml"); }); $("#results").on("click", "tr", function() { $("#results tr.selected").removeClass(); $(this).addClass("selected"); }); $("#objects").on("click", ".schema-group-title", function(e) { $(this).parent().toggleClass("expanded"); }); $("#objects").on("click", ".schema-name", function(e) { $(this).parent().toggleClass("expanded"); }); $("#objects").on("click", "li", function(e) { currentObject = { name: $(this).data("id"), type: $(this).data("type") }; $("#objects li").removeClass("active"); $(this).addClass("active"); $(".current-page").data("page", 1); $(".filters select, .filters input").val(""); showTableInfo(); showTableContent(); }); $("#results").on("click", "th", function(e) { var sortColumn = this.attributes['data'].value; var contentTab = $('#table_content').hasClass('selected'); if (!contentTab) { return; } if (this.dataset.sortOrder === "ASC") { this.dataset.sortOrder = "DESC" } else { this.dataset.sortOrder = "ASC" } showTableContent(sortColumn, this.dataset.sortOrder); }); $("#results").on("dblclick", "td > div", function() { if ($(this).has("textarea").length > 0) { return; } var value = unescapeHtml($(this).html()); if (!value) { return; } var textarea = $("