shiba 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
data/web/package.json CHANGED
@@ -1,20 +1,50 @@
1
1
  {
2
- "name": "web",
3
- "version": "1.0.0",
4
- "description": "",
2
+ "name": "hello-world",
3
+ "version": "0.1.0",
4
+ "private": true,
5
5
  "scripts": {
6
- "test": "echo \"Error: no test specified\" && exit 1"
7
- },
8
- "keywords": [],
9
- "author": "",
10
- "license": "ISC",
11
- "devDependencies": {
12
- "webpack": "^4.29.0",
13
- "webpack-cli": "^3.2.1"
6
+ "serve": "vue-cli-service serve",
7
+ "build": "vue-cli-service build",
8
+ "lint": "vue-cli-service lint",
9
+ "autobuild": "vue-cli-service build --watch"
14
10
  },
15
11
  "dependencies": {
16
- "lodash": "^4.17.11",
17
- "vue": "^2.5.22",
12
+ "bootstrap": "^4.3.1",
13
+ "nanoajax": "^0.4.3",
14
+ "vue": "^2.6.6",
18
15
  "vue-js-modal": "^1.3.28"
19
- }
16
+ },
17
+ "devDependencies": {
18
+ "@vue/cli-plugin-babel": "^3.5.0",
19
+ "@vue/cli-plugin-eslint": "^3.5.0",
20
+ "@vue/cli-service": "^3.5.0",
21
+ "babel-eslint": "^10.0.1",
22
+ "eslint": "^5.8.0",
23
+ "eslint-plugin-vue": "^5.0.0",
24
+ "vue-template-compiler": "^2.5.21"
25
+ },
26
+ "eslintConfig": {
27
+ "root": true,
28
+ "env": {
29
+ "node": true
30
+ },
31
+ "extends": [
32
+ "plugin:vue/essential",
33
+ "eslint:recommended"
34
+ ],
35
+ "rules": {},
36
+ "parserOptions": {
37
+ "parser": "babel-eslint"
38
+ }
39
+ },
40
+ "postcss": {
41
+ "plugins": {
42
+ "autoprefixer": {}
43
+ }
44
+ },
45
+ "browserslist": [
46
+ "> 1%",
47
+ "last 2 versions",
48
+ "not ie <= 8"
49
+ ]
20
50
  }
