├── .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 |
65 | 77 | 80 |
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 |
74 | 86 | 89 |
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 | 168 | 169 | 170 | {{end}} 171 | {{if .Table}} 172 | 173 | 174 | 175 | 176 | {{end}} 177 | {{if .Index}} 178 | 179 | 180 | 181 | 182 | {{end}} 183 | {{if .Alias}} 184 | 185 | 186 | 187 | 188 | {{end}} 189 | {{if .ParentRelationship}} 190 | 191 | 192 | 193 | 194 | {{end}} 195 | {{if .PlanCost}} 196 | 197 | 198 | 199 | 200 | {{end}} 201 | {{if .PlanRows}} 202 | 203 | 204 | 205 | 206 | {{end}} 207 | {{if .PlanWidth}} 208 | 209 | 210 | 211 | 212 | {{end}} 213 | {{if .ActualTotalTime}} 214 | 215 | 216 | 217 | 218 | {{end}} 219 | {{if .ActualRows}} 220 | 221 | 222 | 223 | 224 | {{end}} 225 | {{if .ActualLoops}} 226 | 227 | 228 | 229 | 230 | {{end}} 231 | {{if .Filter}} 232 | 233 | 234 | 235 | 236 | {{end}} 237 | {{if .JoinFilter}} 238 | 239 | 240 | 241 | 242 | {{end}} 243 | {{if .HashCond}} 244 | 245 | 246 | 247 | 248 | {{end}} 249 | {{if .IndexCond}} 250 | 251 | 252 | 253 | 254 | {{end}} 255 | {{if .RecheckCond}} 256 | 257 | 258 | 259 | 260 | {{end}} 261 | {{if .BuffersHit}} 262 | 263 | 264 | 265 | 266 | {{end}} 267 | {{if .BuffersRead}} 268 | 269 | 270 | 271 | 272 | {{end}} 273 | {{if .HashBuckets}} 274 | 275 | 276 | 277 | 278 | {{end}} 279 | {{if .HashBatches}} 280 | 281 | 282 | 283 | 284 | {{end}} 285 | {{if .MemoryUsage}} 286 | 287 | 288 | 289 | 290 | {{end}} 291 | {{if .SortKey}} 292 | 293 | 294 | 295 | 296 | {{end}} 297 | {{if .SortMethod}} 298 | 299 | 300 | 301 | 302 | {{end}} 303 | {{if .SortSpaceUsed}} 304 | 305 | 306 | 307 | 308 | {{end}} 309 | {{if .SortSpaceType}} 310 | 311 | 312 | 313 | 314 | {{end}} 315 | 316 |
Method{{.Method}}
Table{{.Table}}
Index{{.Index}}
Alias{{.Alias}}
Parent Relationship{{.ParentRelationship}}
Plan Cost{{.PlanCost}}
Plan Rows{{.PlanRows}}
Plan Width{{.PlanWidth}}
Actual Total Time{{.ActualTotalTime}} ms
Actual Rows{{.ActualRows}}
Actual Loops{{.ActualLoops}}
Filter{{.Filter}}
Join Filter{{.JoinFilter}}
Hash Cond{{.HashCond}}
Index Cond{{.IndexCond}}
Recheck Cond{{.RecheckCond}}
Buffers Shared Hit{{.BuffersHit}}
Buffers Shared Read{{.BuffersRead}}
Hash Buckets{{.HashBuckets}}
Hash Batches{{.HashBatches}}
Memory Usage{{.MemoryUsage}} kB
Sort Key{{.SortKey}}
Sort Method{{.SortMethod}}
Sort Space Used{{.SortSpaceUsed}} kB
Sort Space Type{{.SortSpaceType}}
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