pghero 2.6.0 → 2.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pghero might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/LICENSE.txt +1 -1
- data/README.md +2 -8
- data/app/assets/javascripts/pghero/Chart.bundle.js +4432 -2965
- data/app/assets/javascripts/pghero/application.js +7 -0
- data/app/assets/javascripts/pghero/jquery.js +756 -482
- data/app/assets/javascripts/pghero/nouislider.js +287 -94
- data/app/assets/stylesheets/pghero/application.css +4 -0
- data/app/assets/stylesheets/pghero/nouislider.css +22 -11
- data/app/controllers/pg_hero/home_controller.rb +2 -6
- data/lib/pghero.rb +16 -4
- data/lib/pghero/database.rb +5 -2
- data/lib/pghero/methods/basic.rb +9 -1
- data/lib/pghero/methods/query_stats.rb +77 -20
- data/lib/pghero/methods/sequences.rb +2 -1
- data/lib/pghero/methods/space.rb +4 -0
- data/lib/pghero/methods/suggested_indexes.rb +18 -2
- data/lib/pghero/methods/system.rb +1 -2
- data/lib/pghero/methods/users.rb +2 -2
- data/lib/pghero/version.rb +1 -1
- data/licenses/LICENSE-chart.js.txt +9 -0
- data/licenses/LICENSE-chartkick.js.txt +22 -0
- data/licenses/LICENSE-highlight.js.txt +29 -0
- data/licenses/LICENSE-jquery.txt +20 -0
- data/licenses/LICENSE-moment.txt +22 -0
- data/licenses/LICENSE-nouislider.txt +21 -0
- metadata +13 -91
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            /*! nouislider - 14. | 
| 1 | 
            +
            /*! nouislider - 14.6.1 - 8/17/2020 */
         | 
| 2 2 | 
             
            /* Functional styling;
         | 
| 3 3 | 
             
             * These styles are required for noUiSlider to function.
         | 
| 4 4 | 
             
             * You don't need to change these rules to apply your design.
         | 
| @@ -18,7 +18,6 @@ | |
| 18 18 | 
             
            }
         | 
| 19 19 | 
             
            .noUi-target {
         | 
| 20 20 | 
             
              position: relative;
         | 
| 21 | 
            -
              direction: ltr;
         | 
| 22 21 | 
             
            }
         | 
| 23 22 | 
             
            .noUi-base,
         | 
| 24 23 | 
             
            .noUi-connects {
         | 
| @@ -39,7 +38,7 @@ | |
| 39 38 | 
             
              position: absolute;
         | 
| 40 39 | 
             
              z-index: 1;
         | 
| 41 40 | 
             
              top: 0;
         | 
| 42 | 
            -
               | 
| 41 | 
            +
              right: 0;
         | 
| 43 42 | 
             
              -ms-transform-origin: 0 0;
         | 
| 44 43 | 
             
              -webkit-transform-origin: 0 0;
         | 
| 45 44 | 
             
              -webkit-transform-style: preserve-3d;
         | 
| @@ -56,9 +55,9 @@ | |
| 56 55 | 
             
            }
         | 
| 57 56 | 
             
            /* Offset direction
         | 
| 58 57 | 
             
             */
         | 
| 59 | 
            -
             | 
| 60 | 
            -
              left:  | 
| 61 | 
            -
              right:  | 
| 58 | 
            +
            .noUi-txt-dir-rtl.noUi-horizontal .noUi-origin {
         | 
| 59 | 
            +
              left: 0;
         | 
| 60 | 
            +
              right: auto;
         | 
| 62 61 | 
             
            }
         | 