data/web/src/App.vue ADDED
@@ -0,0 +1,185 @@
1
+ <template>
2
+ <div id="app" v-cloak>
3
+ <v-dialog :width="600"></v-dialog>
4
+ <div class="container" style="">
5
+ <div class="row" v-if="hasFuzzed">
6
+ <div class="alert alert-warning" role="alert">
7
+ This query analysis was generated using estimated table sizes.
8
+ To improve these results and find other problem queries beyond missing indexes, we'll need more stats.<br/>
9
+ <a target="_blank" href="https://github.com/burrito-brothers/shiba/blob/master/README.md#going-beyond-table-scans">Find out how to get a more accurate analysis by feeding Shiba index stats</a>
10
+ </div>
11
+ </div>
12
+ <div class="row">
13
+ <div class="col-10"></div>
14
+ <div class="col-2"><input :value="search" @input="updateSearch" placeholder="search..."></div>
15
+ </div>
16
+ <div class="row">
17
+ <div class="col-12">We found {{ queries.length }} queries that
18
+ <span v-if="search == ''">deserve your attention:</span>
19
+ <span v-else>match your search term</span>
20
+ </div>
21
+ </div>
22
+ <div class="row">
23
+ <div class="col-3">Table</div>
24
+ <div class="col-5">Query</div>
25
+ <div class="col-3">Source</div>
26
+ <div class="col-1">Severity</div>
27
+ </div>
28
+ <div class="queries">
29
+ <Query v-for="query in queries" v-bind:query="query" v-bind:key="query.sql" v-bind:tags="tags" v-bind:url="url"></query>
30
+ </div>
31
+ <div v-if="search == ''">
32
+ <div class="row">
33
+ <div class="col-12">We also found <a href="#" v-on:click.prevent="lowExpanded = !lowExpanded">{{ queriesLow.length }} queries</a> that look fine.</div>
34
+ </div>
35
+ <a name="lowExapnded"></a>
36
+ <div class="queries" v-if="lowExpanded">
37
+ <query v-for="query in queriesLow" v-bind:query="query" v-bind:key="query.sql" v-bind:tags="tags" v-bind:url="url"></query>
38
+ </div>
39
+ </div>
40
+ <div style="height:50px"></div>
41
+ </div>
42
+ </div>
43
+ </template>
44
+
45
+ <script>
46
+ import Query from './components/Query.vue'
47
+ import registerMessage from './components/Message.js'
48
+ import QueryData from './query_data.js'
49
+ import VModal from 'vue-js-modal';
50
+ import _ from 'lodash'
51
+ import Vue from 'vue';
52
+ import nanoajax from 'nanoajax';
53
+ import 'bootstrap/dist/css/bootstrap.min.css'
54
+
55
+ Vue.use(VModal, { dialog: true });
56
+
57
+ function categorizeQueries(v, queries) {
58
+ queries.forEach(function(query) {
59
+ var q = new QueryData(query);
60
+
61
+ if ( q.severity == "none" ) {
62
+ v.lowQ.push(q);
63
+ } else {
64
+ v.highQ.push(q);
65
+ }
66
+
67
+ if ( q.hasTag("fuzzed_data") )
68
+ this.hasFuzzed = true;
69
+
70
+ var rCost = 0;
71
+ q.messages.forEach(function(m) {
72
+ if ( m.cost && m.cost != 0) {
73
+ rCost += m.cost;
74
+ m.running_cost = rCost;
75
+ } else {
76
+ m.running_cost = undefined;
77
+ }
78
+ });
79
+ });
80
+
81
+ var f = QueryData.sortByFunc(['severityIndex', 'table']);
82
+ v.highQ = v.highQ.sort(f);
83
+ v.lowQ = v.lowQ.sort(f);
84
+ }
85
+
86
+
87
+ export default {
88
+ name: 'app',
89
+ data: () => ({
90
+ highQ: [],
91
+ lowQ: [],
92
+ tags: {},
93
+ lowExpanded: false,
94
+ hasFuzzed: false,
95
+ search: '',
96
+ url: null
97
+ }),
98
+ mounted () {
99
+ if ( typeof(shibaData) === "undefined" ) {
100
+ nanoajax.ajax({url:'/example_data.json'}, function (code, responseText) {
101
+ if ( code == 200 ) {
102
+ var data = JSON.parse(responseText);
103
+ this.setupData(data);
104
+ }
105
+ }.bind(this));
106
+ } else {
107
+ // eslint-disable-next-line
108
+ this.setupData(shibaData);
109
+ }
110
+ },
111
+ methods: {
112
+ setupData: function(data) {
113
+ this.url = data.url;
114
+ this.tags = data.tags;
115
+
116
+ Object.keys(this.tags).forEach((k) => {
117
+ registerMessage(k, this.tags[k].title, this.tags[k].summary);
118
+ })
119
+ categorizeQueries(this, data.queries);
120
+ },
121
+ updateSearch: _.debounce(function (e) {
122
+ this.search = e.target.value;
123
+ }, 500)
124
+ },
125
+ computed: {
126
+ queries: function() {
127
+ if ( this.search != '' ) {
128
+ var filtered = [];
129
+ var lcSearch = this.search.toLowerCase();
130
+ this.highQ.concat(this.lowQ).forEach(function(q) {
131
+ if ( q.searchString.includes(lcSearch) )
132
+ filtered.push(q);
133
+ });
134
+ return filtered;
135
+ } else
136
+ return this.highQ;
137
+ },
138
+ queriesLow: function() {
139
+ return this.lowQ;
140
+ }
141
+ },
142
+ components: {
143
+ Query
144
+ }
145
+ }
146
+ </script>
147
+
148
+ <style>
149
+ .sql {
150
+ font-family: monospace;
151
+ }
152
+
153
+ .badge {
154
+ color: black;
155
+ background-color: white;
156
+ border-style: solid;
157
+ border-width: 1.5px;
158
+ margin-right: 5px;
159
+ width: 100px;
160
+ }
161
+
162
+ .shiba-badge-td {
163
+ width: 100px;
164
+ }
165
+
166
+ .shiba-messages {
167
+ margin: 0px;
168
+ margin-top: 10px;
169
+ width: 100%;
170
+ }
171
+
172
+ .shiba-messages td {
173
+ }
174
+
175
+ .shiba-message {
176
+ padding-right: 10px;
177
+ width: 90%;
178
+ }
179
+
180
+ .running-totals {
181
+ align: right;
182
+ font-family: monospace;
183
+ }
184
+ [v-cloak] { display: none }
185
+ </style>
Binary file
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <div>
3
+ Stack Trace:<br>
4
+ <div class="backtrace">
5
+ <div v-for="bt in backtrace" v-bind:key="bt" v-html="makeURL(bt, bt)"></div>
6
+ </div>
7
+ </div>
8
+ </template>
9
+
10
+ <script>
11
+ export default {
12
+ name: 'Backtrace',
13
+ props: ['url', 'backtrace'],
14
+ methods: {
15
+ makeURL: function(line, content) {
16
+ if ( !this.url || !line )
17
+ return content;
18
+
19
+ var matches = line.match(/(.+):(\d+):/);
20
+ var f = matches[1].replace(/^\/+/, '');
21
+ var l = matches[2];
22
+
23
+ return `<a href='${this.url}/${f}#L${l}' target='_new'>${content}</a>`;
24
+ }
25
+ }
26
+ }
27
+ </script>
28
+ <style>
29
+ .backtrace {
30
+ font-family: monospace;
31
+ background-color: #EEEEEE;
32
+ padding: 5px;
33
+ margin: 10px
34
+ }
35
+ </style>
36
+
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <div class="hello">
3
+ <h1>{{ msg }}</h1>
4
+ <p>
5
+ For a guide and recipes on how to configure / customize this project,<br>
6
+ check out the
7
+ <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
8
+ </p>
9
+ <h3>Installed CLI Plugins</h3>
10
+ <ul>
11
+ <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
12
+ <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
13
+ </ul>
14
+ <h3>Essential Links</h3>
15
+ <ul>
16
+ <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
17
+ <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
18
+ <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
19
+ <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
20
+ <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
21
+ </ul>
22
+ <h3>Ecosystem</h3>
23
+ <ul>
24
+ <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
25
+ <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
26
+ <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
27
+ <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
28
+ <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
29
+ </ul>
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+ export default {
35
+ name: 'HelloWorld',
36
+ props: {
37
+ msg: String
38
+ }
39
+ }
40
+ </script>
41
+
42
+ <!-- Add "scoped" attribute to limit CSS to this component only -->
43
+ <style scoped>
44
+ h3 {
45
+ margin: 40px 0 0;
46
+ }
47
+ ul {
48
+ list-style-type: none;
49
+ padding: 0;
50
+ }
51
+ li {
52
+ display: inline-block;
53
+ margin: 0 10px;
54
+ }
55
+ a {
56
+ color: #42b983;
57
+ }
58
+ </style>
@@ -0,0 +1,104 @@
1
+ var template = `
2
+ <tr>
3
+ <td class="shiba-badge-td">
4
+ <a class="badge" v-bind:style="costToColor">::TITLE::</a>
5
+ </td>
6
+ <td class="shiba-message">
7
+ ::SUMMARY::
8
+ </td>
9
+ <td class="running-totals">
10
+ {{ formattedRunningCost }}
11
+ </td>
12
+ </tr>
13
+ `
14
+
15
+ var greenToRedGradient = [
16
+ '#57bb8a','#63b682', '#73b87e', '#84bb7b', '#94bd77', '#a4c073', '#b0be6e',
17
+ '#c4c56d', '#d4c86a', '#e2c965', '#f5ce62', '#f3c563', '#e9b861', '#e6ad61',
18
+ '#ecac67', '#e9a268', '#e79a69', '#e5926b', '#e2886c', '#e0816d', '#dd776e'
19
+ ];
20
+
21
+ var templateComputedFunctions = {
22
+ key_parts: function() {
23
+ if ( this.index_used && this.index_used.length > 0 )
24
+ return this.index_used.join(',');
25
+ else
26
+ return "";
27
+ },
28
+ fuzz_table_sizes: function() {
29
+ var h = {};
30
+ var tables = this.tables;
31
+
32
+ Object.keys(tables).forEach(function(k) {
33
+ var size = tables[k];
34
+ if ( !h[size] )
35
+ h[size] = [];
36
+
37
+ h[size].push(k);
38
+ });
39
+
40
+ var sizesDesc = Object.keys(h).sort(function(a, b) { return b - a });
41
+ var str = "";
42
+
43
+ sizesDesc.forEach(function(size) {
44
+ str = str + h[size].join(", ") + ": " + size.toLocaleString() + " rows. ";
45
+ });
46
+
47
+ return str;
48
+ },
49
+ formatted_cost: function() {
50
+ var readPercentage = (this.rows_read / this.table_size) * 100.0;
51
+ if ( this.rows_read > 100 && readPercentage > 1 ) // todo: make better
52
+ return `${readPercentage.toFixed()}% (${this.rows_read.toLocaleString()}) of the`;
53
+ else
54
+ return this.rows_read.toLocaleString();
55
+ },
56
+ costToColor: function() {
57
+ var costScale = this.cost ? this.cost / 0.5 : 0;
58
+
59
+ if ( costScale > 1 )
60
+ costScale = 1;
61
+
62
+ var pos = (costScale * (greenToRedGradient.length - 1)).toFixed();
63
+
64
+ return "border-color: " + greenToRedGradient[pos];
65
+ },
66
+ formattedRunningCost: function() {
67
+ if ( this.running_cost === undefined )
68
+ return "-";
69
+ else if ( this.running_cost < 1.0 )
70
+ return (this.running_cost * 100).toFixed() + "ms";
71
+ else
72
+ return this.running_cost.toFixed(1) + "s";
73
+ },
74
+ formatted_result: function() {
75
+ var rb = this.result_bytes;
76
+ var result;
77
+ if ( rb == 0 )
78
+ return "" + this.result_size + " rows";
79
+ else if ( rb < 1000 )
80
+ result = rb + " bytes ";
81
+ else if ( rb < 1000000 )
82
+ result = (rb / 1000).toFixed() + "kb ";
83
+ else
84
+ result = (rb / 1000000 ).toFixed(1) + "mb ";
85
+
86
+ return result + " (" + this.result_size.toLocaleString() + " rows)";
87
+ }
88
+ }
89
+
90
+ import Vue from 'vue'
91
+
92
+ export default function (tag, title, summary) {
93
+ var tmpl = template.replace("::TITLE::", title).replace("::SUMMARY::", summary);
94
+
95
+ Vue.component(`tag-${tag}`, {
96
+ template: tmpl,
97
+ props: [ 'table_size', 'result_size', 'table', 'cost', 'index', 'join_to', 'index_used', 'running_cost', 'tables', 'rows_read', 'result_bytes' ],
98
+ computed: templateComputedFunctions,
99
+ data: function () {
100
+ return { lastRunnningCost: undefined };
101
+ }
102
+ });
103
+ }
104
+