shiba 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+