| 63 62 | 
             
            /* Give origins 0 height/width so they don't interfere with clicking the
         | 
| 64 63 | 
             
             * connect elements.
         | 
| @@ -94,7 +93,7 @@ html:not([dir="rtl"]) .noUi-horizontal .noUi-origin { | |
| 94 93 | 
             
            .noUi-horizontal .noUi-handle {
         | 
| 95 94 | 
             
              width: 34px;
         | 
| 96 95 | 
             
              height: 28px;
         | 
| 97 | 
            -
               | 
| 96 | 
            +
              right: -17px;
         | 
| 98 97 | 
             
              top: -6px;
         | 
| 99 98 | 
             
            }
         | 
| 100 99 | 
             
            .noUi-vertical {
         | 
| @@ -103,12 +102,12 @@ html:not([dir="rtl"]) .noUi-horizontal .noUi-origin { | |
| 103 102 | 
             
            .noUi-vertical .noUi-handle {
         | 
| 104 103 | 
             
              width: 28px;
         | 
| 105 104 | 
             
              height: 34px;
         | 
| 106 | 
            -
               | 
| 105 | 
            +
              right: -6px;
         | 
| 107 106 | 
             
              top: -17px;
         | 
| 108 107 | 
             
            }
         | 
| 109 | 
            -
             | 
| 110 | 
            -
               | 
| 111 | 
            -
               | 
| 108 | 
            +
            .noUi-txt-dir-rtl.noUi-horizontal .noUi-handle {
         | 
| 109 | 
            +
              left: -17px;
         | 
| 110 | 
            +
              right: auto;
         | 
| 112 111 | 
             
            }
         | 
| 113 112 | 
             
            /* Styling;
         | 
| 114 113 | 
             
             * Giving the connect element a border radius causes issues with using transform: scale
         | 
| @@ -297,3 +296,15 @@ html:not([dir="rtl"]) .noUi-horizontal .noUi-handle { | |
| 297 296 | 
             
              top: 50%;
         | 
| 298 297 | 
             
              right: 120%;
         | 
| 299 298 | 
             
            }
         | 
| 299 | 
            +
            .noUi-horizontal .noUi-origin > .noUi-tooltip {
         | 
| 300 | 
            +
              -webkit-transform: translate(50%, 0);
         | 
| 301 | 
            +
              transform: translate(50%, 0);
         | 
| 302 | 
            +
              left: auto;
         | 
| 303 | 
            +
              bottom: 10px;
         | 
| 304 | 
            +
            }
         | 
| 305 | 
            +
            .noUi-vertical .noUi-origin > .noUi-tooltip {
         | 
| 306 | 
            +
              -webkit-transform: translate(0, -18px);
         | 
| 307 | 
            +
              transform: translate(0, -18px);
         | 
| 308 | 
            +
              top: auto;
         | 
| 309 | 
            +
              right: 28px;
         | 
| 310 | 
            +
            }
         | 
| @@ -2,7 +2,7 @@ module PgHero | |
| 2 2 | 
             
              class HomeController < ActionController::Base
         | 
| 3 3 | 
             
                layout "pg_hero/application"
         | 
| 4 4 |  | 
| 5 | 
            -
                protect_from_forgery
         | 
| 5 | 
            +
                protect_from_forgery with: :exception
         | 
| 6 6 |  | 
| 7 7 | 
             
                http_basic_authenticate_with name: PgHero.username, password: PgHero.password if PgHero.password
         | 
| 8 8 |  | 
| @@ -373,11 +373,7 @@ module PgHero | |
| 373 373 | 
             
                protected
         | 
| 374 374 |  | 
| 375 375 | 
             
                def redirect_backward(options = {})
         | 
| 376 | 
            -
                   | 
| 377 | 
            -
                    redirect_back options.merge(fallback_location: root_path)
         | 
| 378 | 
            -
                  else
         | 
| 379 | 
            -
                    redirect_to :back, options
         | 
| 380 | 
            -
                  end
         | 
| 376 | 
            +
                  redirect_back fallback_location: root_path, **options
         | 
| 381 377 | 
             
                end
         | 
| 382 378 |  | 
| 383 379 | 
             
                def set_database
         | 
    
        data/lib/pghero.rb
    CHANGED
    
    | @@ -113,13 +113,13 @@ module PgHero | |
| 113 113 |  | 
| 114 114 | 
             
                      if !ENV["PGHERO_DATABASE_URL"] && spec_supported?
         | 
| 115 115 | 
             
                        ActiveRecord::Base.configurations.configs_for(env_name: env, include_replicas: true).each do |db|
         | 
| 116 | 
            -
                          databases[db. | 
| 116 | 
            +
                          databases[db.send(spec_name_key)] = {"spec" => db.send(spec_name_key)}
         | 
| 117 117 | 
             
                        end
         | 
| 118 118 | 
             
                      end
         | 
| 119 119 |  | 
| 120 120 | 
             
                      if databases.empty?
         | 
| 121 121 | 
             
                        databases["primary"] = {
         | 
| 122 | 
            -
                          "url" => ENV["PGHERO_DATABASE_URL"] || ActiveRecord::Base | 
| 122 | 
            +
                          "url" => ENV["PGHERO_DATABASE_URL"] || connection_config(ActiveRecord::Base)
         | 
| 123 123 | 
             
                        }
         | 
| 124 124 | 
             
                      end
         | 
| 125 125 |  | 
| @@ -196,13 +196,13 @@ module PgHero | |
| 196 196 | 
             
                # stats for old databases are not cleaned up since we can't use an index
         | 
| 197 197 | 
             
                def clean_query_stats
         | 
| 198 198 | 
             
                  each_database do |database|
         | 
| 199 | 
            -
                     | 
| 199 | 
            +
                    database.clean_query_stats
         | 
| 200 200 | 
             
                  end
         | 
| 201 201 | 
             
                end
         | 
| 202 202 |  | 
| 203 203 | 
             
                def clean_space_stats
         | 
| 204 204 | 
             
                  each_database do |database|
         | 
| 205 | 
            -
                     | 
| 205 | 
            +
                    database.clean_space_stats
         | 
| 206 206 | 
             
                  end
         | 
| 207 207 | 
             
                end
         | 
| 208 208 |  | 
| @@ -211,6 +211,18 @@ module PgHero | |
| 211 211 | 
             
                  ActiveRecord::VERSION::MAJOR >= 6
         | 
| 212 212 | 
             
                end
         | 
| 213 213 |  | 
| 214 | 
            +
                # private
         | 
| 215 | 
            +
                def connection_config(model)
         | 
| 216 | 
            +
                  ActiveRecord::VERSION::STRING.to_f >= 6.1 ? model.connection_db_config.configuration_hash : model.connection_config
         | 
| 217 | 
            +
                end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                # private
         | 
| 220 | 
            +
                # Rails 6.1 deprecate `spec_name` and use `name` for configurations
         | 
| 221 | 
            +
                # https://github.com/rails/rails/pull/38536
         | 
| 222 | 
            +
                def spec_name_key
         | 
| 223 | 
            +
                  ActiveRecord::VERSION::STRING.to_f >= 6.1 ? :name : :spec_name
         | 
| 224 | 
            +
                end
         | 
| 225 | 
            +
             | 
| 214 226 | 
             
                private
         | 
| 215 227 |  | 
| 216 228 | 
             
                def each_database
         | 
    
        data/lib/pghero/database.rb
    CHANGED
    
    | @@ -149,11 +149,14 @@ module PgHero | |
| 149 149 | 
             
                  # resolve spec
         | 
| 150 150 | 
             
                  if !url && config["spec"]
         | 
| 151 151 | 
             
                    raise Error, "Spec requires Rails 6+" unless PgHero.spec_supported?
         | 
| 152 | 
            -
                     | 
| 152 | 
            +
                    config_options = {env_name: PgHero.env, PgHero.spec_name_key => config["spec"], include_replicas: true}
         | 
| 153 | 
            +
                    resolved = ActiveRecord::Base.configurations.configs_for(**config_options)
         | 
| 153 154 | 
             
                    raise Error, "Spec not found: #{config["spec"]}" unless resolved
         | 
| 154 | 
            -
                    url = resolved.config
         | 
| 155 | 
            +
                    url = ActiveRecord::VERSION::STRING.to_f >= 6.1 ? resolved.configuration_hash : resolved.config
         | 
| 155 156 | 
             
                  end
         | 
| 156 157 |  | 
| 158 | 
            +
                  url = url.dup
         | 
| 159 | 
            +
             | 
| 157 160 | 
             
                  Class.new(PgHero::Connection) do
         | 
| 158 161 | 
             
                    def self.name
         | 
| 159 162 | 
             
                      "PgHero::Connection::Database#{object_id}"
         | 
    
        data/lib/pghero/methods/basic.rb
    CHANGED
    
    | @@ -18,6 +18,10 @@ module PgHero | |
| 18 18 | 
             
                    select_one("SELECT current_database()")
         | 
| 19 19 | 
             
                  end
         | 
| 20 20 |  | 
| 21 | 
            +
                  def current_user
         | 
| 22 | 
            +
                    select_one("SELECT current_user")
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 21 25 | 
             
                  def server_version
         | 
| 22 26 | 
             
                    @server_version ||= select_one("SHOW server_version")
         | 
| 23 27 | 
             
                  end
         | 
| @@ -38,7 +42,11 @@ module PgHero | |
| 38 42 | 
             
                    retries = 0
         | 
| 39 43 | 
             
                    begin
         | 
| 40 44 | 
             
                      result = conn.select_all(add_source(squish(sql)))
         | 
| 41 | 
            -
                       | 
| 45 | 
            +
                      if ActiveRecord::VERSION::STRING.to_f >= 6.1
         | 
| 46 | 
            +
                        result = result.map(&:symbolize_keys)
         | 
| 47 | 
            +
                      else
         | 
| 48 | 
            +
                        result = result.map { |row| Hash[row.map { |col, val| [col.to_sym, result.column_types[col].send(:cast_value, val)] }] }
         | 
| 49 | 
            +
                      end
         | 
| 42 50 | 
             
                      if filter_data
         | 
| 43 51 | 
             
                        query_columns.each do |column|
         | 
| 44 52 | 
             
                          result.each do |row|
         | 
| @@ -56,8 +56,46 @@ module PgHero | |
| 56 56 | 
             
                    true
         | 
| 57 57 | 
             
                  end
         | 
| 58 58 |  | 
| 59 | 
            -
                   | 
| 60 | 
            -
             | 
| 59 | 
            +
                  # TODO scope by database in PgHero 3.0
         | 
| 60 | 
            +
                  # (add database: database_name to options)
         | 
| 61 | 
            +
                  def reset_query_stats(**options)
         | 
| 62 | 
            +
                    reset_instance_query_stats(**options)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  # resets query stats for the entire instance
         | 
| 66 | 
            +
                  # it's possible to reset stats for a specific
         | 
| 67 | 
            +
                  # database, user or query hash in Postgres 12+
         | 
| 68 | 
            +
                  def reset_instance_query_stats(database: nil, user: nil, query_hash: nil, raise_errors: false)
         | 
| 69 | 
            +
                    if database || user || query_hash
         | 
| 70 | 
            +
                      raise PgHero::Error, "Requires PostgreSQL 12+" if server_version_num < 120000
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                      if database
         | 
| 73 | 
            +
                        database_id = execute("SELECT oid FROM pg_database WHERE datname = #{quote(database)}").first.try(:[], "oid")
         | 
| 74 | 
            +
                        raise PgHero::Error, "Database not found: #{database}" unless database_id
         | 
| 75 | 
            +
                      else
         | 
| 76 | 
            +
                        database_id = 0
         | 
| 77 | 
            +
                      end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                      if user
         | 
| 80 | 
            +
                        user_id = execute("SELECT usesysid FROM pg_user WHERE usename = #{quote(user)}").first.try(:[], "usesysid")
         | 
| 81 | 
            +
                        raise PgHero::Error, "User not found: #{user}" unless user_id
         | 
| 82 | 
            +
                      else
         | 
| 83 | 
            +
                        user_id = 0
         | 
| 84 | 
            +
                      end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                      if query_hash
         | 
| 87 | 
            +
                        query_id = query_hash.to_i
         | 
| 88 | 
            +
                        # may not be needed
         | 
| 89 | 
            +
                        # but not intuitive that all query hashes are reset with 0
         | 
| 90 | 
            +
                        raise PgHero::Error, "Invalid query hash: #{query_hash}" if query_id == 0
         | 
| 91 | 
            +
                      else
         | 
| 92 | 
            +
                        query_id = 0
         | 
| 93 | 
            +
                      end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                      execute("SELECT pg_stat_statements_reset(#{quote(user_id.to_i)}, #{quote(database_id.to_i)}, #{quote(query_id.to_i)})")
         | 
| 96 | 
            +
                    else
         | 
| 97 | 
            +
                      execute("SELECT pg_stat_statements_reset()")
         | 
| 98 | 
            +
                    end
         | 
| 61 99 | 
             
                    true
         | 
| 62 100 | 
             
                  rescue ActiveRecord::StatementInvalid => e
         | 
| 63 101 | 
             
                    raise e if raise_errors
         | 
| @@ -104,31 +142,32 @@ module PgHero | |
| 104 142 | 
             
                      query_stats[database_id] = query_stats(limit: 1000000, database: database_name)
         | 
| 105 143 | 
             
                    end
         | 
| 106 144 |  | 
| 107 | 
            -
                     | 
| 145 | 
            +
                    query_stats = query_stats.select { |_, v| v.any? }
         | 
| 108 146 |  | 
| 109 | 
            -
                     | 
| 147 | 
            +
                    # nothing to do
         | 
| 148 | 
            +
                    return if query_stats.empty?
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    # use mapping, not query stats here
         | 
| 151 | 
            +
                    # TODO add option for this, and make default in PgHero 3.0
         | 
| 152 | 
            +
                    if false # mapping.size == 1 && server_version_num >= 120000
         | 
| 110 153 | 
             
                      query_stats.each do |db_id, db_query_stats|
         | 
| 111 | 
            -
                        if  | 
| 112 | 
            -
                           | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
                                now,
         | 
| 120 | 
            -
                                supports_query_hash ? qs[:query_hash] : nil,
         | 
| 121 | 
            -
                                qs[:user]
         | 
| 122 | 
            -
                              ]
         | 
| 123 | 
            -
                            end
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                          columns = %w[database query total_time calls captured_at query_hash user]
         | 
| 126 | 
            -
                          insert_stats("pghero_query_stats", columns, values)
         | 
| 154 | 
            +
                        if reset_query_stats(database: mapping[db_id], raise_errors: raise_errors)
         | 
| 155 | 
            +
                          insert_query_stats(db_id, db_query_stats, now)
         | 
| 156 | 
            +
                        end
         | 
| 157 | 
            +
                      end
         | 
| 158 | 
            +
                    else
         | 
| 159 | 
            +
                      if reset_query_stats(raise_errors: raise_errors)
         | 
| 160 | 
            +
                        query_stats.each do |db_id, db_query_stats|
         | 
| 161 | 
            +
                          insert_query_stats(db_id, db_query_stats, now)
         | 
| 127 162 | 
             
                        end
         | 
| 128 163 | 
             
                      end
         | 
| 129 164 | 
             
                    end
         | 
| 130 165 | 
             
                  end
         | 
| 131 166 |  | 
| 167 | 
            +
                  def clean_query_stats
         | 
| 168 | 
            +
                    PgHero::QueryStats.where(database: id).where("captured_at < ?", 14.days.ago).delete_all
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
             | 
| 132 171 | 
             
                  def slow_queries(query_stats: nil, **options)
         | 
| 133 172 | 
             
                    query_stats ||= self.query_stats(options)
         | 
| 134 173 | 
             
                    query_stats.select { |q| q[:calls].to_i >= slow_query_calls.to_i && q[:average_time].to_f >= slow_query_ms.to_f }
         | 
| @@ -287,6 +326,24 @@ module PgHero | |
| 287 326 | 
             
                  def normalize_query(query)
         | 
| 288 327 | 
             
                    squish(query.to_s.gsub(/\?(, ?\?)+/, "?").gsub(/\/\*.+?\*\//, ""))
         | 
| 289 328 | 
             
                  end
         | 
| 329 | 
            +
             | 
| 330 | 
            +
                  def insert_query_stats(db_id, db_query_stats, now)
         | 
| 331 | 
            +
                    values =
         | 
| 332 | 
            +
                      db_query_stats.map do |qs|
         | 
| 333 | 
            +
                        [
         | 
| 334 | 
            +
                          db_id,
         | 
| 335 | 
            +
                          qs[:query],
         | 
| 336 | 
            +
                          qs[:total_minutes] * 60 * 1000,
         | 
| 337 | 
            +
                          qs[:calls],
         | 
| 338 | 
            +
                          now,
         | 
| 339 | 
            +
                          supports_query_hash? ? qs[:query_hash] : nil,
         | 
| 340 | 
            +
                          qs[:user]
         | 
| 341 | 
            +
                        ]
         | 
| 342 | 
            +
                      end
         | 
| 343 | 
            +
             | 
| 344 | 
            +
                    columns = %w[database query total_time calls captured_at query_hash user]
         | 
| 345 | 
            +
                    insert_stats("pghero_query_stats", columns, values)
         | 
| 346 | 
            +
                  end
         | 
| 290 347 | 
             
                end
         | 
| 291 348 | 
             
              end
         | 
| 292 349 | 
             
            end
         | 
    
        data/lib/pghero/methods/space.rb
    CHANGED
    
    | @@ -129,6 +129,10 @@ module PgHero | |
| 129 129 | 
             
                    insert_stats("pghero_space_stats", columns, values) if values.any?
         | 
| 130 130 | 
             
                  end
         | 
| 131 131 |  | 
| 132 | 
            +
                  def clean_space_stats
         | 
| 133 | 
            +
                    PgHero::SpaceStats.where(database: id).where("captured_at < ?", 90.days.ago).delete_all
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
             | 
| 132 136 | 
             
                  def space_stats_enabled?
         | 
| 133 137 | 
             
                    table_exists?("pghero_space_stats")
         | 
| 134 138 | 
             
                  end
         | 
| @@ -30,7 +30,23 @@ module PgHero | |
| 30 30 | 
             
                          if best_index[:found]
         | 
| 31 31 | 
             
                            index = best_index[:index]
         | 
| 32 32 | 
             
                            best_index[:table_indexes] = indexes_by_table[index[:table]].to_a
         | 
| 33 | 
            -
             | 
| 33 | 
            +
             | 
| 34 | 
            +
                            # indexes of same type
         | 
| 35 | 
            +
                            indexes = existing_columns[index[:using] || "btree"][index[:table]]
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                            if best_index[:structure][:sort].empty?
         | 
| 38 | 
            +
                              # gist indexes without an opclass
         | 
| 39 | 
            +
                              # (opclass is part of column name, so columns won't match if opclass present)
         | 
| 40 | 
            +
                              indexes += existing_columns["gist"][index[:table]]
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                              # hash indexes work for equality
         | 
| 43 | 
            +
                              indexes += existing_columns["hash"][index[:table]] if best_index[:structure][:where].all? { |v| v[:op] == "=" }
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                              # brin indexes work for all
         | 
| 46 | 
            +
                              indexes += existing_columns["brin"][index[:table]]
         | 
| 47 | 
            +
                            end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                            covering_index = indexes.find { |e| index_covers?(e.map { |v| v.sub(/ inet_ops\z/, "") }, index[:columns]) }
         | 
| 34 50 | 
             
                            if covering_index
         | 
| 35 51 | 
             
                              best_index[:covering_index] = covering_index
         | 
| 36 52 | 
             
                              best_index[:explanation] = "Covered by index on (#{covering_index.join(", ")})"
         | 
| @@ -86,7 +102,7 @@ module PgHero | |
| 86 102 | 
             
                    # get stats about columns for relevant tables
         | 
| 87 103 | 
             
                    tables = parts.values.map { |t| t[:table] }.uniq
         | 
| 88 104 | 
             
                    # TODO get schema from query structure, then try search path
         | 
| 89 | 
            -
                    schema =  | 
| 105 | 
            +
                    schema = PgHero.connection_config(connection_model)[:schema] || "public"
         | 
| 90 106 | 
             
                    if tables.any?
         | 
| 91 107 | 
             
                      row_stats = Hash[table_stats(table: tables, schema: schema).map { |i| [i[:table], i[:estimated_rows]] }]
         | 
| 92 108 | 
             
                      col_stats = column_stats(table: tables, schema: schema).group_by { |i| i[:table] }
         | 
| @@ -144,7 +144,7 @@ module PgHero | |
| 144 144 |  | 
| 145 145 | 
             
                    # validate input since we need to interpolate below
         | 
| 146 146 | 
             
                    raise Error, "Invalid metric name" unless metric_name =~ /\A[a-z\/_]+\z/i
         | 
| 147 | 
            -
                    raise Error, "Invalid database id" unless gcp_database_id =~ /\A[a- | 
| 147 | 
            +
                    raise Error, "Invalid database id" unless gcp_database_id =~ /\A[a-z0-9\-:]+\z/i
         | 
| 148 148 |  | 
| 149 149 | 
             
                    # we handle three situations:
         | 
| 150 150 | 
             
                    # 1. google-cloud-monitoring-v3
         | 
| @@ -276,7 +276,6 @@ module PgHero | |
| 276 276 |  | 
| 277 277 | 
             
                  def add_missing_data(data, start_time, end_time, period)
         | 
| 278 278 | 
             
                    time = start_time
         | 
| 279 | 
            -
                    end_time = end_time
         | 
| 280 279 | 
             
                    while time < end_time
         | 
| 281 280 | 
             
                      data[time] ||= nil
         | 
| 282 281 | 
             
                      time += period
         | 
    
        data/lib/pghero/methods/users.rb
    CHANGED
    
    | @@ -5,7 +5,7 @@ module PgHero | |
| 5 5 | 
             
                  # TODO quote in 3.0, but still not officially supported
         | 
| 6 6 | 
             
                  def create_user(user, password: nil, schema: "public", database: nil, readonly: false, tables: nil)
         | 
| 7 7 | 
             
                    password ||= random_password
         | 
| 8 | 
            -
                    database ||=  | 
| 8 | 
            +
                    database ||= PgHero.connection_config(connection_model)[:database]
         | 
| 9 9 |  | 
| 10 10 | 
             
                    commands =
         | 
| 11 11 | 
             
                      [
         | 
| @@ -44,7 +44,7 @@ module PgHero | |
| 44 44 | 
             
                  # documented as unsafe to pass user input
         | 
| 45 45 | 
             
                  # TODO quote in 3.0, but still not officially supported
         | 
| 46 46 | 
             
                  def drop_user(user, schema: "public", database: nil)
         | 
| 47 | 
            -
                    database ||=  | 
| 47 | 
            +
                    database ||= PgHero.connection_config(connection_model)[:database]
         | 
| 48 48 |  | 
| 49 49 | 
             
                    # thanks shiftb
         | 
| 50 50 | 
             
                    commands =
         | 
    
        data/lib/pghero/version.rb
    CHANGED
    
    
| @@ -0,0 +1,9 @@ | |
| 1 | 
            +
            The MIT License (MIT)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Copyright (c) 2018 Chart.js Contributors
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            Copyright (c) 2013-2019 Andrew Kane
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            MIT License
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 6 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 7 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 8 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 9 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 10 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 11 | 
            +
            the following conditions:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 14 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 17 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 18 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 19 | 
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 20 | 
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 21 | 
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 22 | 
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         |