├── .gitignore
├── LICENSE
├── README.md
├── docs
├── cte.json
├── cte_flamegraph.html
├── flamegraph.html
└── plan.json
├── go.mod
├── go.sum
├── main.go
├── pkg
├── html
│ ├── html.go
│ ├── html_test.go
│ └── template.go
└── plan
│ ├── plan.go
│ └── plan_test.go
├── static.go
├── static
├── bootstrap.min.css
├── d3-flamegraph.css
├── d3-flamegraph.min.js
├── d3-tip.min.js
├── d3.v4.min.js
├── html5shiv.min.js
└── respond.min.js
└── webview.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # The produced binary
15 | pg_flame
16 |
17 | # Common name of flamegraph output
18 | flamegraph.html
19 | pg_flame.html
20 | static.zip
21 | # Distribution directory
22 | dist
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pg_flame
2 |
3 | A flamegraph generator for Postgres `EXPLAIN ANALYZE` output.
4 |
5 | A almost rewrite version,
6 |
7 | * static.go is generated with [zipdata](https://github.com/alitrack/zipdata) from static directory.
8 | * Support offline.
9 | * Use [webview](https://github.com/webview/webview) to automatically open created html.
10 | * Support DATABASE_URL enviroment variable.
11 |
12 |
13 |
14 |
15 |
16 | ## Demo
17 |
18 | Try the demo [here](https://mgartner.github.io/pg_flame/flamegraph.html).
19 |
20 | ## Installation
21 |
22 | ### Build from source
23 |
24 | If you'd like to build a binary from the source code, run the following
25 | commands. Note that compiling requires Go version 1.13+.
26 |
27 | ```
28 | $ go get -u github.com/alitrack/pg_flame
29 | ```
30 |
31 | A `pg_flame` binary will be created that you can place in your `$GOPATH/bin`.
32 |
33 | ## Usage
34 |
35 | ```bash
36 | usage: pg_flame []
37 |
38 | A flamegraph generator for Postgres EXPLAIN ANALYZE output.
39 |
40 | Flags:
41 | --help Show context-sensitive help (also try --help-long and --help-man).
42 | -U, --username="steven" database user name
43 | -h, --host="localhost" database server host or socket directory
44 | -p, --port=5432 database server port
45 | --sslmode="disable" database server sslmode
46 | --password=PASSWORD database server password
47 | --dbname="postgres" database name
48 | -o, --output="pg_flame.html" output html file
49 | -c, --command=COMMAND run only single command (SQL)
50 | -f, --file=FILE execute commands from file
51 | -s, --show_browser Launch browser if successful
52 | --version Show application version.
53 | ```
54 |
55 | ### Example: from sql command
56 |
57 | ```bash
58 | $ pg_flame -c 'EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT id FROM users'
59 | ```
60 |
61 | ### Example: from sql file
62 |
63 | Create a SQL file with the `EXPLAIN ANALYZE` query.
64 |
65 | ```sql
66 | -- query.sql
67 | EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
68 | SELECT id
69 | FROM users
70 | ```
71 |
72 | Then run the query and save the JSON to a file.
73 |
74 | ```bash
75 | $ pg_flame -f query.sql
76 | ```
77 |
78 | ## Background
79 |
80 | [Flamegraphs](http://www.brendangregg.com/flamegraphs.html) were invented by
81 | Brendan Gregg to visualize CPU consumption per code-path of profiled software.
82 | They are useful visualization tools in many types of performance
83 | investigations. Flamegraphs have been used to visualize Oracle database
84 | [query
85 | plans](https://blog.tanelpoder.com/posts/visualizing-sql-plan-execution-time-with-flamegraphs/)
86 | and [query
87 | executions](https://externaltable.blogspot.com/2014/05/flame-graphs-for-oracle.html)
88 | , proving useful for debugging slow database queries.
89 |
90 | Pg_flame is in extension of that work for Postgres query plans. It generates a
91 | visual hierarchy of query plans. This visualization identifies the relative
92 | time of each part of a query plan.
93 |
94 | This tool relies on the
95 | [`spiermar/d3-flame-graph`](https://github.com/spiermar/d3-flame-graph) plugin to
96 | generate the flamegraph.
97 |
--------------------------------------------------------------------------------
/docs/cte.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Plan": {
4 | "Node Type": "Aggregate",
5 | "Strategy": "Sorted",
6 | "Partial Mode": "Simple",
7 | "Parallel Aware": false,
8 | "Startup Cost": 15552.51,
9 | "Total Cost": 15554.36,
10 | "Plan Rows": 1,
11 | "Plan Width": 33,
12 | "Actual Startup Time": 755.383,
13 | "Actual Total Time": 755.383,
14 | "Actual Rows": 0,
15 | "Actual Loops": 1,
16 | "Group Key": ["u.id"],
17 | "Filter": "(count(*) > 1)",
18 | "Rows Removed by Filter": 0,
19 | "Shared Hit Blocks": 2957,
20 | "Shared Read Blocks": 0,
21 | "Shared Dirtied Blocks": 0,
22 | "Shared Written Blocks": 0,
23 | "Local Hit Blocks": 0,
24 | "Local Read Blocks": 0,
25 | "Local Dirtied Blocks": 0,
26 | "Local Written Blocks": 0,
27 | "Temp Read Blocks": 0,
28 | "Temp Written Blocks": 0,
29 | "Plans": [
30 | {
31 | "Node Type": "Seq Scan",
32 | "Parent Relationship": "InitPlan",
33 | "Subplan Name": "CTE sql_posts",
34 | "Parallel Aware": false,
35 | "Relation Name": "posts",
36 | "Alias": "posts",
37 | "Startup Cost": 0.00,
38 | "Total Cost": 209.00,
39 | "Plan Rows": 4971,
40 | "Plan Width": 36,
41 | "Actual Startup Time": 0.023,
42 | "Actual Total Time": 0.023,
43 | "Actual Rows": 1,
44 | "Actual Loops": 1,
45 | "Filter": "((title)::text ~ '.*sql.*'::text)",
46 | "Rows Removed by Filter": 1,
47 | "Shared Hit Blocks": 1,
48 | "Shared Read Blocks": 0,
49 | "Shared Dirtied Blocks": 0,
50 | "Shared Written Blocks": 0,
51 | "Local Hit Blocks": 0,
52 | "Local Read Blocks": 0,
53 | "Local Dirtied Blocks": 0,
54 | "Local Written Blocks": 0,
55 | "Temp Read Blocks": 0,
56 | "Temp Written Blocks": 0
57 | },
58 | {
59 | "Node Type": "Seq Scan",
60 | "Parent Relationship": "InitPlan",
61 | "Subplan Name": "CTE postgres_posts",
62 | "Parallel Aware": false,
63 | "Relation Name": "posts",
64 | "Alias": "posts_1",
65 | "Startup Cost": 0.00,
66 | "Total Cost": 209.00,
67 | "Plan Rows": 2481,
68 | "Plan Width": 36,
69 | "Actual Startup Time": 0.032,
70 | "Actual Total Time": 0.032,
71 | "Actual Rows": 1,
72 | "Actual Loops": 1,
73 | "Filter": "((title)::text ~ '.*postgres.*'::text)",
74 | "Rows Removed by Filter": 13,
75 | "Shared Hit Blocks": 1,
76 | "Shared Read Blocks": 0,
77 | "Shared Dirtied Blocks": 0,
78 | "Shared Written Blocks": 0,
79 | "Local Hit Blocks": 0,
80 | "Local Read Blocks": 0,
81 | "Local Dirtied Blocks": 0,
82 | "Local Written Blocks": 0,
83 | "Temp Read Blocks": 0,
84 | "Temp Written Blocks": 0
85 | },
86 | {
87 | "Node Type": "Seq Scan",
88 | "Parent Relationship": "InitPlan",
89 | "Subplan Name": "CTE database_posts",
90 | "Parallel Aware": false,
91 | "Relation Name": "posts",
92 | "Alias": "posts_2",
93 | "Startup Cost": 0.00,
94 | "Total Cost": 209.00,
95 | "Plan Rows": 2548,
96 | "Plan Width": 36,
97 | "Actual Startup Time": 0.010,
98 | "Actual Total Time": 0.010,
99 | "Actual Rows": 1,
100 | "Actual Loops": 1,
101 | "Filter": "((title)::text ~ '.*databases.*'::text)",
102 | "Rows Removed by Filter": 0,
103 | "Shared Hit Blocks": 1,
104 | "Shared Read Blocks": 0,
105 | "Shared Dirtied Blocks": 0,
106 | "Shared Written Blocks": 0,
107 | "Local Hit Blocks": 0,
108 | "Local Read Blocks": 0,
109 | "Local Dirtied Blocks": 0,
110 | "Local Written Blocks": 0,
111 | "Temp Read Blocks": 0,
112 | "Temp Written Blocks": 0
113 | },
114 | {
115 | "Node Type": "Nested Loop",
116 | "Parent Relationship": "InitPlan",
117 | "Subplan Name": "CTE performance_comments",
118 | "Parallel Aware": false,
119 | "Join Type": "Inner",
120 | "Startup Cost": 0.29,
121 | "Total Cost": 2242.31,
122 | "Plan Rows": 1,
123 | "Plan Width": 25,
124 | "Actual Startup Time": 223.165,
125 | "Actual Total Time": 223.165,
126 | "Actual Rows": 0,
127 | "Actual Loops": 1,
128 | "Inner Unique": true,
129 | "Shared Hit Blocks": 984,
130 | "Shared Read Blocks": 0,
131 | "Shared Dirtied Blocks": 0,
132 | "Shared Written Blocks": 0,
133 | "Local Hit Blocks": 0,
134 | "Local Read Blocks": 0,
135 | "Local Dirtied Blocks": 0,
136 | "Local Written Blocks": 0,
137 | "Temp Read Blocks": 0,
138 | "Temp Written Blocks": 0,
139 | "Plans": [
140 | {
141 | "Node Type": "Seq Scan",
142 | "Parent Relationship": "Outer",
143 | "Parallel Aware": false,
144 | "Relation Name": "comments",
145 | "Alias": "c",
146 | "Startup Cost": 0.00,
147 | "Total Cost": 2234.00,
148 | "Plan Rows": 1,
149 | "Plan Width": 4,
150 | "Actual Startup Time": 223.164,
151 | "Actual Total Time": 223.164,
152 | "Actual Rows": 0,
153 | "Actual Loops": 1,
154 | "Filter": "((body)::text ~ '.*performance.*'::text)",
155 | "Rows Removed by Filter": 100000,
156 | "Shared Hit Blocks": 984,
157 | "Shared Read Blocks": 0,
158 | "Shared Dirtied Blocks": 0,
159 | "Shared Written Blocks": 0,
160 | "Local Hit Blocks": 0,
161 | "Local Read Blocks": 0,
162 | "Local Dirtied Blocks": 0,
163 | "Local Written Blocks": 0,
164 | "Temp Read Blocks": 0,
165 | "Temp Written Blocks": 0
166 | },
167 | {
168 | "Node Type": "Index Scan",
169 | "Parent Relationship": "Inner",
170 | "Parallel Aware": false,
171 | "Scan Direction": "Forward",
172 | "Index Name": "users_pkey",
173 | "Relation Name": "users",
174 | "Alias": "u_1",
175 | "Startup Cost": 0.29,
176 | "Total Cost": 8.31,
177 | "Plan Rows": 1,
178 | "Plan Width": 25,
179 | "Actual Startup Time": 0.000,
180 | "Actual Total Time": 0.000,
181 | "Actual Rows": 0,
182 | "Actual Loops": 0,
183 | "Index Cond": "(id = c.user_id)",
184 | "Rows Removed by Index Recheck": 0,
185 | "Shared Hit Blocks": 0,
186 | "Shared Read Blocks": 0,
187 | "Shared Dirtied Blocks": 0,
188 | "Shared Written Blocks": 0,
189 | "Local Hit Blocks": 0,
190 | "Local Read Blocks": 0,
191 | "Local Dirtied Blocks": 0,
192 | "Local Written Blocks": 0,
193 | "Temp Read Blocks": 0,
194 | "Temp Written Blocks": 0
195 | }
196 | ]
197 | },
198 | {
199 | "Node Type": "Hash Join",
200 | "Parent Relationship": "InitPlan",
201 | "Subplan Name": "CTE testing_comments",
202 | "Parallel Aware": false,
203 | "Join Type": "Inner",
204 | "Startup Cost": 2861.59,
205 | "Total Cost": 5594.66,
206 | "Plan Rows": 50207,
207 | "Plan Width": 25,
208 | "Actual Startup Time": 252.949,
209 | "Actual Total Time": 252.949,
210 | "Actual Rows": 1,
211 | "Actual Loops": 1,
212 | "Inner Unique": false,
213 | "Hash Cond": "(u_2.id = c_1.user_id)",
214 | "Shared Hit Blocks": 985,
215 | "Shared Read Blocks": 0,
216 | "Shared Dirtied Blocks": 0,
217 | "Shared Written Blocks": 0,
218 | "Local Hit Blocks": 0,
219 | "Local Read Blocks": 0,
220 | "Local Dirtied Blocks": 0,
221 | "Local Written Blocks": 0,
222 | "Temp Read Blocks": 0,
223 | "Temp Written Blocks": 0,
224 | "Plans": [
225 | {
226 | "Node Type": "Seq Scan",
227 | "Parent Relationship": "Outer",
228 | "Parallel Aware": false,
229 | "Relation Name": "users",
230 | "Alias": "u_2",
231 | "Startup Cost": 0.00,
232 | "Total Cost": 1731.00,
233 | "Plan Rows": 100000,
234 | "Plan Width": 25,
235 | "Actual Startup Time": 0.016,
236 | "Actual Total Time": 0.017,
237 | "Actual Rows": 3,
238 | "Actual Loops": 1,
239 | "Shared Hit Blocks": 1,
240 | "Shared Read Blocks": 0,
241 | "Shared Dirtied Blocks": 0,
242 | "Shared Written Blocks": 0,
243 | "Local Hit Blocks": 0,
244 | "Local Read Blocks": 0,
245 | "Local Dirtied Blocks": 0,
246 | "Local Written Blocks": 0,
247 | "Temp Read Blocks": 0,
248 | "Temp Written Blocks": 0
249 | },
250 | {
251 | "Node Type": "Hash",
252 | "Parent Relationship": "Inner",
253 | "Parallel Aware": false,
254 | "Startup Cost": 2234.00,
255 | "Total Cost": 2234.00,
256 | "Plan Rows": 50207,
257 | "Plan Width": 4,
258 | "Actual Startup Time": 252.611,
259 | "Actual Total Time": 252.611,
260 | "Actual Rows": 49998,
261 | "Actual Loops": 1,
262 | "Hash Buckets": 65536,
263 | "Original Hash Buckets": 65536,
264 | "Hash Batches": 1,
265 | "Original Hash Batches": 1,
266 | "Peak Memory Usage": 2270,
267 | "Shared Hit Blocks": 984,
268 | "Shared Read Blocks": 0,
269 | "Shared Dirtied Blocks": 0,
270 | "Shared Written Blocks": 0,
271 | "Local Hit Blocks": 0,
272 | "Local Read Blocks": 0,
273 | "Local Dirtied Blocks": 0,
274 | "Local Written Blocks": 0,
275 | "Temp Read Blocks": 0,
276 | "Temp Written Blocks": 0,
277 | "Plans": [
278 | {
279 | "Node Type": "Seq Scan",
280 | "Parent Relationship": "Outer",
281 | "Parallel Aware": false,
282 | "Relation Name": "comments",
283 | "Alias": "c_1",
284 | "Startup Cost": 0.00,
285 | "Total Cost": 2234.00,
286 | "Plan Rows": 50207,
287 | "Plan Width": 4,
288 | "Actual Startup Time": 0.035,
289 | "Actual Total Time": 239.699,
290 | "Actual Rows": 49998,
291 | "Actual Loops": 1,
292 | "Filter": "((body)::text ~ '.*testing.*'::text)",
293 | "Rows Removed by Filter": 50002,
294 | "Shared Hit Blocks": 984,
295 | "Shared Read Blocks": 0,
296 | "Shared Dirtied Blocks": 0,
297 | "Shared Written Blocks": 0,
298 | "Local Hit Blocks": 0,
299 | "Local Read Blocks": 0,
300 | "Local Dirtied Blocks": 0,
301 | "Local Written Blocks": 0,
302 | "Temp Read Blocks": 0,
303 | "Temp Written Blocks": 0
304 | }
305 | ]
306 | }
307 | ]
308 | },
309 | {
310 | "Node Type": "Hash Join",
311 | "Parent Relationship": "InitPlan",
312 | "Subplan Name": "CTE modularity_comments",
313 | "Parallel Aware": false,
314 | "Join Type": "Inner",
315 | "Startup Cost": 2545.59,
316 | "Total Cost": 5025.86,
317 | "Plan Rows": 24927,
318 | "Plan Width": 25,
319 | "Actual Startup Time": 279.072,
320 | "Actual Total Time": 279.073,
321 | "Actual Rows": 1,
322 | "Actual Loops": 1,
323 | "Inner Unique": false,
324 | "Hash Cond": "(u_3.id = c_2.user_id)",
325 | "Shared Hit Blocks": 985,
326 | "Shared Read Blocks": 0,
327 | "Shared Dirtied Blocks": 0,
328 | "Shared Written Blocks": 0,
329 | "Local Hit Blocks": 0,
330 | "Local Read Blocks": 0,
331 | "Local Dirtied Blocks": 0,
332 | "Local Written Blocks": 0,
333 | "Temp Read Blocks": 0,
334 | "Temp Written Blocks": 0,
335 | "Plans": [
336 | {
337 | "Node Type": "Seq Scan",
338 | "Parent Relationship": "Outer",
339 | "Parallel Aware": false,
340 | "Relation Name": "users",
341 | "Alias": "u_3",
342 | "Startup Cost": 0.00,
343 | "Total Cost": 1731.00,
344 | "Plan Rows": 100000,
345 | "Plan Width": 25,
346 | "Actual Startup Time": 0.012,
347 | "Actual Total Time": 0.012,
348 | "Actual Rows": 1,
349 | "Actual Loops": 1,
350 | "Shared Hit Blocks": 1,
351 | "Shared Read Blocks": 0,
352 | "Shared Dirtied Blocks": 0,
353 | "Shared Written Blocks": 0,
354 | "Local Hit Blocks": 0,
355 | "Local Read Blocks": 0,
356 | "Local Dirtied Blocks": 0,
357 | "Local Written Blocks": 0,
358 | "Temp Read Blocks": 0,
359 | "Temp Written Blocks": 0
360 | },
361 | {
362 | "Node Type": "Hash",
363 | "Parent Relationship": "Inner",
364 | "Parallel Aware": false,
365 | "Startup Cost": 2234.00,
366 | "Total Cost": 2234.00,
367 | "Plan Rows": 24927,
368 | "Plan Width": 4,
369 | "Actual Startup Time": 278.911,
370 | "Actual Total Time": 278.911,
371 | "Actual Rows": 25058,
372 | "Actual Loops": 1,
373 | "Hash Buckets": 32768,
374 | "Original Hash Buckets": 32768,
375 | "Hash Batches": 1,
376 | "Original Hash Batches": 1,
377 | "Peak Memory Usage": 1137,
378 | "Shared Hit Blocks": 984,
379 | "Shared Read Blocks": 0,
380 | "Shared Dirtied Blocks": 0,
381 | "Shared Written Blocks": 0,
382 | "Local Hit Blocks": 0,
383 | "Local Read Blocks": 0,
384 | "Local Dirtied Blocks": 0,
385 | "Local Written Blocks": 0,
386 | "Temp Read Blocks": 0,
387 | "Temp Written Blocks": 0,
388 | "Plans": [
389 | {
390 | "Node Type": "Seq Scan",
391 | "Parent Relationship": "Outer",
392 | "Parallel Aware": false,
393 | "Relation Name": "comments",
394 | "Alias": "c_2",
395 | "Startup Cost": 0.00,
396 | "Total Cost": 2234.00,
397 | "Plan Rows": 24927,
398 | "Plan Width": 4,
399 | "Actual Startup Time": 0.025,
400 | "Actual Total Time": 271.456,
401 | "Actual Rows": 25058,
402 | "Actual Loops": 1,
403 | "Filter": "((body)::text ~ '.*modularity.*'::text)",
404 | "Rows Removed by Filter": 74942,
405 | "Shared Hit Blocks": 984,
406 | "Shared Read Blocks": 0,
407 | "Shared Dirtied Blocks": 0,
408 | "Shared Written Blocks": 0,
409 | "Local Hit Blocks": 0,
410 | "Local Read Blocks": 0,
411 | "Local Dirtied Blocks": 0,
412 | "Local Written Blocks": 0,
413 | "Temp Read Blocks": 0,
414 | "Temp Written Blocks": 0
415 | }
416 | ]
417 | }
418 | ]
419 | },
420 | {
421 | "Node Type": "Index Scan",
422 | "Parent Relationship": "InitPlan",
423 | "Subplan Name": "CTE early_gmail_users",
424 | "Parallel Aware": false,
425 | "Scan Direction": "Forward",
426 | "Index Name": "users_pkey",
427 | "Relation Name": "users",
428 | "Alias": "users",
429 | "Startup Cost": 0.29,
430 | "Total Cost": 10.23,
431 | "Plan Rows": 25,
432 | "Plan Width": 4,
433 | "Actual Startup Time": 0.000,
434 | "Actual Total Time": 0.000,
435 | "Actual Rows": 0,
436 | "Actual Loops": 0,
437 | "Index Cond": "(id < 100)",
438 | "Rows Removed by Index Recheck": 0,
439 | "Filter": "((email)::text ~ '.*gmail.*'::text)",
440 | "Rows Removed by Filter": 0,
441 | "Shared Hit Blocks": 0,
442 | "Shared Read Blocks": 0,
443 | "Shared Dirtied Blocks": 0,
444 | "Shared Written Blocks": 0,
445 | "Local Hit Blocks": 0,
446 | "Local Read Blocks": 0,
447 | "Local Dirtied Blocks": 0,
448 | "Local Written Blocks": 0,
449 | "Temp Read Blocks": 0,
450 | "Temp Written Blocks": 0
451 | },
452 | {
453 | "Node Type": "Index Scan",
454 | "Parent Relationship": "InitPlan",
455 | "Subplan Name": "CTE early_yahoo_users",
456 | "Parallel Aware": false,
457 | "Scan Direction": "Forward",
458 | "Index Name": "users_pkey",
459 | "Relation Name": "users",
460 | "Alias": "users_1",
461 | "Startup Cost": 0.29,
462 | "Total Cost": 10.23,
463 | "Plan Rows": 16,
464 | "Plan Width": 4,
465 | "Actual Startup Time": 0.000,
466 | "Actual Total Time": 0.000,
467 | "Actual Rows": 0,
468 | "Actual Loops": 0,
469 | "Index Cond": "(id < 100)",
470 | "Rows Removed by Index Recheck": 0,
471 | "Filter": "((email)::text ~ '.*yahoo.*'::text)",
472 | "Rows Removed by Filter": 0,
473 | "Shared Hit Blocks": 0,
474 | "Shared Read Blocks": 0,
475 | "Shared Dirtied Blocks": 0,
476 | "Shared Written Blocks": 0,
477 | "Local Hit Blocks": 0,
478 | "Local Read Blocks": 0,
479 | "Local Dirtied Blocks": 0,
480 | "Local Written Blocks": 0,
481 | "Temp Read Blocks": 0,
482 | "Temp Written Blocks": 0
483 | },
484 | {
485 | "Node Type": "Index Scan",
486 | "Parent Relationship": "InitPlan",
487 | "Subplan Name": "CTE early_hotmail_users",
488 | "Parallel Aware": false,
489 | "Scan Direction": "Forward",
490 | "Index Name": "users_pkey",
491 | "Relation Name": "users",
492 | "Alias": "users_2",
493 | "Startup Cost": 0.29,
494 | "Total Cost": 10.23,
495 | "Plan Rows": 56,
496 | "Plan Width": 4,
497 | "Actual Startup Time": 0.000,
498 | "Actual Total Time": 0.000,
499 | "Actual Rows": 0,
500 | "Actual Loops": 0,
501 | "Index Cond": "(id < 100)",
502 | "Rows Removed by Index Recheck": 0,
503 | "Filter": "((email)::text ~ '.*hotmail.*'::text)",
504 | "Rows Removed by Filter": 0,
505 | "Shared Hit Blocks": 0,
506 | "Shared Read Blocks": 0,
507 | "Shared Dirtied Blocks": 0,
508 | "Shared Written Blocks": 0,
509 | "Local Hit Blocks": 0,
510 | "Local Read Blocks": 0,
511 | "Local Dirtied Blocks": 0,
512 | "Local Written Blocks": 0,
513 | "Temp Read Blocks": 0,
514 | "Temp Written Blocks": 0
515 | },
516 | {
517 | "Node Type": "Nested Loop",
518 | "Parent Relationship": "Outer",
519 | "Parallel Aware": false,
520 | "Join Type": "Inner",
521 | "Startup Cost": 2031.99,
522 | "Total Cost": 2033.82,
523 | "Plan Rows": 1,
524 | "Plan Width": 25,
525 | "Actual Startup Time": 755.380,
526 | "Actual Total Time": 755.380,
527 | "Actual Rows": 0,
528 | "Actual Loops": 1,
529 | "Inner Unique": false,
530 | "Join Filter": "(eyu.id = u.id)",
531 | "Rows Removed by Join Filter": 0,
532 | "Shared Hit Blocks": 2957,
533 | "Shared Read Blocks": 0,
534 | "Shared Dirtied Blocks": 0,
535 | "Shared Written Blocks": 0,
536 | "Local Hit Blocks": 0,
537 | "Local Read Blocks": 0,
538 | "Local Dirtied Blocks": 0,
539 | "Local Written Blocks": 0,
540 | "Temp Read Blocks": 0,
541 | "Temp Written Blocks": 0,
542 | "Plans": [
543 | {
544 | "Node Type": "Merge Join",
545 | "Parent Relationship": "Outer",
546 | "Parallel Aware": false,
547 | "Join Type": "Inner",
548 | "Startup Cost": 2031.47,
549 | "Total Cost": 2031.61,
550 | "Plan Rows": 1,
551 | "Plan Width": 53,
552 | "Actual Startup Time": 755.379,
553 | "Actual Total Time": 755.380,
554 | "Actual Rows": 0,
555 | "Actual Loops": 1,
556 | "Inner Unique": false,
557 | "Merge Cond": "(u.id = egu.id)",
558 | "Shared Hit Blocks": 2957,
559 | "Shared Read Blocks": 0,
560 | "Shared Dirtied Blocks": 0,
561 | "Shared Written Blocks": 0,
562 | "Local Hit Blocks": 0,
563 | "Local Read Blocks": 0,
564 | "Local Dirtied Blocks": 0,
565 | "Local Written Blocks": 0,
566 | "Temp Read Blocks": 0,
567 | "Temp Written Blocks": 0,
568 | "Plans": [
569 | {
570 | "Node Type": "Sort",
571 | "Parent Relationship": "Outer",
572 | "Parallel Aware": false,
573 | "Startup Cost": 2030.39,
574 | "Total Cost": 2030.39,
575 | "Plan Rows": 1,
576 | "Plan Width": 49,
577 | "Actual Startup Time": 755.378,
578 | "Actual Total Time": 755.379,
579 | "Actual Rows": 0,
580 | "Actual Loops": 1,
581 | "Sort Key": ["u.id"],
582 | "Sort Method": "quicksort",
583 | "Sort Space Used": 25,
584 | "Sort Space Type": "Memory",
585 | "Shared Hit Blocks": 2957,
586 | "Shared Read Blocks": 0,
587 | "Shared Dirtied Blocks": 0,
588 | "Shared Written Blocks": 0,
589 | "Local Hit Blocks": 0,
590 | "Local Read Blocks": 0,
591 | "Local Dirtied Blocks": 0,
592 | "Local Written Blocks": 0,
593 | "Temp Read Blocks": 0,
594 | "Temp Written Blocks": 0,
595 | "Plans": [
596 | {
597 | "Node Type": "Hash Join",
598 | "Parent Relationship": "Outer",
599 | "Parallel Aware": false,
600 | "Join Type": "Inner",
601 | "Startup Cost": 837.95,
602 | "Total Cost": 2030.38,
603 | "Plan Rows": 1,
604 | "Plan Width": 49,
605 | "Actual Startup Time": 755.357,
606 | "Actual Total Time": 755.357,
607 | "Actual Rows": 0,
608 | "Actual Loops": 1,
609 | "Inner Unique": false,
610 | "Hash Cond": "(tc.user_id = u.id)",
611 | "Shared Hit Blocks": 2957,
612 | "Shared Read Blocks": 0,
613 | "Shared Dirtied Blocks": 0,
614 | "Shared Written Blocks": 0,
615 | "Local Hit Blocks": 0,
616 | "Local Read Blocks": 0,
617 | "Local Dirtied Blocks": 0,
618 | "Local Written Blocks": 0,
619 | "Temp Read Blocks": 0,
620 | "Temp Written Blocks": 0,
621 | "Plans": [
622 | {
623 | "Node Type": "CTE Scan",
624 | "Parent Relationship": "Outer",
625 | "Parallel Aware": false,
626 | "CTE Name": "testing_comments",
627 | "Alias": "tc",
628 | "Startup Cost": 0.00,
629 | "Total Cost": 1004.14,
630 | "Plan Rows": 50207,
631 | "Plan Width": 4,
632 | "Actual Startup Time": 252.951,
633 | "Actual Total Time": 252.951,
634 | "Actual Rows": 1,
635 | "Actual Loops": 1,
636 | "Shared Hit Blocks": 985,
637 | "Shared Read Blocks": 0,
638 | "Shared Dirtied Blocks": 0,
639 | "Shared Written Blocks": 0,
640 | "Local Hit Blocks": 0,
641 | "Local Read Blocks": 0,
642 | "Local Dirtied Blocks": 0,
643 | "Local Written Blocks": 0,
644 | "Temp Read Blocks": 0,
645 | "Temp Written Blocks": 0
646 | },
647 | {
648 | "Node Type": "Hash",
649 | "Parent Relationship": "Inner",
650 | "Parallel Aware": false,
651 | "Startup Cost": 837.94,
652 | "Total Cost": 837.94,
653 | "Plan Rows": 1,
654 | "Plan Width": 45,
655 | "Actual Startup Time": 502.388,
656 | "Actual Total Time": 502.388,
657 | "Actual Rows": 0,
658 | "Actual Loops": 1,
659 | "Hash Buckets": 1024,
660 | "Original Hash Buckets": 1024,
661 | "Hash Batches": 1,
662 | "Original Hash Batches": 1,
663 | "Peak Memory Usage": 8,
664 | "Shared Hit Blocks": 1972,
665 | "Shared Read Blocks": 0,
666 | "Shared Dirtied Blocks": 0,
667 | "Shared Written Blocks": 0,
668 | "Local Hit Blocks": 0,
669 | "Local Read Blocks": 0,
670 | "Local Dirtied Blocks": 0,
671 | "Local Written Blocks": 0,
672 | "Temp Read Blocks": 0,
673 | "Temp Written Blocks": 0,
674 | "Plans": [
675 | {
676 | "Node Type": "Hash Join",
677 | "Parent Relationship": "Outer",
678 | "Parallel Aware": false,
679 | "Join Type": "Inner",
680 | "Startup Cost": 245.91,
681 | "Total Cost": 837.94,
682 | "Plan Rows": 1,
683 | "Plan Width": 45,
684 | "Actual Startup Time": 502.387,
685 | "Actual Total Time": 502.387,
686 | "Actual Rows": 0,
687 | "Actual Loops": 1,
688 | "Inner Unique": false,
689 | "Hash Cond": "(mc.user_id = u.id)",
690 | "Shared Hit Blocks": 1972,
691 | "Shared Read Blocks": 0,
692 | "Shared Dirtied Blocks": 0,
693 | "Shared Written Blocks": 0,
694 | "Local Hit Blocks": 0,
695 | "Local Read Blocks": 0,
696 | "Local Dirtied Blocks": 0,
697 | "Local Written Blocks": 0,
698 | "Temp Read Blocks": 0,
699 | "Temp Written Blocks": 0,
700 | "Plans": [
701 | {
702 | "Node Type": "CTE Scan",
703 | "Parent Relationship": "Outer",
704 | "Parallel Aware": false,
705 | "CTE Name": "modularity_comments",
706 | "Alias": "mc",
707 | "Startup Cost": 0.00,
708 | "Total Cost": 498.54,
709 | "Plan Rows": 24927,
710 | "Plan Width": 4,
711 | "Actual Startup Time": 279.075,
712 | "Actual Total Time": 279.075,
713 | "Actual Rows": 1,
714 | "Actual Loops": 1,
715 | "Shared Hit Blocks": 985,
716 | "Shared Read Blocks": 0,
717 | "Shared Dirtied Blocks": 0,
718 | "Shared Written Blocks": 0,
719 | "Local Hit Blocks": 0,
720 | "Local Read Blocks": 0,
721 | "Local Dirtied Blocks": 0,
722 | "Local Written Blocks": 0,
723 | "Temp Read Blocks": 0,
724 | "Temp Written Blocks": 0
725 | },
726 | {
727 | "Node Type": "Hash",
728 | "Parent Relationship": "Inner",
729 | "Parallel Aware": false,
730 | "Startup Cost": 245.90,
731 | "Total Cost": 245.90,
732 | "Plan Rows": 1,
733 | "Plan Width": 41,
734 | "Actual Startup Time": 223.292,
735 | "Actual Total Time": 223.292,
736 | "Actual Rows": 0,
737 | "Actual Loops": 1,
738 | "Hash Buckets": 1024,
739 | "Original Hash Buckets": 1024,
740 | "Hash Batches": 1,
741 | "Original Hash Batches": 1,
742 | "Peak Memory Usage": 8,
743 | "Shared Hit Blocks": 987,
744 | "Shared Read Blocks": 0,
745 | "Shared Dirtied Blocks": 0,
746 | "Shared Written Blocks": 0,
747 | "Local Hit Blocks": 0,
748 | "Local Read Blocks": 0,
749 | "Local Dirtied Blocks": 0,
750 | "Local Written Blocks": 0,
751 | "Temp Read Blocks": 0,
752 | "Temp Written Blocks": 0,
753 | "Plans": [
754 | {
755 | "Node Type": "Hash Join",
756 | "Parent Relationship": "Outer",
757 | "Parallel Aware": false,
758 | "Join Type": "Inner",
759 | "Startup Cost": 127.83,
760 | "Total Cost": 245.90,
761 | "Plan Rows": 1,
762 | "Plan Width": 41,
763 | "Actual Startup Time": 223.291,
764 | "Actual Total Time": 223.291,
765 | "Actual Rows": 0,
766 | "Actual Loops": 1,
767 | "Inner Unique": false,
768 | "Hash Cond": "(sp.user_id = u.id)",
769 | "Shared Hit Blocks": 987,
770 | "Shared Read Blocks": 0,
771 | "Shared Dirtied Blocks": 0,
772 | "Shared Written Blocks": 0,
773 | "Local Hit Blocks": 0,
774 | "Local Read Blocks": 0,
775 | "Local Dirtied Blocks": 0,
776 | "Local Written Blocks": 0,
777 | "Temp Read Blocks": 0,
778 | "Temp Written Blocks": 0,
779 | "Plans": [
780 | {
781 | "Node Type": "CTE Scan",
782 | "Parent Relationship": "Outer",
783 | "Parallel Aware": false,
784 | "CTE Name": "sql_posts",
785 | "Alias": "sp",
786 | "Startup Cost": 0.00,
787 | "Total Cost": 99.42,
788 | "Plan Rows": 4971,
789 | "Plan Width": 4,
790 | "Actual Startup Time": 0.025,
791 | "Actual Total Time": 0.025,
792 | "Actual Rows": 1,
793 | "Actual Loops": 1,
794 | "Shared Hit Blocks": 1,
795 | "Shared Read Blocks": 0,
796 | "Shared Dirtied Blocks": 0,
797 | "Shared Written Blocks": 0,
798 | "Local Hit Blocks": 0,
799 | "Local Read Blocks": 0,
800 | "Local Dirtied Blocks": 0,
801 | "Local Written Blocks": 0,
802 | "Temp Read Blocks": 0,
803 | "Temp Written Blocks": 0
804 | },
805 | {
806 | "Node Type": "Hash",
807 | "Parent Relationship": "Inner",
808 | "Parallel Aware": false,
809 | "Startup Cost": 127.81,
810 | "Total Cost": 127.81,
811 | "Plan Rows": 1,
812 | "Plan Width": 37,
813 | "Actual Startup Time": 223.252,
814 | "Actual Total Time": 223.252,
815 | "Actual Rows": 0,
816 | "Actual Loops": 1,
817 | "Hash Buckets": 1024,
818 | "Original Hash Buckets": 1024,
819 | "Hash Batches": 1,
820 | "Original Hash Batches": 1,
821 | "Peak Memory Usage": 8,
822 | "Shared Hit Blocks": 986,
823 | "Shared Read Blocks": 0,
824 | "Shared Dirtied Blocks": 0,
825 | "Shared Written Blocks": 0,
826 | "Local Hit Blocks": 0,
827 | "Local Read Blocks": 0,
828 | "Local Dirtied Blocks": 0,
829 | "Local Written Blocks": 0,
830 | "Temp Read Blocks": 0,
831 | "Temp Written Blocks": 0,
832 | "Plans": [
833 | {
834 | "Node Type": "Hash Join",
835 | "Parent Relationship": "Outer",
836 | "Parallel Aware": false,
837 | "Join Type": "Inner",
838 | "Startup Cost": 67.29,
839 | "Total Cost": 127.81,
840 | "Plan Rows": 1,
841 | "Plan Width": 37,
842 | "Actual Startup Time": 223.252,
843 | "Actual Total Time": 223.252,
844 | "Actual Rows": 0,
845 | "Actual Loops": 1,
846 | "Inner Unique": false,
847 | "Hash Cond": "(dp.user_id = u.id)",
848 | "Shared Hit Blocks": 986,
849 | "Shared Read Blocks": 0,
850 | "Shared Dirtied Blocks": 0,
851 | "Shared Written Blocks": 0,
852 | "Local Hit Blocks": 0,
853 | "Local Read Blocks": 0,
854 | "Local Dirtied Blocks": 0,
855 | "Local Written Blocks": 0,
856 | "Temp Read Blocks": 0,
857 | "Temp Written Blocks": 0,
858 | "Plans": [
859 | {
860 | "Node Type": "CTE Scan",
861 | "Parent Relationship": "Outer",
862 | "Parallel Aware": false,
863 | "CTE Name": "database_posts",
864 | "Alias": "dp",
865 | "Startup Cost": 0.00,
866 | "Total Cost": 50.96,
867 | "Plan Rows": 2548,
868 | "Plan Width": 4,
869 | "Actual Startup Time": 0.011,
870 | "Actual Total Time": 0.011,
871 | "Actual Rows": 1,
872 | "Actual Loops": 1,
873 | "Shared Hit Blocks": 1,
874 | "Shared Read Blocks": 0,
875 | "Shared Dirtied Blocks": 0,
876 | "Shared Written Blocks": 0,
877 | "Local Hit Blocks": 0,
878 | "Local Read Blocks": 0,
879 | "Local Dirtied Blocks": 0,
880 | "Local Written Blocks": 0,
881 | "Temp Read Blocks": 0,
882 | "Temp Written Blocks": 0
883 | },
884 | {
885 | "Node Type": "Hash",
886 | "Parent Relationship": "Inner",
887 | "Parallel Aware": false,
888 | "Startup Cost": 67.28,
889 | "Total Cost": 67.28,
890 | "Plan Rows": 1,
891 | "Plan Width": 33,
892 | "Actual Startup Time": 223.214,
893 | "Actual Total Time": 223.214,
894 | "Actual Rows": 0,
895 | "Actual Loops": 1,
896 | "Hash Buckets": 1024,
897 | "Original Hash Buckets": 1024,
898 | "Hash Batches": 1,
899 | "Original Hash Batches": 1,
900 | "Peak Memory Usage": 8,
901 | "Shared Hit Blocks": 985,
902 | "Shared Read Blocks": 0,
903 | "Shared Dirtied Blocks": 0,
904 | "Shared Written Blocks": 0,
905 | "Local Hit Blocks": 0,
906 | "Local Read Blocks": 0,
907 | "Local Dirtied Blocks": 0,
908 | "Local Written Blocks": 0,
909 | "Temp Read Blocks": 0,
910 | "Temp Written Blocks": 0,
911 | "Plans": [
912 | {
913 | "Node Type": "Hash Join",
914 | "Parent Relationship": "Outer",
915 | "Parallel Aware": false,
916 | "Join Type": "Inner",
917 | "Startup Cost": 8.34,
918 | "Total Cost": 67.28,
919 | "Plan Rows": 1,
920 | "Plan Width": 33,
921 | "Actual Startup Time": 223.213,
922 | "Actual Total Time": 223.214,
923 | "Actual Rows": 0,
924 | "Actual Loops": 1,
925 | "Inner Unique": false,
926 | "Hash Cond": "(pp.user_id = u.id)",
927 | "Shared Hit Blocks": 985,
928 | "Shared Read Blocks": 0,
929 | "Shared Dirtied Blocks": 0,
930 | "Shared Written Blocks": 0,
931 | "Local Hit Blocks": 0,
932 | "Local Read Blocks": 0,
933 | "Local Dirtied Blocks": 0,
934 | "Local Written Blocks": 0,
935 | "Temp Read Blocks": 0,
936 | "Temp Written Blocks": 0,
937 | "Plans": [
938 | {
939 | "Node Type": "CTE Scan",
940 | "Parent Relationship": "Outer",
941 | "Parallel Aware": false,
942 | "CTE Name": "postgres_posts",
943 | "Alias": "pp",
944 | "Startup Cost": 0.00,
945 | "Total Cost": 49.62,
946 | "Plan Rows": 2481,
947 | "Plan Width": 4,
948 | "Actual Startup Time": 0.035,
949 | "Actual Total Time": 0.035,
950 | "Actual Rows": 1,
951 | "Actual Loops": 1,
952 | "Shared Hit Blocks": 1,
953 | "Shared Read Blocks": 0,
954 | "Shared Dirtied Blocks": 0,
955 | "Shared Written Blocks": 0,
956 | "Local Hit Blocks": 0,
957 | "Local Read Blocks": 0,
958 | "Local Dirtied Blocks": 0,
959 | "Local Written Blocks": 0,
960 | "Temp Read Blocks": 0,
961 | "Temp Written Blocks": 0
962 | },
963 | {
964 | "Node Type": "Hash",
965 | "Parent Relationship": "Inner",
966 | "Parallel Aware": false,
967 | "Startup Cost": 8.33,
968 | "Total Cost": 8.33,
969 | "Plan Rows": 1,
970 | "Plan Width": 29,
971 | "Actual Startup Time": 223.167,
972 | "Actual Total Time": 223.167,
973 | "Actual Rows": 0,
974 | "Actual Loops": 1,
975 | "Hash Buckets": 1024,
976 | "Original Hash Buckets": 1024,
977 | "Hash Batches": 1,
978 | "Original Hash Batches": 1,
979 | "Peak Memory Usage": 8,
980 | "Shared Hit Blocks": 984,
981 | "Shared Read Blocks": 0,
982 | "Shared Dirtied Blocks": 0,
983 | "Shared Written Blocks": 0,
984 | "Local Hit Blocks": 0,
985 | "Local Read Blocks": 0,
986 | "Local Dirtied Blocks": 0,
987 | "Local Written Blocks": 0,
988 | "Temp Read Blocks": 0,
989 | "Temp Written Blocks": 0,
990 | "Plans": [
991 | {
992 | "Node Type": "Nested Loop",
993 | "Parent Relationship": "Outer",
994 | "Parallel Aware": false,
995 | "Join Type": "Inner",
996 | "Startup Cost": 0.29,
997 | "Total Cost": 8.33,
998 | "Plan Rows": 1,
999 | "Plan Width": 29,
1000 | "Actual Startup Time": 223.166,
1001 | "Actual Total Time": 223.166,
1002 | "Actual Rows": 0,
1003 | "Actual Loops": 1,
1004 | "Inner Unique": true,
1005 | "Shared Hit Blocks": 984,
1006 | "Shared Read Blocks": 0,
1007 | "Shared Dirtied Blocks": 0,
1008 | "Shared Written Blocks": 0,
1009 | "Local Hit Blocks": 0,
1010 | "Local Read Blocks": 0,
1011 | "Local Dirtied Blocks": 0,
1012 | "Local Written Blocks": 0,
1013 | "Temp Read Blocks": 0,
1014 | "Temp Written Blocks": 0,
1015 | "Plans": [
1016 | {
1017 | "Node Type": "CTE Scan",
1018 | "Parent Relationship": "Outer",
1019 | "Parallel Aware": false,
1020 | "CTE Name": "performance_comments",
1021 | "Alias": "pc",
1022 | "Startup Cost": 0.00,
1023 | "Total Cost": 0.02,
1024 | "Plan Rows": 1,
1025 | "Plan Width": 4,
1026 | "Actual Startup Time": 223.166,
1027 | "Actual Total Time": 223.166,
1028 | "Actual Rows": 0,
1029 | "Actual Loops": 1,
1030 | "Shared Hit Blocks": 984,
1031 | "Shared Read Blocks": 0,
1032 | "Shared Dirtied Blocks": 0,
1033 | "Shared Written Blocks": 0,
1034 | "Local Hit Blocks": 0,
1035 | "Local Read Blocks": 0,
1036 | "Local Dirtied Blocks": 0,
1037 | "Local Written Blocks": 0,
1038 | "Temp Read Blocks": 0,
1039 | "Temp Written Blocks": 0
1040 | },
1041 | {
1042 | "Node Type": "Index Scan",
1043 | "Parent Relationship": "Inner",
1044 | "Parallel Aware": false,
1045 | "Scan Direction": "Forward",
1046 | "Index Name": "users_pkey",
1047 | "Relation Name": "users",
1048 | "Alias": "u",
1049 | "Startup Cost": 0.29,
1050 | "Total Cost": 8.31,
1051 | "Plan Rows": 1,
1052 | "Plan Width": 25,
1053 | "Actual Startup Time": 0.000,
1054 | "Actual Total Time": 0.000,
1055 | "Actual Rows": 0,
1056 | "Actual Loops": 0,
1057 | "Index Cond": "(id = pc.user_id)",
1058 | "Rows Removed by Index Recheck": 0,
1059 | "Shared Hit Blocks": 0,
1060 | "Shared Read Blocks": 0,
1061 | "Shared Dirtied Blocks": 0,
1062 | "Shared Written Blocks": 0,
1063 | "Local Hit Blocks": 0,
1064 | "Local Read Blocks": 0,
1065 | "Local Dirtied Blocks": 0,
1066 | "Local Written Blocks": 0,
1067 | "Temp Read Blocks": 0,
1068 | "Temp Written Blocks": 0
1069 | }
1070 | ]
1071 | }
1072 | ]
1073 | }
1074 | ]
1075 | }
1076 | ]
1077 | }
1078 | ]
1079 | }
1080 | ]
1081 | }
1082 | ]
1083 | }
1084 | ]
1085 | }
1086 | ]
1087 | }
1088 | ]
1089 | }
1090 | ]
1091 | }
1092 | ]
1093 | },
1094 | {
1095 | "Node Type": "Sort",
1096 | "Parent Relationship": "Inner",
1097 | "Parallel Aware": false,
1098 | "Startup Cost": 1.08,
1099 | "Total Cost": 1.14,
1100 | "Plan Rows": 25,
1101 | "Plan Width": 4,
1102 | "Actual Startup Time": 0.000,
1103 | "Actual Total Time": 0.000,
1104 | "Actual Rows": 0,
1105 | "Actual Loops": 0,
1106 | "Sort Key": ["egu.id"],
1107 | "Shared Hit Blocks": 0,
1108 | "Shared Read Blocks": 0,
1109 | "Shared Dirtied Blocks": 0,
1110 | "Shared Written Blocks": 0,
1111 | "Local Hit Blocks": 0,
1112 | "Local Read Blocks": 0,
1113 | "Local Dirtied Blocks": 0,
1114 | "Local Written Blocks": 0,
1115 | "Temp Read Blocks": 0,
1116 | "Temp Written Blocks": 0,
1117 | "Plans": [
1118 | {
1119 | "Node Type": "CTE Scan",
1120 | "Parent Relationship": "Outer",
1121 | "Parallel Aware": false,
1122 | "CTE Name": "early_gmail_users",
1123 | "Alias": "egu",
1124 | "Startup Cost": 0.00,
1125 | "Total Cost": 0.50,
1126 | "Plan Rows": 25,
1127 | "Plan Width": 4,
1128 | "Actual Startup Time": 0.000,
1129 | "Actual Total Time": 0.000,
1130 | "Actual Rows": 0,
1131 | "Actual Loops": 0,
1132 | "Shared Hit Blocks": 0,
1133 | "Shared Read Blocks": 0,
1134 | "Shared Dirtied Blocks": 0,
1135 | "Shared Written Blocks": 0,
1136 | "Local Hit Blocks": 0,
1137 | "Local Read Blocks": 0,
1138 | "Local Dirtied Blocks": 0,
1139 | "Local Written Blocks": 0,
1140 | "Temp Read Blocks": 0,
1141 | "Temp Written Blocks": 0
1142 | }
1143 | ]
1144 | }
1145 | ]
1146 | },
1147 | {
1148 | "Node Type": "Hash Join",
1149 | "Parent Relationship": "Inner",
1150 | "Parallel Aware": false,
1151 | "Join Type": "Inner",
1152 | "Startup Cost": 0.52,
1153 | "Total Cost": 2.01,
1154 | "Plan Rows": 16,
1155 | "Plan Width": 8,
1156 | "Actual Startup Time": 0.000,
1157 | "Actual Total Time": 0.000,
1158 | "Actual Rows": 0,
1159 | "Actual Loops": 0,
1160 | "Inner Unique": false,
1161 | "Hash Cond": "(ehu.id = eyu.id)",
1162 | "Shared Hit Blocks": 0,
1163 | "Shared Read Blocks": 0,
1164 | "Shared Dirtied Blocks": 0,
1165 | "Shared Written Blocks": 0,
1166 | "Local Hit Blocks": 0,
1167 | "Local Read Blocks": 0,
1168 | "Local Dirtied Blocks": 0,
1169 | "Local Written Blocks": 0,
1170 | "Temp Read Blocks": 0,
1171 | "Temp Written Blocks": 0,
1172 | "Plans": [
1173 | {
1174 | "Node Type": "CTE Scan",
1175 | "Parent Relationship": "Outer",
1176 | "Parallel Aware": false,
1177 | "CTE Name": "early_hotmail_users",
1178 | "Alias": "ehu",
1179 | "Startup Cost": 0.00,
1180 | "Total Cost": 1.12,
1181 | "Plan Rows": 56,
1182 | "Plan Width": 4,
1183 | "Actual Startup Time": 0.000,
1184 | "Actual Total Time": 0.000,
1185 | "Actual Rows": 0,
1186 | "Actual Loops": 0,
1187 | "Shared Hit Blocks": 0,
1188 | "Shared Read Blocks": 0,
1189 | "Shared Dirtied Blocks": 0,
1190 | "Shared Written Blocks": 0,
1191 | "Local Hit Blocks": 0,
1192 | "Local Read Blocks": 0,
1193 | "Local Dirtied Blocks": 0,
1194 | "Local Written Blocks": 0,
1195 | "Temp Read Blocks": 0,
1196 | "Temp Written Blocks": 0
1197 | },
1198 | {
1199 | "Node Type": "Hash",
1200 | "Parent Relationship": "Inner",
1201 | "Parallel Aware": false,
1202 | "Startup Cost": 0.32,
1203 | "Total Cost": 0.32,
1204 | "Plan Rows": 16,
1205 | "Plan Width": 4,
1206 | "Actual Startup Time": 0.000,
1207 | "Actual Total Time": 0.000,
1208 | "Actual Rows": 0,
1209 | "Actual Loops": 0,
1210 | "Shared Hit Blocks": 0,
1211 | "Shared Read Blocks": 0,
1212 | "Shared Dirtied Blocks": 0,
1213 | "Shared Written Blocks": 0,
1214 | "Local Hit Blocks": 0,
1215 | "Local Read Blocks": 0,
1216 | "Local Dirtied Blocks": 0,
1217 | "Local Written Blocks": 0,
1218 | "Temp Read Blocks": 0,
1219 | "Temp Written Blocks": 0,
1220 | "Plans": [
1221 | {
1222 | "Node Type": "CTE Scan",
1223 | "Parent Relationship": "Outer",
1224 | "Parallel Aware": false,
1225 | "CTE Name": "early_yahoo_users",
1226 | "Alias": "eyu",
1227 | "Startup Cost": 0.00,
1228 | "Total Cost": 0.32,
1229 | "Plan Rows": 16,
1230 | "Plan Width": 4,
1231 | "Actual Startup Time": 0.000,
1232 | "Actual Total Time": 0.000,
1233 | "Actual Rows": 0,
1234 | "Actual Loops": 0,
1235 | "Shared Hit Blocks": 0,
1236 | "Shared Read Blocks": 0,
1237 | "Shared Dirtied Blocks": 0,
1238 | "Shared Written Blocks": 0,
1239 | "Local Hit Blocks": 0,
1240 | "Local Read Blocks": 0,
1241 | "Local Dirtied Blocks": 0,
1242 | "Local Written Blocks": 0,
1243 | "Temp Read Blocks": 0,
1244 | "Temp Written Blocks": 0
1245 | }
1246 | ]
1247 | }
1248 | ]
1249 | }
1250 | ]
1251 | }
1252 | ]
1253 | },
1254 | "Planning Time": 20.603,
1255 | "Triggers": [
1256 | ],
1257 | "Execution Time": 756.376
1258 | }
1259 | ]
1260 |
--------------------------------------------------------------------------------
/docs/flamegraph.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
56 |
57 | pg_flame
58 |
59 |
60 |
61 |
62 |
63 |
64 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/docs/plan.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Plan": {
4 | "Node Type": "Sort",
5 | "Parallel Aware": false,
6 | "Startup Cost": 2864.87,
7 | "Total Cost": 2869.01,
8 | "Plan Rows": 1657,
9 | "Plan Width": 33,
10 | "Actual Startup Time": 48.641,
11 | "Actual Total Time": 48.649,
12 | "Actual Rows": 106,
13 | "Actual Loops": 1,
14 | "Sort Key": ["u.id", "u.email DESC"],
15 | "Sort Method": "quicksort",
16 | "Sort Space Used": 33,
17 | "Sort Space Type": "Memory",
18 | "Shared Hit Blocks": 821,
19 | "Shared Read Blocks": 0,
20 | "Shared Dirtied Blocks": 0,
21 | "Shared Written Blocks": 0,
22 | "Local Hit Blocks": 0,
23 | "Local Read Blocks": 0,
24 | "Local Dirtied Blocks": 0,
25 | "Local Written Blocks": 0,
26 | "Temp Read Blocks": 0,
27 | "Temp Written Blocks": 0,
28 | "Plans": [
29 | {
30 | "Node Type": "Aggregate",
31 | "Strategy": "Hashed",
32 | "Partial Mode": "Simple",
33 | "Parent Relationship": "Outer",
34 | "Parallel Aware": false,
35 | "Startup Cost": 2714.13,
36 | "Total Cost": 2776.27,
37 | "Plan Rows": 1657,
38 | "Plan Width": 33,
39 | "Actual Startup Time": 48.048,
40 | "Actual Total Time": 48.557,
41 | "Actual Rows": 106,
42 | "Actual Loops": 1,
43 | "Group Key": ["u.id"],
44 | "Filter": "(count(*) > 1)",
45 | "Rows Removed by Filter": 4758,
46 | "Shared Hit Blocks": 815,
47 | "Shared Read Blocks": 0,
48 | "Shared Dirtied Blocks": 0,
49 | "Shared Written Blocks": 0,
50 | "Local Hit Blocks": 0,
51 | "Local Read Blocks": 0,
52 | "Local Dirtied Blocks": 0,
53 | "Local Written Blocks": 0,
54 | "Temp Read Blocks": 0,
55 | "Temp Written Blocks": 0,
56 | "Plans": [
57 | {
58 | "Node Type": "Hash Join",
59 | "Parent Relationship": "Outer",
60 | "Parallel Aware": false,
61 | "Join Type": "Inner",
62 | "Startup Cost": 271.14,
63 | "Total Cost": 2676.85,
64 | "Plan Rows": 4971,
65 | "Plan Width": 25,
66 | "Actual Startup Time": 18.000,
67 | "Actual Total Time": 46.020,
68 | "Actual Rows": 4971,
69 | "Actual Loops": 1,
70 | "Inner Unique": false,
71 | "Hash Cond": "(u.id = posts.user_id)",
72 | "Shared Hit Blocks": 815,
73 | "Shared Read Blocks": 0,
74 | "Shared Dirtied Blocks": 0,
75 | "Shared Written Blocks": 0,
76 | "Local Hit Blocks": 0,
77 | "Local Read Blocks": 0,
78 | "Local Dirtied Blocks": 0,
79 | "Local Written Blocks": 0,
80 | "Temp Read Blocks": 0,
81 | "Temp Written Blocks": 0,
82 | "Plans": [
83 | {
84 | "Node Type": "Seq Scan",
85 | "Parent Relationship": "Outer",
86 | "Parallel Aware": false,
87 | "Relation Name": "users",
88 | "Alias": "u",
89 | "Startup Cost": 0.00,
90 | "Total Cost": 1731.00,
91 | "Plan Rows": 100000,
92 | "Plan Width": 25,
93 | "Actual Startup Time": 0.015,
94 | "Actual Total Time": 12.836,
95 | "Actual Rows": 100000,
96 | "Actual Loops": 1,
97 | "Shared Hit Blocks": 731,
98 | "Shared Read Blocks": 0,
99 | "Shared Dirtied Blocks": 0,
100 | "Shared Written Blocks": 0,
101 | "Local Hit Blocks": 0,
102 | "Local Read Blocks": 0,
103 | "Local Dirtied Blocks": 0,
104 | "Local Written Blocks": 0,
105 | "Temp Read Blocks": 0,
106 | "Temp Written Blocks": 0
107 | },
108 | {
109 | "Node Type": "Hash",
110 | "Parent Relationship": "Inner",
111 | "Parallel Aware": false,
112 | "Startup Cost": 209.00,
113 | "Total Cost": 209.00,
114 | "Plan Rows": 4971,
115 | "Plan Width": 4,
116 | "Actual Startup Time": 17.893,
117 | "Actual Total Time": 17.894,
118 | "Actual Rows": 4971,
119 | "Actual Loops": 1,
120 | "Hash Buckets": 8192,
121 | "Original Hash Buckets": 8192,
122 | "Hash Batches": 1,
123 | "Original Hash Batches": 1,
124 | "Peak Memory Usage": 239,
125 | "Shared Hit Blocks": 84,
126 | "Shared Read Blocks": 0,
127 | "Shared Dirtied Blocks": 0,
128 | "Shared Written Blocks": 0,
129 | "Local Hit Blocks": 0,
130 | "Local Read Blocks": 0,
131 | "Local Dirtied Blocks": 0,
132 | "Local Written Blocks": 0,
133 | "Temp Read Blocks": 0,
134 | "Temp Written Blocks": 0,
135 | "Plans": [
136 | {
137 | "Node Type": "Seq Scan",
138 | "Parent Relationship": "Outer",
139 | "Parallel Aware": false,
140 | "Relation Name": "posts",
141 | "Alias": "posts",
142 | "Startup Cost": 0.00,
143 | "Total Cost": 209.00,
144 | "Plan Rows": 4971,
145 | "Plan Width": 4,
146 | "Actual Startup Time": 0.032,
147 | "Actual Total Time": 16.073,
148 | "Actual Rows": 4971,
149 | "Actual Loops": 1,
150 | "Filter": "((title)::text ~ '.*sql.*'::text)",
151 | "Rows Removed by Filter": 5029,
152 | "Shared Hit Blocks": 84,
153 | "Shared Read Blocks": 0,
154 | "Shared Dirtied Blocks": 0,
155 | "Shared Written Blocks": 0,
156 | "Local Hit Blocks": 0,
157 | "Local Read Blocks": 0,
158 | "Local Dirtied Blocks": 0,
159 | "Local Written Blocks": 0,
160 | "Temp Read Blocks": 0,
161 | "Temp Written Blocks": 0
162 | }
163 | ]
164 | }
165 | ]
166 | }
167 | ]
168 | }
169 | ]
170 | },
171 | "Planning Time": 1.641,
172 | "Triggers": [
173 | ],
174 | "Execution Time": 48.942
175 | }
176 | ]
177 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/alitrack/pg_flame
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/alecthomas/kingpin v2.2.6+incompatible
7 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
8 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
9 | github.com/jackc/pgx/v4 v4.8.1
10 | github.com/stretchr/testify v1.5.1
11 | github.com/webview/webview v0.0.0-20200724072439-e0c01595b361
12 | )
13 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2 | github.com/alecthomas/kingpin v1.3.7 h1:GLMgiQ5nZb3rg5pozvF7KDbseQ12eNO1p7bB2t5K1fw=
3 | github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI=
4 | github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
5 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
6 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
7 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
8 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
9 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
10 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
11 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
12 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
13 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
14 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
16 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
18 | github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
19 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
20 | github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
21 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
22 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
23 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
24 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
25 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
26 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
27 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
28 | github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
29 | github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
30 | github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
31 | github.com/jackc/pgconn v1.6.4 h1:S7T6cx5o2OqmxdHaXLH1ZeD1SbI8jBznyYE9Ec0RCQ8=
32 | github.com/jackc/pgconn v1.6.4/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78=
33 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
34 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
35 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
36 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
37 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
38 | github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
39 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
40 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
41 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
42 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
43 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
44 | github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
45 | github.com/jackc/pgproto3/v2 v2.0.2 h1:q1Hsy66zh4vuNsajBUF2PNqfAMMfxU5mk594lPE9vjY=
46 | github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
47 | github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
48 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
49 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
50 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
51 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
52 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
53 | github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
54 | github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
55 | github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
56 | github.com/jackc/pgtype v1.4.2 h1:t+6LWm5eWPLX1H5Se702JSBcirq6uWa4jiG4wV1rAWY=
57 | github.com/jackc/pgtype v1.4.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
58 | github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
59 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
60 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
61 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
62 | github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
63 | github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
64 | github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
65 | github.com/jackc/pgx/v4 v4.8.1 h1:SUbCLP2pXvf/Sr/25KsuI4aTxiFYIvpfk4l6aTSdyCw=
66 | github.com/jackc/pgx/v4 v4.8.1/go.mod h1:4HOLxrl8wToZJReD04/yB20GDwf4KBYETvlHciCnwW0=
67 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
68 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
69 | github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
70 | github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
71 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
72 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
73 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
74 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
75 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
76 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
77 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
78 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
79 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
80 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
81 | github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
82 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
83 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
84 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
85 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
86 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
87 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
88 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
89 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
90 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
91 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
92 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
93 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
94 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
95 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
96 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
97 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
98 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
99 | github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
100 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
101 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
102 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
103 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
104 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
105 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
106 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
107 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
108 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
109 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
110 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
111 | github.com/webview/webview v0.0.0-20200724072439-e0c01595b361 h1:e0+/fQY5l9NdCwPsEg9S8AgE5lFhZ/6UX+b2KkpIBFg=
112 | github.com/webview/webview v0.0.0-20200724072439-e0c01595b361/go.mod h1:rpXAuuHgyEJb6kXcXldlkOjU6y4x+YcASKKXJNUhh0Y=
113 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
114 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
115 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
116 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
117 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
118 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
119 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
120 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
121 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
122 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
123 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
124 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
125 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
126 | golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
127 | golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
128 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
129 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
130 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
131 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
132 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
133 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
134 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
135 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
136 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
137 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
138 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
139 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
140 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
141 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
142 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
143 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
144 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
145 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
146 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
147 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
148 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
149 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
150 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
151 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
152 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
153 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
154 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
155 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
156 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
157 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
158 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
159 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
160 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
161 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
162 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
163 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
164 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
165 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
166 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
167 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
168 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
169 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
170 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 | "os/user"
9 | "strings"
10 |
11 | "github.com/alecthomas/kingpin"
12 | "github.com/alitrack/pg_flame/pkg/html"
13 | "github.com/alitrack/pg_flame/pkg/plan"
14 | "github.com/jackc/pgx/v4"
15 | )
16 |
17 | var (
18 | usr, _ = user.Current()
19 | userName = kingpin.Flag("username", "database user name").Default(usr.Username).Short('U').String()
20 | host = kingpin.Flag("host", "database server host or socket directory").Short('h').Default("localhost").String()
21 | port = kingpin.Flag("port", "database server port").Short('p').Default("5432").Int()
22 | sslmode = kingpin.Flag("sslmode", "database server sslmode").Default("disable").String()
23 | password = kingpin.Flag("password", "database server password").String()
24 | dbName = kingpin.Flag("dbname", "database name").Default("postgres").String()
25 | output = kingpin.Flag("output", "output html file").Default("pg_flame.html").Short('o').String()
26 | sql = kingpin.Flag("command", "run only single command (SQL)").Short('c').String()
27 | file = kingpin.Flag("file", "execute commands from file").Short('f').ExistingFile()
28 | showBrowser = kingpin.Flag("show_browser", "Launch browser if successful").Default("true").Short('s').Bool()
29 | version = "0.1.0"
30 | )
31 |
32 | func main() {
33 | kingpin.Version(version)
34 |
35 | kingpin.CommandLine.Help = "A flamegraph generator for Postgres EXPLAIN ANALYZE output."
36 | kingpin.Parse()
37 |
38 | dbURL := fmt.Sprintf("host=%s port=%d dbname=%s sslmode=%s user=%s password=%s",
39 | *host, *port, *dbName, *sslmode, *userName, *password)
40 |
41 | conn, err := pgx.Connect(context.Background(), dbURL)
42 | if err != nil {
43 | conn, err = pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
44 | if err != nil {
45 | fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
46 | kingpin.Usage()
47 | return
48 | }
49 | }
50 | defer conn.Close(context.Background())
51 |
52 | // sql := "EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT * FROM iris"
53 |
54 | switch {
55 | case *sql != "":
56 | break
57 | case *file != "":
58 | bs, err := ioutil.ReadFile(*file)
59 | if err != nil {
60 | fmt.Println(err)
61 | return
62 | }
63 | *sql = string(bs)
64 | default:
65 | fmt.Println("Please use sql command(-c) or sql file(-f) ")
66 | return
67 | }
68 |
69 | var json string
70 | err = conn.QueryRow(context.Background(), *sql).Scan(&json)
71 | if err != nil {
72 | fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
73 | os.Exit(1)
74 | }
75 |
76 | reader := strings.NewReader(json)
77 |
78 | p, err := plan.New(reader)
79 | if err != nil {
80 | fmt.Println(err)
81 | }
82 |
83 | o := os.Stdout
84 | if *output != "" {
85 | o, err = os.Create(*output)
86 | if err != nil {
87 | fmt.Println(err)
88 | }
89 | }
90 | err = html.Generate(o, p)
91 | if err != nil {
92 | fmt.Println(err)
93 | }
94 | if *showBrowser && *output != "" {
95 | view(*output)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/pkg/html/html.go:
--------------------------------------------------------------------------------
1 | package html
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 |
8 | "github.com/alitrack/pg_flame/pkg/plan"
9 | )
10 |
11 | type Flame struct {
12 | Name string `json:"name"`
13 | Value float64 `json:"value"`
14 | Time float64 `json:"time"`
15 | Detail string `json:"detail"`
16 | Color string `json:"color"`
17 | InitPlan bool `json:"init_plan"`
18 | Children []Flame `json:"children"`
19 | }
20 |
21 | const detailSpan = "%s"
22 |
23 | const colorPlan = "#00C05A"
24 | const colorInit = "#C0C0C0"
25 |
26 | func Generate(w io.Writer, p plan.Plan) error {
27 | f, err := buildFlame(p)
28 | if err != nil {
29 | return err
30 | }
31 |
32 | err = templateHTML.Execute(w, f)
33 | if err != nil {
34 | return err
35 | }
36 |
37 | return nil
38 | }
39 |
40 | func buildFlame(p plan.Plan) (Flame, error) {
41 | planningFlame := Flame{
42 | Name: "Query Planning",
43 | Value: p.PlanningTime,
44 | Time: p.PlanningTime,
45 | Detail: fmt.Sprintf(detailSpan, "Time to generate the query plan"),
46 | Color: colorPlan,
47 | }
48 |
49 | executionFlame, err := convertPlanNode(p.ExecutionTree, "")
50 | if err != nil {
51 | return Flame{}, err
52 | }
53 |
54 | return Flame{
55 | Name: "Total",
56 | Value: planningFlame.Value + executionFlame.Value,
57 | Time: planningFlame.Time + executionFlame.Time,
58 | Detail: fmt.Sprintf(detailSpan, "Includes planning and execution time"),
59 | Children: []Flame{planningFlame, executionFlame},
60 | }, nil
61 | }
62 |
63 | func convertPlanNode(n plan.Node, color string) (Flame, error) {
64 | initPlan := n.ParentRelationship == "InitPlan"
65 | value := n.ActualTotalTime
66 |
67 | if initPlan {
68 | color = colorInit
69 | }
70 |
71 | var childFlames []Flame
72 | for _, childNode := range n.Children {
73 |
74 | // Pass the color forward for grey InitPlan trees
75 | f, err := convertPlanNode(childNode, color)
76 | if err != nil {
77 | return Flame{}, err
78 | }
79 |
80 | // Add to the total value if the child is an InitPlan node
81 | if f.InitPlan {
82 | value += f.Value
83 | }
84 |
85 | childFlames = append(childFlames, f)
86 | }
87 |
88 | d, err := detail(n)
89 | if err != nil {
90 | return Flame{}, err
91 | }
92 |
93 | return Flame{
94 | Name: name(n),
95 | Value: value,
96 | Time: n.ActualTotalTime,
97 | Detail: d,
98 | Color: color,
99 | InitPlan: initPlan,
100 | Children: childFlames,
101 | }, nil
102 | }
103 |
104 | func name(n plan.Node) string {
105 | switch {
106 | case n.Table != "" && n.Index != "":
107 | return fmt.Sprintf("%s using %s on %s", n.Method, n.Index, n.Table)
108 | case n.Table != "":
109 | return fmt.Sprintf("%s on %s", n.Method, n.Table)
110 | default:
111 | return n.Method
112 | }
113 | }
114 |
115 | func detail(n plan.Node) (string, error) {
116 | var b bytes.Buffer
117 |
118 | err := templateTable.Execute(&b, n)
119 | if err != nil {
120 | return "", err
121 | }
122 |
123 | return b.String(), nil
124 | }
125 |
--------------------------------------------------------------------------------
/pkg/html/html_test.go:
--------------------------------------------------------------------------------
1 | package html
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 |
7 | "github.com/alitrack/pg_flame/pkg/plan"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestNew(t *testing.T) {
12 |
13 | t.Run("writes an HTML flamegraph based on a Flame", func(t *testing.T) {
14 | p := plan.Plan{
15 | ExecutionTree: plan.Node{
16 | Table: "bears",
17 | ActualTotalTime: 0.022,
18 | },
19 | }
20 |
21 | b := new(bytes.Buffer)
22 |
23 | err := Generate(b, p)
24 |
25 | assert.NoError(t, err)
26 |
27 | assert.Contains(t, b.String(), p.ExecutionTree.Table)
28 | })
29 |
30 | }
31 |
32 | func Test_buildFlame(t *testing.T) {
33 |
34 | t.Run("creates a new Flame from a Plan", func(t *testing.T) {
35 | p := plan.Plan{
36 | PlanningTime: 0.01,
37 | ExecutionTree: plan.Node{
38 | Method: "Limit",
39 | ActualTotalTime: 0.123,
40 | Children: []plan.Node{
41 | {
42 | Method: "Seq Scan",
43 | Table: "bears",
44 | ActualTotalTime: 0.022,
45 | },
46 | },
47 | },
48 | }
49 |
50 | f, err := buildFlame(p)
51 |
52 | assert.NoError(t, err)
53 |
54 | assert.Equal(t, "Total", f.Name)
55 | assert.Equal(t, 0.133, f.Value)
56 | assert.Equal(t, 0.133, f.Time)
57 | assert.Equal(t, "Includes planning and execution time", f.Detail)
58 |
59 | assert.Equal(t, "Query Planning", f.Children[0].Name)
60 | assert.Equal(t, colorPlan, f.Children[0].Color)
61 | assert.Equal(t, 0.01, f.Children[0].Value)
62 | assert.Equal(t, 0.01, f.Children[0].Time)
63 | assert.Equal(t, "Time to generate the query plan", f.Children[0].Detail)
64 |
65 | assert.Equal(t, "Limit", f.Children[1].Name)
66 | assert.Equal(t, 0.123, f.Children[1].Value)
67 | assert.Equal(t, 0.123, f.Children[1].Time)
68 |
69 | assert.Equal(t, "Seq Scan on bears", f.Children[1].Children[0].Name)
70 | assert.Equal(t, 0.022, f.Children[1].Children[0].Value)
71 | assert.Equal(t, 0.022, f.Children[1].Children[0].Time)
72 | })
73 |
74 | t.Run("handles InitPlan nodes", func(t *testing.T) {
75 | p := plan.Plan{
76 | ExecutionTree: plan.Node{
77 | Method: "Seq Scan",
78 | ActualTotalTime: 0.12,
79 | Children: []plan.Node{
80 | {
81 | Method: "Seq Scan",
82 | Table: "bears",
83 | ParentRelationship: "InitPlan",
84 | ActualTotalTime: 0.2,
85 | Children: []plan.Node{
86 | {
87 | Method: "Seq Scan",
88 | ActualTotalTime: 0.12,
89 | },
90 | },
91 | },
92 | },
93 | },
94 | }
95 |
96 | f, err := buildFlame(p)
97 |
98 | assert.NoError(t, err)
99 |
100 | assert.Equal(t, "Total", f.Name)
101 | assert.Equal(t, 0.32, f.Value)
102 | assert.Equal(t, 0.12, f.Time)
103 |
104 | assert.Equal(t, "Seq Scan", f.Children[1].Name)
105 | assert.Equal(t, 0.32, f.Children[1].Value)
106 | assert.Equal(t, 0.12, f.Children[1].Time)
107 | assert.Equal(t, "", f.Children[1].Color)
108 | assert.False(t, f.Children[1].InitPlan)
109 |
110 | assert.Equal(t, "Seq Scan on bears", f.Children[1].Children[0].Name)
111 | assert.Equal(t, 0.2, f.Children[1].Children[0].Value)
112 | assert.Equal(t, 0.2, f.Children[1].Children[0].Time)
113 | assert.Equal(t, colorInit, f.Children[1].Children[0].Color)
114 | assert.True(t, f.Children[1].Children[0].InitPlan)
115 |
116 | assert.Equal(t, colorInit, f.Children[1].Children[0].Children[0].Color)
117 | })
118 |
119 | }
120 |
121 | func Test_name(t *testing.T) {
122 |
123 | t.Run("returns the method and table if table exists", func(t *testing.T) {
124 | n := plan.Node{
125 | Method: "Seq Scan",
126 | Table: "bears",
127 | }
128 |
129 | assert.Equal(t, "Seq Scan on bears", name(n))
130 | })
131 |
132 | t.Run("returns the method, index, and table if table exists", func(t *testing.T) {
133 | n := plan.Node{
134 | Method: "Index Scan",
135 | Table: "bears",
136 | Index: "bears_pkey",
137 | }
138 |
139 | assert.Equal(t, "Index Scan using bears_pkey on bears", name(n))
140 | })
141 |
142 | t.Run("returns the method if there is no table", func(t *testing.T) {
143 | n := plan.Node{Method: "Seq Scan"}
144 |
145 | assert.Equal(t, "Seq Scan", name(n))
146 | })
147 |
148 | }
149 |
150 | func Test_detail(t *testing.T) {
151 |
152 | t.Run("returns a table of details", func(t *testing.T) {
153 | n := plan.Node{
154 | Filter: "(id = 123)",
155 | MemoryUsage: 12,
156 | }
157 |
158 | d, err := detail(n)
159 |
160 | assert.NoError(t, err)
161 |
162 | assert.Contains(t, d, n.Filter)
163 | assert.Contains(t, d, "12 kB")
164 | })
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/pkg/html/template.go:
--------------------------------------------------------------------------------
1 | package html
2 |
3 | import (
4 | "html/template"
5 | )
6 |
7 | var templateHTML *template.Template = template.Must(template.New("html").Parse(`
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
62 |
63 | pg_flame
64 |
65 |
66 |
70 |
71 |
72 |
73 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
158 |
159 |
160 | `))
161 |
162 | var templateTable *template.Template = template.Must(template.New("table").Parse(`
163 |
164 |
165 | {{if .Method}}
166 |
167 | Method |
168 | {{.Method}} |
169 |
170 | {{end}}
171 | {{if .Table}}
172 |
173 | Table |
174 | {{.Table}} |
175 |
176 | {{end}}
177 | {{if .Index}}
178 |
179 | Index |
180 | {{.Index}} |
181 |
182 | {{end}}
183 | {{if .Alias}}
184 |
185 | Alias |
186 | {{.Alias}} |
187 |
188 | {{end}}
189 | {{if .ParentRelationship}}
190 |
191 | Parent Relationship |
192 | {{.ParentRelationship}} |
193 |
194 | {{end}}
195 | {{if .PlanCost}}
196 |
197 | Plan Cost |
198 | {{.PlanCost}} |
199 |
200 | {{end}}
201 | {{if .PlanRows}}
202 |
203 | Plan Rows |
204 | {{.PlanRows}} |
205 |
206 | {{end}}
207 | {{if .PlanWidth}}
208 |
209 | Plan Width |
210 | {{.PlanWidth}} |
211 |
212 | {{end}}
213 | {{if .ActualTotalTime}}
214 |
215 | Actual Total Time |
216 | {{.ActualTotalTime}} ms |
217 |
218 | {{end}}
219 | {{if .ActualRows}}
220 |
221 | Actual Rows |
222 | {{.ActualRows}} |
223 |
224 | {{end}}
225 | {{if .ActualLoops}}
226 |
227 | Actual Loops |
228 | {{.ActualLoops}} |
229 |
230 | {{end}}
231 | {{if .Filter}}
232 |
233 | Filter |
234 | {{.Filter}} |
235 |
236 | {{end}}
237 | {{if .JoinFilter}}
238 |
239 | Join Filter |
240 | {{.JoinFilter}} |
241 |
242 | {{end}}
243 | {{if .HashCond}}
244 |
245 | Hash Cond |
246 | {{.HashCond}} |
247 |
248 | {{end}}
249 | {{if .IndexCond}}
250 |
251 | Index Cond |
252 | {{.IndexCond}} |
253 |
254 | {{end}}
255 | {{if .RecheckCond}}
256 |
257 | Recheck Cond |
258 | {{.RecheckCond}} |
259 |
260 | {{end}}
261 | {{if .BuffersHit}}
262 |
263 | Buffers Shared Hit |
264 | {{.BuffersHit}} |
265 |
266 | {{end}}
267 | {{if .BuffersRead}}
268 |
269 | Buffers Shared Read |
270 | {{.BuffersRead}} |
271 |
272 | {{end}}
273 | {{if .HashBuckets}}
274 |
275 | Hash Buckets |
276 | {{.HashBuckets}} |
277 |
278 | {{end}}
279 | {{if .HashBatches}}
280 |
281 | Hash Batches |
282 | {{.HashBatches}} |
283 |
284 | {{end}}
285 | {{if .MemoryUsage}}
286 |
287 | Memory Usage |
288 | {{.MemoryUsage}} kB |
289 |
290 | {{end}}
291 | {{if .SortKey}}
292 |
293 | Sort Key |
294 | {{.SortKey}} |
295 |
296 | {{end}}
297 | {{if .SortMethod}}
298 |
299 | Sort Method |
300 | {{.SortMethod}} |
301 |
302 | {{end}}
303 | {{if .SortSpaceUsed}}
304 |
305 | Sort Space Used |
306 | {{.SortSpaceUsed}} kB |
307 |
308 | {{end}}
309 | {{if .SortSpaceType}}
310 |
311 | Sort Space Type |
312 | {{.SortSpaceType}} |
313 |
314 | {{end}}
315 |
316 |
317 | `))
318 |
--------------------------------------------------------------------------------
/pkg/plan/plan.go:
--------------------------------------------------------------------------------
1 | package plan
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "io"
7 | )
8 |
9 | type Plan struct {
10 | PlanningTime float64 `json:"Planning Time"`
11 | ExecutionTree Node `json:"Plan"`
12 | }
13 |
14 | type Node struct {
15 | Method string `json:"Node Type"`
16 | Table string `json:"Relation Name"`
17 | Alias string `json:"Alias"`
18 | Index string `json:"Index Name"`
19 | ParentRelationship string `json:"Parent Relationship"`
20 | PlanCost float64 `json:"Total Cost"`
21 | PlanRows int `json:"Plan Rows"`
22 | PlanWidth int `json:"Plan Width"`
23 | ActualTotalTime float64 `json:"Actual Total Time"`
24 | ActualRows int `json:"Actual Rows"`
25 | ActualLoops int `json:"Actual Loops"`
26 | Filter string `json:"Filter"`
27 | JoinFilter string `json:"Join Filter"`
28 | HashCond string `json:"Hash Cond"`
29 | IndexCond string `json:"Index Cond"`
30 | RecheckCond string `json:"Recheck Cond"`
31 | BuffersHit int `json:"Shared Hit Blocks"`
32 | BuffersRead int `json:"Shared Read Blocks"`
33 | MemoryUsage int `json:"Peak Memory Usage"`
34 | HashBuckets int `json:"Hash Buckets"`
35 | HashBatches int `json:"Hash Batches"`
36 | SortKey []string `json:"Sort Key"`
37 | SortMethod string `json:"Sort Method"`
38 | SortSpaceUsed int `json:"Sort Space Used"`
39 | SortSpaceType string `json:"Sort Space Type"`
40 | Children []Node `json:"Plans"`
41 | }
42 |
43 | var ErrEmptyPlanJSON = errors.New("empty plan JSON")
44 | var ErrInvalidPlanJSON = errors.New("invalid plan JSON")
45 |
46 | func New(r io.Reader) (Plan, error) {
47 | var plans []Plan
48 |
49 | err := json.NewDecoder(r).Decode(&plans)
50 | if err != nil {
51 | var e *json.UnmarshalTypeError
52 | if errors.As(err, &e) {
53 | err = ErrInvalidPlanJSON
54 | }
55 | return Plan{}, err
56 | }
57 |
58 | if len(plans) < 1 {
59 | return Plan{}, ErrEmptyPlanJSON
60 | }
61 |
62 | return plans[0], nil
63 | }
64 |
--------------------------------------------------------------------------------
/pkg/plan/plan_test.go:
--------------------------------------------------------------------------------
1 | package plan
2 |
3 | import (
4 | "strings"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestNew(t *testing.T) {
11 |
12 | t.Run("decodes EXPLAIN ANALYZE plan JSON", func(t *testing.T) {
13 | input := strings.NewReader(planJSON)
14 |
15 | p, err := New(input)
16 |
17 | assert.NoError(t, err)
18 |
19 | assert.Equal(t, "Nested Loop", p.ExecutionTree.Method)
20 | assert.Equal(t, "", p.ExecutionTree.Table)
21 | assert.Equal(t, 0.049, p.ExecutionTree.ActualTotalTime)
22 |
23 | child := p.ExecutionTree.Children[0]
24 |
25 | assert.Equal(t, "Hash Join", child.Method)
26 | assert.Equal(t, "users", child.Table)
27 | assert.Equal(t, "users_pkey", child.Index)
28 | assert.Equal(t, "u", child.Alias)
29 | assert.Equal(t, "Outer", child.ParentRelationship)
30 |
31 | assert.Equal(t, 35.06, child.PlanCost)
32 | assert.Equal(t, 1, child.PlanRows)
33 | assert.Equal(t, 543, child.PlanWidth)
34 |
35 | assert.Equal(t, 0.049, child.ActualTotalTime)
36 | assert.Equal(t, 5, child.ActualRows)
37 | assert.Equal(t, 1, child.ActualLoops)
38 |
39 | assert.Equal(t, "((title)::text ~ '.*sql.*'::text)", child.Filter)
40 | assert.Equal(t, "(id = 123)", child.JoinFilter)
41 | assert.Equal(t, "((p.user_id = c.user_id) AND (p.id = c.post_id))", child.HashCond)
42 | assert.Equal(t, "(id = p.user_id)", child.IndexCond)
43 | assert.Equal(t, "(p.user_id = 123)", child.RecheckCond)
44 |
45 | assert.Equal(t, 5, child.BuffersHit)
46 | assert.Equal(t, 1, child.BuffersRead)
47 |
48 | assert.Equal(t, 1024, child.HashBuckets)
49 | assert.Equal(t, 1, child.HashBatches)
50 | assert.Equal(t, 8, child.MemoryUsage)
51 |
52 | assert.Equal(t, []string{"u.id", "u.email DESC"}, child.SortKey)
53 | assert.Equal(t, "quicksort", child.SortMethod)
54 | assert.Equal(t, 33, child.SortSpaceUsed)
55 | assert.Equal(t, "Memory", child.SortSpaceType)
56 | })
57 |
58 | t.Run("returns an error with empty plan JSON", func(t *testing.T) {
59 | input := strings.NewReader("[]")
60 |
61 | _, err := New(input)
62 |
63 | assert.Error(t, err)
64 | assert.Equal(t, ErrEmptyPlanJSON, err)
65 | })
66 |
67 | t.Run("returns an error with invalid plan JSON", func(t *testing.T) {
68 | input := strings.NewReader("{}")
69 |
70 | _, err := New(input)
71 |
72 | assert.Error(t, err)
73 | assert.Equal(t, ErrInvalidPlanJSON, err)
74 | })
75 |
76 | t.Run("returns an error with invalid JSON syntax", func(t *testing.T) {
77 | input := strings.NewReader("[}")
78 |
79 | _, err := New(input)
80 |
81 | assert.Error(t, err)
82 | })
83 |
84 | }
85 |
86 | const planJSON = `
87 | [
88 | {
89 | "Plan": {
90 | "Node Type": "Nested Loop",
91 | "Parallel Aware": false,
92 | "Join Type": "Inner",
93 | "Startup Cost": 265.38,
94 | "Total Cost": 288.42,
95 | "Plan Rows": 1,
96 | "Plan Width": 539,
97 | "Actual Startup Time": 0.049,
98 | "Actual Total Time": 0.049,
99 | "Actual Rows": 0,
100 | "Actual Loops": 1,
101 | "Inner Unique": true,
102 | "Shared Hit Blocks": 5,
103 | "Shared Read Blocks": 1,
104 | "Shared Dirtied Blocks": 0,
105 | "Shared Written Blocks": 0,
106 | "Local Hit Blocks": 0,
107 | "Local Read Blocks": 0,
108 | "Local Dirtied Blocks": 0,
109 | "Local Written Blocks": 0,
110 | "Temp Read Blocks": 0,
111 | "Temp Written Blocks": 0,
112 | "Plans": [
113 | {
114 | "Node Type": "Hash Join",
115 | "Relation Name": "users",
116 | "Alias": "u",
117 | "Index Name": "users_pkey",
118 | "Parent Relationship": "Outer",
119 | "Parallel Aware": false,
120 | "Join Type": "Inner",
121 | "Startup Cost": 13.50,
122 | "Total Cost": 35.06,
123 | "Plan Rows": 1,
124 | "Plan Width": 543,
125 | "Actual Startup Time": 0.049,
126 | "Actual Total Time": 0.049,
127 | "Actual Rows": 5,
128 | "Actual Loops": 1,
129 | "Inner Unique": false,
130 | "Filter": "((title)::text ~ '.*sql.*'::text)",
131 | "Hash Cond": "((p.user_id = c.user_id) AND (p.id = c.post_id))",
132 | "Index Cond": "(id = p.user_id)",
133 | "Join Filter": "(id = 123)",
134 | "Recheck Cond": "(p.user_id = 123)",
135 | "Hash Buckets": 1024,
136 | "Hash Batches": 1,
137 | "Peak Memory Usage": 8,
138 | "Shared Hit Blocks": 5,
139 | "Shared Read Blocks": 1,
140 | "Shared Dirtied Blocks": 0,
141 | "Shared Written Blocks": 0,
142 | "Sort Key": ["u.id", "u.email DESC"],
143 | "Sort Method": "quicksort",
144 | "Sort Space Used": 33,
145 | "Sort Space Type": "Memory",
146 | "Local Hit Blocks": 0,
147 | "Local Read Blocks": 0,
148 | "Local Dirtied Blocks": 0,
149 | "Local Written Blocks": 0,
150 | "Temp Read Blocks": 0,
151 | "Temp Written Blocks": 0,
152 | "Plans": [
153 | ]
154 | }
155 | ]
156 | },
157 | "Planning Time": 2.523,
158 | "Triggers": [
159 | ],
160 | "Execution Time": 0.221
161 | }
162 | ]
163 | `
164 |
--------------------------------------------------------------------------------
/static/d3-flamegraph.css:
--------------------------------------------------------------------------------
1 | .d3-flame-graph rect {
2 | stroke: #EEEEEE;
3 | fill-opacity: .8;
4 | }
5 |
6 | .d3-flame-graph rect:hover {
7 | stroke: #474747;
8 | stroke-width: 0.5;
9 | cursor: pointer;
10 | }
11 |
12 | .d3-flame-graph-label {
13 | pointer-events: none;
14 | white-space: nowrap;
15 | text-overflow: ellipsis;
16 | overflow: hidden;
17 | font-size: 12px;
18 | font-family: Verdana;
19 | margin-left: 4px;
20 | margin-right: 4px;
21 | line-height: 1.5;
22 | padding: 0 0 0;
23 | font-weight: 400;
24 | color: black;
25 | text-align: left;
26 | }
27 |
28 | .d3-flame-graph .fade {
29 | opacity: 0.6 !important;
30 | }
31 |
32 | .d3-flame-graph .title {
33 | font-size: 20px;
34 | font-family: Verdana;
35 | }
36 |
37 | .d3-flame-graph-tip {
38 | line-height: 1;
39 | font-family: Verdana;
40 | font-size: 12px;
41 | padding: 12px;
42 | background: rgba(0, 0, 0, 0.8);
43 | color: #fff;
44 | border-radius: 2px;
45 | pointer-events: none;
46 | }
47 |
48 | /* Creates a small triangle extender for the tooltip */
49 | .d3-flame-graph-tip:after {
50 | box-sizing: border-box;
51 | display: inline;
52 | font-size: 10px;
53 | width: 100%;
54 | line-height: 1;
55 | color: rgba(0, 0, 0, 0.8);
56 | position: absolute;
57 | pointer-events: none;
58 | }
59 |
60 | /* Northward tooltips */
61 | .d3-flame-graph-tip.n:after {
62 | content: "\25BC";
63 | margin: -1px 0 0 0;
64 | top: 100%;
65 | left: 0;
66 | text-align: center;
67 | }
68 |
69 | /* Eastward tooltips */
70 | .d3-flame-graph-tip.e:after {
71 | content: "\25C0";
72 | margin: -4px 0 0 0;
73 | top: 50%;
74 | left: -8px;
75 | }
76 |
77 | /* Southward tooltips */
78 | .d3-flame-graph-tip.s:after {
79 | content: "\25B2";
80 | margin: 0 0 1px 0;
81 | top: -8px;
82 | left: 0;
83 | text-align: center;
84 | }
85 |
86 | /* Westward tooltips */
87 | .d3-flame-graph-tip.w:after {
88 | content: "\25B6";
89 | margin: -4px 0 0 -1px;
90 | top: 50%;
91 | left: 100%;
92 | }
--------------------------------------------------------------------------------
/static/d3-flamegraph.min.js:
--------------------------------------------------------------------------------
1 | !function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})}(this,function(t){"use strict";var n="http://www.w3.org/1999/xhtml",e={svg:"http://www.w3.org/2000/svg",xhtml:n,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},r=function(t){var n=t+="",r=n.indexOf(":");return r>=0&&"xmlns"!==(n=t.slice(0,r))&&(t=t.slice(r+1)),e.hasOwnProperty(n)?{space:e[n],local:t}:t};var i=function(t){var e=r(t);return(e.local?function(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}:function(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===n&&e.documentElement.namespaceURI===n?e.createElement(t):e.createElementNS(r,t)}})(e)};function u(){}var o=function(t){return null==t?u:function(){return this.querySelector(t)}};function a(){return[]}var l=function(t){return null==t?a:function(){return this.querySelectorAll(t)}},c=function(t){return function(){return this.matches(t)}};if("undefined"!=typeof document){var s=document.documentElement;if(!s.matches){var f=s.webkitMatchesSelector||s.msMatchesSelector||s.mozMatchesSelector||s.oMatchesSelector;c=function(t){return function(){return f.call(this,t)}}}}var h=c,p=function(t){return new Array(t.length)};function d(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}d.prototype={constructor:d,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var g="$";function v(t,n,e,r,i,u){for(var o,a=0,l=n.length,c=u.length;an?1:t>=n?0:NaN}var w=function(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView};function _(t,n){return t.style.getPropertyValue(n)||w(t).getComputedStyle(t,null).getPropertyValue(n)}function M(t){return t.trim().split(/^|\s+/)}function x(t){return t.classList||new b(t)}function b(t){this._node=t,this._names=M(t.getAttribute("class")||"")}function T(t,n){for(var e=x(t),r=-1,i=n.length;++r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function A(){this.textContent=""}function N(){this.innerHTML=""}function S(){this.nextSibling&&this.parentNode.appendChild(this)}function k(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function D(){return null}function U(){var t=this.parentNode;t&&t.removeChild(this)}function E(){return this.parentNode.insertBefore(this.cloneNode(!1),this.nextSibling)}function H(){return this.parentNode.insertBefore(this.cloneNode(!0),this.nextSibling)}var F={};"undefined"!=typeof document&&("onmouseenter"in document.documentElement||(F={mouseenter:"mouseover",mouseleave:"mouseout"}));function Y(t,n,e){return t=L(t,n,e),function(n){var e=n.relatedTarget;e&&(e===this||8&e.compareDocumentPosition(this))||t.call(this,n)}}function L(t,n,e){return function(r){try{t.call(this,this.__data__,n,e)}finally{}}}function P(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,u=n.length;r=b&&(b=x+1);!(M=w[b])&&++b=0;)(r=i[u])&&(o&&o!==r.nextSibling&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=m);for(var e=this._groups,r=e.length,i=new Array(r),u=0;u1?this.each((null==n?function(t){return function(){this.style.removeProperty(t)}}:"function"==typeof n?function(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}:function(t,n,e){return function(){this.style.setProperty(t,n,e)}})(t,n,null==e?"":e)):_(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?function(t){return function(){delete this[t]}}:"function"==typeof n?function(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}:function(t,n){return function(){this[t]=n}})(t,n)):this.node()[t]},classed:function(t,n){var e=M(t+"");if(arguments.length<2){for(var r=x(this.node()),i=-1,u=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}})}(t+""),o=u.length;if(!(arguments.length<2)){for(a=n?O:P,null==e&&(e=!1),r=0;r1?r[0]+r.slice(2):r,+t.slice(e+1)]},B=function(t){return(t=V(Math.abs(t)))?t[1]:NaN},I=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function W(t){return new Z(t)}function Z(t){if(!(n=I.exec(t)))throw new Error("invalid format: "+t);var n;this.fill=n[1]||" ",this.align=n[2]||">",this.sign=n[3]||"-",this.symbol=n[4]||"",this.zero=!!n[5],this.width=n[6]&&+n[6],this.comma=!!n[7],this.precision=n[8]&&+n[8].slice(1),this.trim=!!n[9],this.type=n[10]||""}W.prototype=Z.prototype,Z.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(null==this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(null==this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var R,Q,J,G,K=function(t){t:for(var n,e=t.length,r=1,i=-1;r0){if(!+t[r])break t;i=0}}return i>0?t.slice(0,i)+t.slice(n+1):t},tt=function(t,n){var e=V(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")},nt={"%":function(t,n){return(100*t).toFixed(n)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,n){return t.toExponential(n)},f:function(t,n){return t.toFixed(n)},g:function(t,n){return t.toPrecision(n)},o:function(t){return Math.round(t).toString(8)},p:function(t,n){return tt(100*t,n)},r:tt,s:function(t,n){var e=V(t,n);if(!e)return t+"";var r=e[0],i=e[1],u=i-(R=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,o=r.length;return u===o?r:u>o?r+new Array(u-o+1).join("0"):u>0?r.slice(0,u)+"."+r.slice(u):"0."+new Array(1-u).join("0")+V(t,Math.max(0,n+u-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}},et=function(t){return t},rt=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];Q=function(t){var n,e,r=t.grouping&&t.thousands?(n=t.grouping,e=t.thousands,function(t,r){for(var i=t.length,u=[],o=0,a=n[0],l=0;i>0&&a>0&&(l+a+1>r&&(a=Math.max(1,r-l)),u.push(t.substring(i-=a,i+a)),!((l+=a+1)>r));)a=n[o=(o+1)%n.length];return u.reverse().join(e)}):et,i=t.currency,u=t.decimal,o=t.numerals?function(t){return function(n){return n.replace(/[0-9]/g,function(n){return t[+n]})}}(t.numerals):et,a=t.percent||"%";function l(t){var n=(t=W(t)).fill,e=t.align,l=t.sign,c=t.symbol,s=t.zero,f=t.width,h=t.comma,p=t.precision,d=t.trim,g=t.type;"n"===g?(h=!0,g="g"):nt[g]||(null==p&&(p=12),d=!0,g="g"),(s||"0"===n&&"="===e)&&(s=!0,n="0",e="=");var v="$"===c?i[0]:"#"===c&&/[boxX]/.test(g)?"0"+g.toLowerCase():"",y="$"===c?i[1]:/[%p]/.test(g)?a:"",m=nt[g],w=/[defgprs%]/.test(g);function _(t){var i,a,c,_=v,M=y;if("c"===g)M=m(t)+M,t="";else{var x=(t=+t)<0;if(t=m(Math.abs(t),p),d&&(t=K(t)),x&&0==+t&&(x=!1),_=(x?"("===l?l:"-":"-"===l||"("===l?"":l)+_,M=("s"===g?rt[8+R/3]:"")+M+(x&&"("===l?")":""),w)for(i=-1,a=t.length;++i(c=t.charCodeAt(i))||c>57){M=(46===c?u+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}h&&!s&&(t=r(t,1/0));var b=_.length+t.length+M.length,T=b>1)+_+t+M+T.slice(b);break;default:t=T+_+t+M}return o(t)}return p=null==p?6:/[gprs]/.test(g)?Math.max(1,Math.min(21,p)):Math.max(0,Math.min(20,p)),_.toString=function(){return t+""},_}return{format:l,formatPrefix:function(t,n){var e=l(((t=W(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(B(n)/3))),i=Math.pow(10,-r),u=rt[8+r/3];return function(t){return e(i*t)+u}}}}({decimal:".",thousands:",",grouping:[3],currency:["$",""]}),J=Q.format,G=Q.formatPrefix;var it=function(t,n){return tn?1:t>=n?0:NaN};var ut,ot,at=(1===(ut=it).length&&(ot=ut,ut=function(t,n){return it(ot(t),n)}),{left:function(t,n,e,r){for(null==e&&(e=0),null==r&&(r=t.length);e>>1;ut(t[i],n)<0?e=i+1:r=i}return e},right:function(t,n,e,r){for(null==e&&(e=0),null==r&&(r=t.length);e>>1;ut(t[i],n)>0?r=i:e=i+1}return e}}).right,lt=Math.sqrt(50),ct=Math.sqrt(10),st=Math.sqrt(2),ft=function(t,n,e){var r,i,u,o,a=-1;if(e=+e,(t=+t)===(n=+n)&&e>0)return[t];if((r=n0)for(t=Math.ceil(t/o),n=Math.floor(n/o),u=new Array(i=Math.ceil(n-t+1));++a=0?(u>=lt?10:u>=ct?5:u>=st?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(u>=lt?10:u>=ct?5:u>=st?2:1)}function pt(t){var n=0,e=t.children,r=e&&e.length;if(r)for(;--r>=0;)n+=e[r].value;else n=1;t.value=n}function dt(t,n){var e,r,i,u,o,a=new mt(t),l=+t.value&&(a.value=t.value),c=[a];for(null==n&&(n=gt);e=c.pop();)if(l&&(e.value=+e.data.value),(i=n(e.data))&&(o=i.length))for(e.children=new Array(o),u=o-1;u>=0;--u)c.push(r=e.children[u]=new mt(i[u])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(yt)}function gt(t){return t.children}function vt(t){t.data=t.data.data}function yt(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function mt(t){this.data=t,this.depth=this.height=0,this.parent=null}mt.prototype=dt.prototype={constructor:mt,count:function(){return this.eachAfter(pt)},each:function(t){var n,e,r,i,u=this,o=[u];do{for(n=o.reverse(),o=[];u=n.pop();)if(t(u),e=u.children)for(r=0,i=e.length;r=0;--e)i.push(n[e]);return this},sum:function(t){return this.eachAfter(function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e})},sort:function(t){return this.eachBefore(function(n){n.children&&n.children.sort(t)})},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){var t=[];return this.each(function(n){t.push(n)}),t},leaves:function(){var t=[];return this.eachBefore(function(n){n.children||t.push(n)}),t},links:function(){var t=this,n=[];return t.each(function(e){e!==t&&n.push({source:e.parent,target:e})}),n},copy:function(){return dt(this).eachBefore(vt)}};var wt=function(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)},_t=function(t,n,e,r,i){for(var u,o=t.children,a=-1,l=o.length,c=t.value&&(r-n)/t.value;++a>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):(n=Ft.exec(t))?Xt(parseInt(n[1],16)):(n=Yt.exec(t))?new Wt(n[1],n[2],n[3],1):(n=Lt.exec(t))?new Wt(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=Pt.exec(t))?Vt(n[1],n[2],n[3],n[4]):(n=Ot.exec(t))?Vt(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=jt.exec(t))?Zt(n[1],n[2]/100,n[3]/100,1):(n=qt.exec(t))?Zt(n[1],n[2]/100,n[3]/100,n[4]):$t.hasOwnProperty(t)?Xt($t[t]):"transparent"===t?new Wt(NaN,NaN,NaN,0):null}function Xt(t){return new Wt(t>>16&255,t>>8&255,255&t,1)}function Vt(t,n,e,r){return r<=0&&(t=n=e=NaN),new Wt(t,n,e,r)}function Bt(t){return t instanceof kt||(t=zt(t)),t?new Wt((t=t.rgb()).r,t.g,t.b,t.opacity):new Wt}function It(t,n,e,r){return 1===arguments.length?Bt(t):new Wt(t,n,e,null==r?1:r)}function Wt(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Zt(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Rt(t,n,e,r)}function Rt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Qt(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}Nt(kt,zt,{displayable:function(){return this.rgb().displayable()},toString:function(){return this.rgb()+""}}),Nt(Wt,It,St(kt,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new Wt(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new Wt(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return 0<=this.r&&this.r<=255&&0<=this.g&&this.g<=255&&0<=this.b&&this.b<=255&&0<=this.opacity&&this.opacity<=1},toString:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}})),Nt(Rt,function(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Rt)return new Rt(t.h,t.s,t.l,t.opacity);if(t instanceof kt||(t=zt(t)),!t)return new Rt;if(t instanceof Rt)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),u=Math.max(n,e,r),o=NaN,a=u-i,l=(u+i)/2;return a?(o=n===u?(e-r)/a+6*(e0&&l<1?0:o,new Rt(o,a,l,t.opacity)}(t):new Rt(t,n,e,null==r?1:r)},St(kt,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new Rt(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new Rt(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new Wt(Qt(t>=240?t-240:t+120,i,r),Qt(t,i,r),Qt(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var Jt=Math.PI/180,Gt=180/Math.PI,Kt=.95047,tn=1,nn=1.08883,en=4/29,rn=6/29,un=3*rn*rn,on=rn*rn*rn;function an(t){if(t instanceof ln)return new ln(t.l,t.a,t.b,t.opacity);if(t instanceof pn){var n=t.h*Jt;return new ln(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof Wt||(t=Bt(t));var e=hn(t.r),r=hn(t.g),i=hn(t.b),u=cn((.4124564*e+.3575761*r+.1804375*i)/Kt),o=cn((.2126729*e+.7151522*r+.072175*i)/tn);return new ln(116*o-16,500*(u-o),200*(o-cn((.0193339*e+.119192*r+.9503041*i)/nn)),t.opacity)}function ln(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function cn(t){return t>on?Math.pow(t,1/3):t/un+en}function sn(t){return t>rn?t*t*t:un*(t-en)}function fn(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function hn(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function pn(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}Nt(ln,function(t,n,e,r){return 1===arguments.length?an(t):new ln(t,n,e,null==r?1:r)},St(kt,{brighter:function(t){return new ln(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new ln(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return t=tn*sn(t),new Wt(fn(3.2404542*(n=Kt*sn(n))-1.5371385*t-.4985314*(e=nn*sn(e))),fn(-.969266*n+1.8760108*t+.041556*e),fn(.0556434*n-.2040259*t+1.0572252*e),this.opacity)}})),Nt(pn,function(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof pn)return new pn(t.h,t.c,t.l,t.opacity);t instanceof ln||(t=an(t));var n=Math.atan2(t.b,t.a)*Gt;return new pn(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}(t):new pn(t,n,e,null==r?1:r)},St(kt,{brighter:function(t){return new pn(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new pn(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return an(this).rgb()}}));var dn=-.14861,gn=1.78277,vn=-.29227,yn=-.90649,mn=1.97294,wn=mn*yn,_n=mn*gn,Mn=gn*vn-yn*dn;function xn(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}Nt(xn,function(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof xn)return new xn(t.h,t.s,t.l,t.opacity);t instanceof Wt||(t=Bt(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(Mn*r+wn*n-_n*e)/(Mn+wn-_n),u=r-i,o=(mn*(e-i)-vn*u)/yn,a=Math.sqrt(o*o+u*u)/(mn*i*(1-i)),l=a?Math.atan2(o,u)*Gt-120:NaN;return new xn(l<0?l+360:l,a,i,t.opacity)}(t):new xn(t,n,e,null==r?1:r)},St(kt,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new xn(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new xn(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*Jt,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new Wt(255*(n+e*(dn*r+gn*i)),255*(n+e*(vn*r+yn*i)),255*(n+e*(mn*r)),this.opacity)}}));var bn=function(t){return function(){return t}};function Tn(t){return 1==(t=+t)?Cn:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):bn(isNaN(n)?e:n)}}function Cn(t,n){var e=n-t;return e?function(t,n){return function(e){return t+e*n}}(t,e):bn(isNaN(t)?n:t)}var An=function t(n){var e=Tn(n);function r(t,n){var r=e((t=It(t)).r,(n=It(n)).r),i=e(t.g,n.g),u=e(t.b,n.b),o=Cn(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=u(n),t.opacity=o(n),t+""}}return r.gamma=t,r}(1),Nn=function(t,n){return n-=t=+t,function(e){return t+n*e}},Sn=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,kn=new RegExp(Sn.source,"g");var Dn,Un,En,Hn,Fn=function(t,n){var e,r,i,u=Sn.lastIndex=kn.lastIndex=0,o=-1,a=[],l=[];for(t+="",n+="";(e=Sn.exec(t))&&(r=kn.exec(n));)(i=r.index)>u&&(i=n.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:Nn(e,r)})),u=kn.lastIndex;return u180?n+=360:n-t>180&&(t+=360),u.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Nn(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(u.rotate,o.rotate,a,l),function(t,n,e,u){t!==n?u.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Nn(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(u.skewX,o.skewX,a,l),function(t,n,e,r,u,o){if(t!==e||n!==r){var a=u.push(i(u)+"scale(",null,",",null,")");o.push({i:a-4,x:Nn(t,e)},{i:a-2,x:Nn(n,r)})}else 1===e&&1===r||u.push(i(u)+"scale("+e+","+r+")")}(u.scaleX,u.scaleY,o.scaleX,o.scaleY,a,l),u=o=null,function(t){for(var n,e=-1,r=l.length;++e=lt?i*=10:u>=ct?i*=5:u>=st&&(i*=2),n2?Zn:Wn,r=i=null,s}function s(n){return(r||(r=e(u,o,l?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=n?0:t>=e?1:r(t)}}}(t):t,a)))(+n)}return s.invert=function(t){return(i||(i=e(o,u,In,l?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=0?n:t>=1?e:r(t)}}}(n):n)))(+t)},s.domain=function(t){return arguments.length?(u=Ct.call(t,Vn),c()):u.slice()},s.range=function(t){return arguments.length?(o=At.call(t),c()):o.slice()},s.rangeRound=function(t){return o=At.call(t),a=Ln,c()},s.clamp=function(t){return arguments.length?(l=!!t,c()):l},s.interpolate=function(t){return arguments.length?(a=t,c()):a},c()}(In,Nn);return t.copy=function(){return n=t,Qn().domain(n.domain()).range(n.range()).interpolate(n.interpolate()).clamp(n.clamp());var n},function(t){var n=t.domain;return t.ticks=function(t){var e=n();return ft(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){return Rn(n(),t,e)},t.nice=function(e){null==e&&(e=10);var r,i=n(),u=0,o=i.length-1,a=i[u],l=i[o];return l0?r=ht(a=Math.floor(a/r)*r,l=Math.ceil(l/r)*r,e):r<0&&(r=ht(a=Math.ceil(a*r)/r,l=Math.floor(l*r)/r,e)),r>0?(i[u]=Math.floor(a/r)*r,i[o]=Math.ceil(l/r)*r,n(i)):r<0&&(i[u]=Math.ceil(a*r)/r,i[o]=Math.floor(l*r)/r,n(i)),t},t}(t)}var Jn=new Date,Gn=new Date;function Kn(t,n,e,r){function i(n){return t(n=new Date(+n)),n}return i.floor=i,i.ceil=function(e){return t(e=new Date(e-1)),n(e,1),t(e),e},i.round=function(t){var n=i(t),e=i.ceil(t);return t-n0))return a;do{a.push(o=new Date(+e)),n(e,u),t(e)}while(o=n)for(;t(n),!e(n);)n.setTime(n-1)},function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})},e&&(i.count=function(n,r){return Jn.setTime(+n),Gn.setTime(+r),t(Jn),t(Gn),Math.floor(e(Jn,Gn))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}var te=Kn(function(){},function(t,n){t.setTime(+t+n)},function(t,n){return n-t});te.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Kn(function(n){n.setTime(Math.floor(n/t)*t)},function(n,e){n.setTime(+n+e*t)},function(n,e){return(e-n)/t}):te:null};var ne=6e4,ee=6048e5,re=(Kn(function(t){t.setTime(1e3*Math.floor(t/1e3))},function(t,n){t.setTime(+t+1e3*n)},function(t,n){return(n-t)/1e3},function(t){return t.getUTCSeconds()}),Kn(function(t){t.setTime(Math.floor(t/ne)*ne)},function(t,n){t.setTime(+t+n*ne)},function(t,n){return(n-t)/ne},function(t){return t.getMinutes()}),Kn(function(t){var n=t.getTimezoneOffset()*ne%36e5;n<0&&(n+=36e5),t.setTime(36e5*Math.floor((+t-n)/36e5)+n)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getHours()}),Kn(function(t){t.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*ne)/864e5},function(t){return t.getDate()-1}));function ie(t){return Kn(function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+7*n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*ne)/ee})}var ue=ie(0),oe=ie(1),ae=(ie(2),ie(3),ie(4)),le=(ie(5),ie(6),Kn(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,n){t.setMonth(t.getMonth()+n)},function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())},function(t){return t.getMonth()}),Kn(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,n){t.setFullYear(t.getFullYear()+n)},function(t,n){return n.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()}));le.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Kn(function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},function(n,e){n.setFullYear(n.getFullYear()+e*t)}):null};Kn(function(t){t.setUTCSeconds(0,0)},function(t,n){t.setTime(+t+n*ne)},function(t,n){return(n-t)/ne},function(t){return t.getUTCMinutes()}),Kn(function(t){t.setUTCMinutes(0,0,0)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getUTCHours()});var ce=Kn(function(t){t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+n)},function(t,n){return(n-t)/864e5},function(t){return t.getUTCDate()-1});function se(t){return Kn(function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+7*n)},function(t,n){return(n-t)/ee})}var fe=se(0),he=se(1),pe=(se(2),se(3),se(4)),de=(se(5),se(6),Kn(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCMonth(t.getUTCMonth()+n)},function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())},function(t){return t.getUTCMonth()}),Kn(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)},function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()}));function ge(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function ve(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function ye(t){return{y:t,m:0,d:1,H:0,M:0,S:0,L:0}}de.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Kn(function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null};var me,we,_e,Me={"-":"",_:" ",0:"0"},xe=/^\s*\d+/,be=/^%/,Te=/[\\^$*+?|[\]().{}]/g;function Ce(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",u=i.length;return r+(u68?1900:2e3),e+r[0].length):-1}function Le(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function Pe(t,n,e){var r=xe.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function Oe(t,n,e){var r=xe.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function je(t,n,e){var r=xe.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function qe(t,n,e){var r=xe.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function $e(t,n,e){var r=xe.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function ze(t,n,e){var r=xe.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function Xe(t,n,e){var r=xe.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function Ve(t,n,e){var r=xe.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Be(t,n,e){var r=be.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Ie(t,n,e){var r=xe.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function We(t,n,e){var r=xe.exec(n.slice(e));return r?(t.Q=1e3*+r[0],e+r[0].length):-1}function Ze(t,n){return Ce(t.getDate(),n,2)}function Re(t,n){return Ce(t.getHours(),n,2)}function Qe(t,n){return Ce(t.getHours()%12||12,n,2)}function Je(t,n){return Ce(1+re.count(le(t),t),n,3)}function Ge(t,n){return Ce(t.getMilliseconds(),n,3)}function Ke(t,n){return Ge(t,n)+"000"}function tr(t,n){return Ce(t.getMonth()+1,n,2)}function nr(t,n){return Ce(t.getMinutes(),n,2)}function er(t,n){return Ce(t.getSeconds(),n,2)}function rr(t){var n=t.getDay();return 0===n?7:n}function ir(t,n){return Ce(ue.count(le(t),t),n,2)}function ur(t,n){var e=t.getDay();return t=e>=4||0===e?ae(t):ae.ceil(t),Ce(ae.count(le(t),t)+(4===le(t).getDay()),n,2)}function or(t){return t.getDay()}function ar(t,n){return Ce(oe.count(le(t),t),n,2)}function lr(t,n){return Ce(t.getFullYear()%100,n,2)}function cr(t,n){return Ce(t.getFullYear()%1e4,n,4)}function sr(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+Ce(n/60|0,"0",2)+Ce(n%60,"0",2)}function fr(t,n){return Ce(t.getUTCDate(),n,2)}function hr(t,n){return Ce(t.getUTCHours(),n,2)}function pr(t,n){return Ce(t.getUTCHours()%12||12,n,2)}function dr(t,n){return Ce(1+ce.count(de(t),t),n,3)}function gr(t,n){return Ce(t.getUTCMilliseconds(),n,3)}function vr(t,n){return gr(t,n)+"000"}function yr(t,n){return Ce(t.getUTCMonth()+1,n,2)}function mr(t,n){return Ce(t.getUTCMinutes(),n,2)}function wr(t,n){return Ce(t.getUTCSeconds(),n,2)}function _r(t){var n=t.getUTCDay();return 0===n?7:n}function Mr(t,n){return Ce(fe.count(de(t),t),n,2)}function xr(t,n){var e=t.getUTCDay();return t=e>=4||0===e?pe(t):pe.ceil(t),Ce(pe.count(de(t),t)+(4===de(t).getUTCDay()),n,2)}function br(t){return t.getUTCDay()}function Tr(t,n){return Ce(he.count(de(t),t),n,2)}function Cr(t,n){return Ce(t.getUTCFullYear()%100,n,2)}function Ar(t,n){return Ce(t.getUTCFullYear()%1e4,n,4)}function Nr(){return"+0000"}function Sr(){return"%"}function kr(t){return+t}function Dr(t){return Math.floor(+t/1e3)}!function(t){me=function(t){var n=t.dateTime,e=t.date,r=t.time,i=t.periods,u=t.days,o=t.shortDays,a=t.months,l=t.shortMonths,c=Ne(i),s=Se(i),f=Ne(u),h=Se(u),p=Ne(o),d=Se(o),g=Ne(a),v=Se(a),y=Ne(l),m=Se(l),w={a:function(t){return o[t.getDay()]},A:function(t){return u[t.getDay()]},b:function(t){return l[t.getMonth()]},B:function(t){return a[t.getMonth()]},c:null,d:Ze,e:Ze,f:Ke,H:Re,I:Qe,j:Je,L:Ge,m:tr,M:nr,p:function(t){return i[+(t.getHours()>=12)]},Q:kr,s:Dr,S:er,u:rr,U:ir,V:ur,w:or,W:ar,x:null,X:null,y:lr,Y:cr,Z:sr,"%":Sr},_={a:function(t){return o[t.getUTCDay()]},A:function(t){return u[t.getUTCDay()]},b:function(t){return l[t.getUTCMonth()]},B:function(t){return a[t.getUTCMonth()]},c:null,d:fr,e:fr,f:vr,H:hr,I:pr,j:dr,L:gr,m:yr,M:mr,p:function(t){return i[+(t.getUTCHours()>=12)]},Q:kr,s:Dr,S:wr,u:_r,U:Mr,V:xr,w:br,W:Tr,x:null,X:null,y:Cr,Y:Ar,Z:Nr,"%":Sr},M={a:function(t,n,e){var r=p.exec(n.slice(e));return r?(t.w=d[r[0].toLowerCase()],e+r[0].length):-1},A:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.w=h[r[0].toLowerCase()],e+r[0].length):-1},b:function(t,n,e){var r=y.exec(n.slice(e));return r?(t.m=m[r[0].toLowerCase()],e+r[0].length):-1},B:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.m=v[r[0].toLowerCase()],e+r[0].length):-1},c:function(t,e,r){return T(t,n,e,r)},d:Oe,e:Oe,f:Ve,H:qe,I:qe,j:je,L:Xe,m:Pe,M:$e,p:function(t,n,e){var r=c.exec(n.slice(e));return r?(t.p=s[r[0].toLowerCase()],e+r[0].length):-1},Q:Ie,s:We,S:ze,u:De,U:Ue,V:Ee,w:ke,W:He,x:function(t,n,r){return T(t,e,n,r)},X:function(t,n,e){return T(t,r,n,e)},y:Ye,Y:Fe,Z:Le,"%":Be};function x(t,n){return function(e){var r,i,u,o=[],a=-1,l=0,c=t.length;for(e instanceof Date||(e=new Date(+e));++a53)return null;"w"in u||(u.w=1),"Z"in u?(i=(r=ve(ye(u.y))).getUTCDay(),r=i>4||0===i?he.ceil(r):he(r),r=ce.offset(r,7*(u.V-1)),u.y=r.getUTCFullYear(),u.m=r.getUTCMonth(),u.d=r.getUTCDate()+(u.w+6)%7):(i=(r=n(ye(u.y))).getDay(),r=i>4||0===i?oe.ceil(r):oe(r),r=re.offset(r,7*(u.V-1)),u.y=r.getFullYear(),u.m=r.getMonth(),u.d=r.getDate()+(u.w+6)%7)}else("W"in u||"U"in u)&&("w"in u||(u.w="u"in u?u.u%7:"W"in u?1:0),i="Z"in u?ve(ye(u.y)).getUTCDay():n(ye(u.y)).getDay(),u.m=0,u.d="W"in u?(u.w+6)%7+7*u.W-(i+5)%7:u.w+7*u.U-(i+6)%7);return"Z"in u?(u.H+=u.Z/100|0,u.M+=u.Z%100,ve(u)):n(u)}}function T(t,n,e,r){for(var i,u,o=0,a=n.length,l=e.length;o=l)return-1;if(37===(i=n.charCodeAt(o++))){if(i=n.charAt(o++),!(u=M[i in Me?n.charAt(o++):i])||(r=u(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return w.x=x(e,w),w.X=x(r,w),w.c=x(n,w),_.x=x(e,_),_.X=x(r,_),_.c=x(n,_),{format:function(t){var n=x(t+="",w);return n.toString=function(){return t},n},parse:function(t){var n=b(t+="",ge);return n.toString=function(){return t},n},utcFormat:function(t){var n=x(t+="",_);return n.toString=function(){return t},n},utcParse:function(t){var n=b(t,ve);return n.toString=function(){return t},n}}}(t),we=me.utcFormat,_e=me.utcParse}({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});Date.prototype.toISOString||we("%Y-%m-%dT%H:%M:%S.%LZ");+new Date("2000-01-01T00:00:00.000Z")||_e("%Y-%m-%dT%H:%M:%S.%LZ");function Ur(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}var Er=function(){var t=function(){return"n"},n=function(){return[0,0]},e=function(){return" "},r=document.body,i=f(),u=null,o=null,a=null;function l(t){var n;n=t.node(),(u=n?"svg"===n.tagName.toLowerCase()?n:n.ownerSVGElement:null)&&(o=u.createSVGPoint(),r.appendChild(i))}l.show=function(){var i=Array.prototype.slice.call(arguments);i[i.length-1]instanceof SVGElement&&(a=i.pop());var u,o=e.apply(this,i),f=n.apply(this,i),p=t.apply(this,i),d=h(),g=s.length,v=document.documentElement.scrollTop||r.scrollTop,y=document.documentElement.scrollLeft||r.scrollLeft;for(d.html(o).style("opacity",1).style("pointer-events","all");g--;)d.classed(s[g],!1);return u=c.get(p).apply(this),d.classed(p,!0).style("top",u.top+f[0]+v+"px").style("left",u.left+f[1]+y+"px"),l},l.hide=function(){return h().style("opacity",0).style("pointer-events","none"),l},l.attr=function(t,n){if(arguments.length<2&&"string"==typeof t)return h().attr(t);var e=Array.prototype.slice.call(arguments);return z.prototype.attr.apply(h(),e),l},l.style=function(t,n){if(arguments.length<2&&"string"==typeof t)return h().style(t);var e=Array.prototype.slice.call(arguments);return z.prototype.style.apply(h(),e),l},l.direction=function(n){return arguments.length?(t=null==n?n:d(n),l):t},l.offset=function(t){return arguments.length?(n=null==t?t:d(t),l):n},l.html=function(t){return arguments.length?(e=null==t?t:d(t),l):e},l.rootElement=function(t){return arguments.length?(r=null==t?t:d(t),l):r},l.destroy=function(){return i&&(h().remove(),i=null),l};var c=bt({n:function(){var t=p(this);return{top:t.n.y-i.offsetHeight,left:t.n.x-i.offsetWidth/2}},s:function(){var t=p(this);return{top:t.s.y,left:t.s.x-i.offsetWidth/2}},e:function(){var t=p(this);return{top:t.e.y-i.offsetHeight/2,left:t.e.x}},w:function(){var t=p(this);return{top:t.w.y-i.offsetHeight/2,left:t.w.x-i.offsetWidth}},nw:function(){var t=p(this);return{top:t.nw.y-i.offsetHeight,left:t.nw.x-i.offsetWidth}},ne:function(){var t=p(this);return{top:t.ne.y-i.offsetHeight,left:t.ne.x}},sw:function(){var t=p(this);return{top:t.sw.y,left:t.sw.x-i.offsetWidth}},se:function(){var t=p(this);return{top:t.se.y,left:t.se.x}}}),s=c.keys();function f(){var t=X(document.createElement("div"));return t.style("position","absolute").style("top",0).style("opacity",0).style("pointer-events","none").style("box-sizing","border-box"),t.node()}function h(){return null==i&&(i=f(),r.appendChild(i)),X(i)}function p(t){for(var n=a||t;null==n.getScreenCTM&&null!=n.parentNode;)n=n.parentNode;var e={},r=n.getScreenCTM(),i=n.getBBox(),u=i.width,l=i.height,c=i.x,s=i.y;return o.x=c,o.y=s,e.nw=o.matrixTransform(r),o.x+=u,e.ne=o.matrixTransform(r),o.y+=l,e.se=o.matrixTransform(r),o.x-=u,e.sw=o.matrixTransform(r),o.y-=l/2,e.w=o.matrixTransform(r),o.x+=u,e.e=o.matrixTransform(r),o.x-=u/2,o.y-=l/2,e.n=o.matrixTransform(r),o.y+=l,e.s=o.matrixTransform(r),e}function d(t){return"function"==typeof t?t:function(){return t}}return l},Hr={value:function(){}};function Fr(){for(var t,n=0,e=arguments.length,r={};n=0&&(n=t.slice(e+1),t=t.slice(0,e)),t&&!r.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}})),o=-1,a=u.length;if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++o0)for(var e,r,i=new Array(e),u=0;u=0&&n._call.call(null,t),n=n._next;--qr}()}finally{qr=0,function(){var t,n,e=Or,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Or=n);jr=t,ni(r)}(),Br=0}}function ti(){var t=Wr.now(),n=t-Vr;n>Xr&&(Ir-=n,Vr=t)}function ni(t){qr||($r&&($r=clearTimeout($r)),t-Br>24?(t<1/0&&($r=setTimeout(Kr,t-Wr.now()-Ir)),zr&&(zr=clearInterval(zr))):(zr||(Vr=Wr.now(),zr=setInterval(ti,Xr)),qr=1,Zr(Kr)))}Jr.prototype=Gr.prototype={constructor:Jr,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?Rr():+e)+(null==n?0:+n),this._next||jr===this||(jr?jr._next=this:Or=this,jr=this),this._call=t,this._time=e,ni()},stop:function(){this._call&&(this._call=null,this._time=1/0,ni())}};var ei=function(t,n,e){var r=new Jr;return n=null==n?0:+n,r.restart(function(e){r.stop(),t(e+n)},n,e),r},ri=Fr("start","end","interrupt"),ii=[],ui=0,oi=1,ai=2,li=3,ci=4,si=5,fi=6,hi=function(t,n,e,r,i,u){var o=t.__transition;if(o){if(e in o)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function u(l){var c,s,f,h;if(e.state!==oi)return a();for(c in i)if((h=i[c]).name===e.name){if(h.state===li)return ei(u);h.state===ci?(h.state=fi,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[c]):+cui)throw new Error("too late; already scheduled");return e}function di(t,n){var e=gi(t,n);if(e.state>ai)throw new Error("too late; already started");return e}function gi(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function vi(t,n,e){var r=t._id;return t.each(function(){var t=di(this,r);(t.value||(t.value={}))[n]=e.apply(this,arguments)}),function(t){return gi(t,r).value[n]}}var yi=function(t,n){var e;return("number"==typeof n?Nn:n instanceof zt?An:(e=zt(n))?(n=e,An):Fn)(t,n)};var mi=z.prototype.constructor;var wi=0;function _i(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function Mi(){return++wi}var xi=z.prototype;_i.prototype=function(t){return z().transition(t)}.prototype={constructor:_i,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=o(t));for(var r=this._groups,i=r.length,u=new Array(i),a=0;a=0&&(t=t.slice(0,n)),!t||"start"===t})}(n)?pi:di;return function(){var o=u(this,t),a=o.on;a!==r&&(i=(r=a).copy()).on(n,e),o.on=i}}(e,t,n))},attr:function(t,n){var e=r(t),i="transform"===e?zn:yi;return this.attrTween(t,"function"==typeof n?(e.local?function(t,n,e){var r,i,u;return function(){var o,a=e(this);if(null!=a)return(o=this.getAttributeNS(t.space,t.local))===a?null:o===r&&a===i?u:u=n(r=o,i=a);this.removeAttributeNS(t.space,t.local)}}:function(t,n,e){var r,i,u;return function(){var o,a=e(this);if(null!=a)return(o=this.getAttribute(t))===a?null:o===r&&a===i?u:u=n(r=o,i=a);this.removeAttribute(t)}})(e,i,vi(this,"attr."+t,n)):null==n?(e.local?function(t){return function(){this.removeAttributeNS(t.space,t.local)}}:function(t){return function(){this.removeAttribute(t)}})(e):(e.local?function(t,n,e){var r,i;return function(){var u=this.getAttributeNS(t.space,t.local);return u===e?null:u===r?i:i=n(r=u,e)}}:function(t,n,e){var r,i;return function(){var u=this.getAttribute(t);return u===e?null:u===r?i:i=n(r=u,e)}})(e,i,n+""))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var i=r(t);return this.tween(e,(i.local?function(t,n){function e(){var e=this,r=n.apply(e,arguments);return r&&function(n){e.setAttributeNS(t.space,t.local,r(n))}}return e._value=n,e}:function(t,n){function e(){var e=this,r=n.apply(e,arguments);return r&&function(n){e.setAttribute(t,r(n))}}return e._value=n,e})(i,n))},style:function(t,n,e){var r="transform"==(t+="")?$n:yi;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var u=_(this,t),o=(this.style.removeProperty(t),_(this,t));return u===o?null:u===e&&o===r?i:i=n(e=u,r=o)}}(t,r)).on("end.style."+t,function(t){return function(){this.style.removeProperty(t)}}(t)):this.styleTween(t,"function"==typeof n?function(t,n,e){var r,i,u;return function(){var o=_(this,t),a=e(this);return null==a&&(this.style.removeProperty(t),a=_(this,t)),o===a?null:o===r&&a===i?u:u=n(r=o,i=a)}}(t,r,vi(this,"style."+t,n)):function(t,n,e){var r,i;return function(){var u=_(this,t);return u===e?null:u===r?i:i=n(r=u,e)}}(t,r,n+""),e)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){function r(){var r=this,i=n.apply(r,arguments);return i&&function(n){r.style.setProperty(t,i(n),e)}}return r._value=n,r}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(vi(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},remove:function(){return this.on("end.remove",(t=this._id,function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}));var t},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=gi(this.node(),e).tween,u=0,o=i.length;uai&&e.state{const e=new RegExp(n),r=w(t);return void 0!==r&&r&&r.match(e)};const N=A;var S,k=function(t){h&&(t?h.innerHTML=t:v?H():h.innerHTML="")},D=k,U=function(t){return w(t)+" ("+J(".3f")(100*(t.x1-t.x0),3)+"%, "+_(t)+" samples)"},E=Er().direction("s").offset([8,0]).attr("class","d3-flame-graph-tip").html(function(t){return U(t)});function H(){h.innerHTML=v+" of "+y+" samples ( "+J(".3f")(v/y*100,3)+"%)"}var F=function(t){return t.highlight?"#E600E6":function(t,n,e){var r,i,u;if(d)r=220,i=220,u=220,e||(e=0),e>0?(u=Math.round(210*(m-e)/m),i=u):e<0&&(r=Math.round(210*(m+e)/m),i=r);else{var o=g?"cold":"warm";g||void 0===n||""===n||(o="red",void 0!==t&&t&&t.match(/::/)&&(o="yellow"),"kernel"===n?o="orange":"jit"===n?o="green":"inlined"===n&&(o="aqua"));var a=0;if(t){var l=t.split("`");l.length>1&&(t=l[l.length-1]),t=t.split("(")[0],a=function(t){var n=0,e=0,r=1;if(t){for(var i=0;i6);i++)n+=r*(t.charCodeAt(i)%10),e+=9*r,r*=.7;e>0&&(n/=e)}return n}(t)}"red"===o?(r=200+Math.round(55*a),i=50+Math.round(80*a),u=i):"orange"===o?(r=190+Math.round(65*a),i=90+Math.round(65*a),u=0):"yellow"===o?(r=175+Math.round(55*a),i=r,u=50+Math.round(20*a)):"green"===o?(r=50+Math.round(60*a),i=200+Math.round(55*a),u=r):"aqua"===o?(r=50+Math.round(60*a),i=165+Math.round(55*a),u=i):"cold"===o?(r=0+Math.round(55*(1-a)),i=0+Math.round(230*(1-a)),u=200+Math.round(55*a)):(r=200+Math.round(55*a),i=0+Math.round(230*(1-a)),u=0+Math.round(55*(1-a)))}return"rgb("+r+","+i+","+u+")"}(w(t),x(t),b(t))},Y=F;function L(t){t.data.fade=!1,t.data.hide=!1,t.children&&t.children.forEach(L)}function P(t){E.hide(t),function(t){let n,e,r,i=t,u=i.parent;for(;u;){for(e=(n=u.children).length;e--;)(r=n[e])!==i&&(r.data.hide=!0);u=(i=u).parent}}(t),L(t),function t(n){n.parent&&(n.parent.data.fade=!0,t(n.parent))}(t),q({resetHeight:!0}),"function"==typeof s&&s(t)}function O(t,n){return"function"==typeof l?l(t,n):l?it(w(t),w(n)):void 0}var j=Mt();function q(u){u=u||{},r.each(function(r){var s=Qn().range([0,t]),h=Qn().range([0,e]);!function(t){let n,e,r,i,u,o,a,l;const c=[],s=[],f=[],h=!p;let d=t.data;for(d.hide?(t.value=0,(e=t.children)&&f.push(e)):(t.value=d.fade?0:_(d),c.push(t));n=c.pop();)if((e=n.children)&&(u=e.length)){for(i=0;u--;)a=e[u],(d=a.data).hide?(a.value=0,(r=a.children)&&f.push(r)):(d.fade?a.value=0:(l=_(d),a.value=l,i+=l),c.push(a));h&&n.value&&(n.value-=i),s.push(e)}for(u=s.length;u--;){for(e=s[u],i=0,o=e.length;o--;)i+=e[o].value;e[0].parent.value+=i}for(;f.length;)for(e=f.pop(),o=e.length;o--;)(a=e[o]).value=0,(r=a.children)&&f.push(r)}(r),y=r.value,l&&r.sort(O),j(r);var d=t/(r.x1-r.x0);function g(t){return(t.x1-t.x0)*d}var v=function(n){var e=n.descendants();if(f>0){var r=t/(n.x1-n.x0);e=e.filter(function(t){return(t.x1-t.x0)*r>f})}return e}(r),m=X(this).select("svg").selectAll("g").data(v,function(t){return t.id});if(!n||u.resetHeight){var M=Math.max.apply(null,v.map(function(t){return t.depth}));n=(M+2)*e,X(this).select("svg").attr("height",n)}m.transition().duration(o).ease(a).attr("transform",function(t){return"translate("+s(t.x0)+","+(c?h(t.depth):n-h(t.depth)-e)+")"}),m.select("rect").transition().duration(o).ease(a).attr("width",g);var x=m.enter().append("svg:g").attr("transform",function(t){return"translate("+s(t.x0)+","+(c?h(t.depth):n-h(t.depth)-e)+")"});x.append("svg:rect").transition().delay(o/2).attr("width",g),i||x.append("svg:title"),x.append("foreignObject").append("xhtml:div"),(m=X(this).select("svg").selectAll("g").data(v,function(t){return t.id})).attr("width",g).attr("height",function(t){return e}).attr("name",function(t){return w(t)}).attr("class",function(t){return t.data.fade?"frame fade":"frame"}),m.select("rect").attr("height",function(t){return e}).attr("fill",function(t){return F(t)}),i||m.select("title").text(U),m.select("foreignObject").attr("width",g).attr("height",function(t){return e}).select("div").attr("class","d3-flame-graph-label").style("display",function(t){return g(t)<35?"none":"block"}).transition().delay(o).text(w),m.on("click",P),m.exit().remove(),m.on("mouseover",function(t){i&&E.show(t,this),k(U(t))}).on("mouseout",function(t){i&&E.hide(t),k(null)})})}function $(t){m=0;let n=0,e=0;const r=d;!function(t,n){n(t);let e=t.children;if(e){const t=[e];let r,i,u;for(;t.length;)for(r=(e=t.pop()).length;r--;)n(i=e[r]),(u=i.children)&&t.push(u)}}(t,function(t){t.id=n++,r&&(e=Math.abs(b(t)),m{const e=Y(n);return t(n,e)}),z):(F=Y,z)},z.color=z.setColorMapper,z.minFrameSize=function(t){return arguments.length?(f=t,z):f},z.setDetailsElement=function(t){return arguments.length?(h=t,z):h},z.details=z.setDetailsElement,z.selfValue=function(t){return arguments.length?(p=t,z):p},z.getName=function(t){return arguments.length?(w=t,z):w},z.getValue=function(t){return arguments.length?(_=t,z):_},z.getChildren=function(t){return arguments.length?(M=t,z):M},z.getLibtype=function(t){return arguments.length?(x=t,z):x},z.getDelta=function(t){return arguments.length?(b=t,z):b},z.setSearchHandler=function(t){return arguments.length?(T=t,z):(T=C,z)},z.setDetailsHandler=function(t){return arguments.length?(k=t,z):(k=D,z)},z.setSearchMatch=function(t){return arguments.length?(A=t,z):(A=N,z)},z},Object.defineProperty(t,"__esModule",{value:!0})});
--------------------------------------------------------------------------------
/static/d3-tip.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("d3-collection"),require("d3-selection")):"function"==typeof define&&define.amd?define(["d3-collection","d3-selection"],e):(t.d3=t.d3||{},t.d3.tip=e(t.d3,t.d3))}(this,function(l,i){"use strict";return function(){var u=function(){return"n"},c=function(){return[0,0]},a=function(){return" "},p=document.body,n=t(),r=null,y=null,d=null;function h(t){var e;e=t.node(),(r=e?"svg"===e.tagName.toLowerCase()?e:e.ownerSVGElement:null)&&(y=r.createSVGPoint(),p.appendChild(n))}h.show=function(){var t=Array.prototype.slice.call(arguments);t[t.length-1]instanceof SVGElement&&(d=t.pop());var e,n=a.apply(this,t),r=c.apply(this,t),o=u.apply(this,t),l=v(),i=x.length,s=document.documentElement.scrollTop||p.scrollTop,f=document.documentElement.scrollLeft||p.scrollLeft;for(l.html(n).style("opacity",1).style("pointer-events","all");i--;)l.classed(x[i],!1);return e=m.get(o).apply(this),l.classed(o,!0).style("top",e.top+r[0]+s+"px").style("left",e.left+r[1]+f+"px"),h},h.hide=function(){return v().style("opacity",0).style("pointer-events","none"),h},h.attr=function(t,e){if(arguments.length<2&&"string"==typeof t)return v().attr(t);var n=Array.prototype.slice.call(arguments);return i.selection.prototype.attr.apply(v(),n),h},h.style=function(t,e){if(arguments.length<2&&"string"==typeof t)return v().style(t);var n=Array.prototype.slice.call(arguments);return i.selection.prototype.style.apply(v(),n),h},h.direction=function(t){return arguments.length?(u=null==t?t:o(t),h):u},h.offset=function(t){return arguments.length?(c=null==t?t:o(t),h):c},h.html=function(t){return arguments.length?(a=null==t?t:o(t),h):a},h.rootElement=function(t){return arguments.length?(p=null==t?t:o(t),h):p},h.destroy=function(){return n&&(v().remove(),n=null),h};var m=l.map({n:function(){var t=e(this);return{top:t.n.y-n.offsetHeight,left:t.n.x-n.offsetWidth/2}},s:function(){var t=e(this);return{top:t.s.y,left:t.s.x-n.offsetWidth/2}},e:function(){var t=e(this);return{top:t.e.y-n.offsetHeight/2,left:t.e.x}},w:function(){var t=e(this);return{top:t.w.y-n.offsetHeight/2,left:t.w.x-n.offsetWidth}},nw:function(){var t=e(this);return{top:t.nw.y-n.offsetHeight,left:t.nw.x-n.offsetWidth}},ne:function(){var t=e(this);return{top:t.ne.y-n.offsetHeight,left:t.ne.x}},sw:function(){var t=e(this);return{top:t.sw.y,left:t.sw.x-n.offsetWidth}},se:function(){var t=e(this);return{top:t.se.y,left:t.se.x}}}),x=m.keys();function t(){var t=i.select(document.createElement("div"));return t.style("position","absolute").style("top",0).style("opacity",0).style("pointer-events","none").style("box-sizing","border-box"),t.node()}function v(){return null==n&&(n=t(),p.appendChild(n)),i.select(n)}function e(t){for(var e=d||t;null==e.getScreenCTM&&null!=e.parentNode;)e=e.parentNode;var n={},r=e.getScreenCTM(),o=e.getBBox(),l=o.width,i=o.height,s=o.x,f=o.y;return y.x=s,y.y=f,n.nw=y.matrixTransform(r),y.x+=l,n.ne=y.matrixTransform(r),y.y+=i,n.se=y.matrixTransform(r),y.x-=l,n.sw=y.matrixTransform(r),y.y-=i/2,n.w=y.matrixTransform(r),y.x+=l,n.e=y.matrixTransform(r),y.x-=l/2,y.y-=i/2,n.n=y.matrixTransform(r),y.y+=i,n.s=y.matrixTransform(r),n}function o(t){return"function"==typeof t?t:function(){return t}}return h}});
--------------------------------------------------------------------------------
/static/html5shiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document);
--------------------------------------------------------------------------------
/static/respond.min.js:
--------------------------------------------------------------------------------
1 | /*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
2 | * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
3 | * */
4 |
5 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b