quick_count 0.1.2.pre.2 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 438a66bd87229dd94820af384fc47b94ebc75d1648eec942aa0c9d840810cd2c
4
- data.tar.gz: 9f89c4f42d931cab0ef6b84539f7850a2f0f36ddb373ea0d1d363cbd331392a2
3
+ metadata.gz: 669ba9aac319e6ac283367c7c11e5f2ab072df5ca4b3ed7feab47dd08b5924b2
4
+ data.tar.gz: 4c0dd5bf2e1396deeed732e4c7161ff1e4545feaee472062f3a52077926dfb0f
5
5
  SHA512:
6
- metadata.gz: 6d8f36f586cc38b6637f0254dd306c586bb58216ea5dfa908d0572803a0ebd636fe40fad06d0bae6114f22c90c0472af79fcbc74177749010621530ed2c35cfa
7
- data.tar.gz: 775faee06a5eac39fa5d311633dcd7ae263ec5396fa668f5bb0ae1f69d6006524481a5d6609625fc7ac4556943864f00988c1a07572eba0dce13b462996bb567
6
+ metadata.gz: ea9041fa37e956fc824227301d59bb77472cf840c7d5897937b064a3124205c254a38b623c133f14e62fe4e7dd891fb988e66ce4eaf0067b970c77c1a6b05735
7
+ data.tar.gz: '0099de6a90ca27ec4e9d157fe32e7ab8830298fedc996617dd29d410c9e5c1504ab3981bf7f06e2e20348cb18030a0718e0de661468bbd7a398eebd9b4d4caff'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # QuickCount
2
2
 
3
+ ## 0.2.0 _(March 9th 2026)_
4
+ - **Breaking:** Dropped support for Rails 4.x
5
+ - Expanded support for Rails 5.0 through 8.0
6
+ - Zero-config: removed SQL function install/uninstall infrastructure
7
+ - Refactored to adapter pattern architecture
8
+ - Fixed SQL injection vulnerability in table name interpolation
9
+ - Enhanced estimation using `pg_stat_user_tables` with `pg_class` fallback
10
+ - Added support for partitioned tables
11
+
12
+ ## 0.1.2 _(January 7th 2020)_
13
+ - Allows for multiple database support
14
+
3
15
  ## 0.1.1 _(November 27th 2018)_
4
16
  - Fix for table names with special characters
5
17
 
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  [![Version ](https://img.shields.io/gem/v/quick_count.svg)](https://rubygems.org/gems/quick_count)
2
- [![Build Status ](https://travis-ci.org/TwilightCoders/quick_count.svg)](https://travis-ci.org/TwilightCoders/quick_count)
3
- [![Code Climate ](https://api.codeclimate.com/v1/badges/43ba3e038a91b44fba2c/maintainability)](https://codeclimate.com/github/TwilightCoders/quick_count/maintainability)
4
- [![Test Coverage](https://api.codeclimate.com/v1/badges/43ba3e038a91b44fba2c/test_coverage)](https://codeclimate.com/github/TwilightCoders/quick_count/test_coverage)
2
+ [![CI Status ](https://github.com/TwilightCoders/quick_count/workflows/CI/badge.svg)](https://github.com/TwilightCoders/quick_count/actions)
3
+ [![Code Quality](https://img.shields.io/badge/code%20quality-qlty-blue)](https://github.com/TwilightCoders/quick_count)
5
4
 
6
5
  # QuickCount
7
6
 
@@ -12,18 +11,16 @@ Luckily, there are [some tricks](https://www.citusdata.com/blog/2016/10/12/count
12
11
  **Supports:**
13
12
  - PostgreSQL
14
13
  - [Multi-table Inheritance](https://github.com/TwilightCoders/active_record-mti)
14
+ - MySQL
15
15
 
16
- | SQL | Version | Result | Accuracy | Time |
17
- | --- | --- | --- | --- | --- |
18
- | `SELECT count(*) FROM small_table;` | -- | `2037104` | `100.0000000%` | `4.900s` |
19
- | `SELECT quick_count('small_table');` | `v0.0.5` | `1988857` | `97.63158877%` | `0.048s` |
20
- | `SELECT quick_count('small_table');` | `v0.0.6` | `2036407` | `99.96578476%` | `0.050s` |
21
- | `SELECT count(*) FROM medium_table;` | -- | `81716243` | `100.0000000%` | `257.5s` |
22
- | `SELECT quick_count('medium_table');` | `v0.0.5` | `79352284` | `97.10711247%` | `0.049s` |
23
- | `SELECT quick_count('medium_table');` | `v0.0.6` | `81600513` | `99.85837577%` | `0.048s` |
24
- | `SELECT count(*) FROM large_table;` | -- | `455270802` | `100.0000000%` | `310.6s` |
25
- | `SELECT quick_count('large_table');` | `v0.0.5` | `448170751` | `98.44047741%` | `0.047s` |
26
- | `SELECT quick_count('large_table');` | `v0.0.6` | `454448393` | `99.81935828%` | `0.046s` |
16
+ | SQL | Result | Accuracy | Time |
17
+ | --- | --- | --- | --- |
18
+ | `SELECT count(*) FROM small_table;` | `2037104` | `100.0000000%` | `4.900s` |
19
+ | `Post.quick_count` | `2036407` | `99.96578476%` | `0.050s` |
20
+ | `SELECT count(*) FROM medium_table;` | `81716243` | `100.0000000%` | `257.5s` |
21
+ | `Post.quick_count` | `81600513` | `99.85837577%` | `0.048s` |
22
+ | `SELECT count(*) FROM large_table;` | `455270802` | `100.0000000%` | `310.6s` |
23
+ | `Post.quick_count` | `454448393` | `99.81935828%` | `0.046s` |
27
24
 
28
25
  _These metrics were pulled from real databases being used in a production environment._
29
26
 
@@ -39,53 +36,26 @@ And then execute:
39
36
 
40
37
  $ bundle
41
38
 
42
- Or install it yourself as:
43
-
44
- $ gem install quick_count
45
-
46
- To finish the install, in rails console:
47
-
48
- $ QuickCount.install # Install with default (500000) threshold
39
+ That's it. QuickCount automatically integrates with ActiveRecord via a Railtie — no setup step required.
49
40
 
50
41
  ## Usage
51
42
 
52
43
  ```ruby
53
- # user.rb
54
-
55
- QuickCount.install # Install with default (500000) threshold
56
-
57
- # Change the threshold for when `quick_count` kicks in...
58
- QuickCount.install(threshold: 500000)
59
-
60
- class User < ActiveRecord::Base
61
-
62
- end
63
-
44
+ # Fast estimated count for large tables
64
45
  User.quick_count
65
46
 
66
- # Override the default threshold on a case-by-case basis.
67
- User.quick_count(threshold: 600000)
68
-
69
- ```
70
-
71
- ## Uninstallation
72
-
73
- Remove this line to your application's Gemfile:
47
+ # Override the default threshold (500,000) on a case-by-case basis
48
+ User.quick_count(threshold: 1_000_000)
74
49
 
75
- ```ruby
76
- gem 'quick_count'
50
+ # Estimate row count for an arbitrary query
51
+ User.where(active: true).count_estimate
77
52
  ```
78
53
 
79
- And then execute:
80
-
81
- $ bundle
82
-
83
- And in a rails console:
84
-
85
- $ QuickCount.uninstall
54
+ If the estimated count is below the threshold, `quick_count` falls back to an exact `SELECT COUNT(*)`. For tables above the threshold, it returns the estimate directly.
86
55
 
87
56
  ## License
88
- Released under the MIT license - http://opensource.org/licenses/MIT
57
+
58
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
89
59
 
90
60
  ## Development
91
61
 
@@ -96,8 +66,3 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
96
66
  ## Contributing
97
67
 
98
68
  Bug reports and pull requests are welcome on GitHub at https://github.com/TwilightCoders/quick_count.
99
-
100
-
101
- ## License
102
-
103
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -5,14 +5,14 @@ module QuickCount
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  module ClassMethods
8
-
9
8
  def quick_count(threshold: nil)
10
- threshold = threshold ? ", #{threshold}" : nil
11
- result = connection.execute("SELECT quick_count('#{table_name}'#{threshold})")
12
- result[0]["quick_count"].to_i
9
+ QuickCount.quick_count(table_name, threshold: threshold, connection: connection)
13
10
  end
14
-
15
11
  end
16
12
 
13
+ # Instance method for ActiveRecord::Relation
14
+ def count_estimate
15
+ QuickCount.count_estimate(to_sql, connection: connection)
16
+ end
17
17
  end
18
18
  end
@@ -0,0 +1,38 @@
1
+ module QuickCount
2
+ module Adapters
3
+ class Base
4
+ attr_reader :connection
5
+
6
+ def initialize(connection:)
7
+ @connection = connection
8
+ end
9
+
10
+ # Abstract methods that must be implemented by adapters
11
+ def quick_count(table_name, threshold: nil)
12
+ raise NotImplementedError, "#{self.class} must implement #quick_count"
13
+ end
14
+
15
+ def count_estimate(query)
16
+ raise NotImplementedError, "#{self.class} must implement #count_estimate"
17
+ end
18
+
19
+ def supported?
20
+ raise NotImplementedError, "#{self.class} must implement #supported?"
21
+ end
22
+
23
+ private
24
+
25
+ def execute_sql(sql)
26
+ connection.execute(sql)
27
+ end
28
+
29
+ def quote_identifier(identifier)
30
+ connection.quote_column_name(identifier)
31
+ end
32
+
33
+ def quote_value(value)
34
+ connection.quote(value)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,32 @@
1
+ require_relative 'postgresql'
2
+ require_relative 'mysql'
3
+
4
+ module QuickCount
5
+ module Adapters
6
+ class Factory
7
+ ADAPTERS = [Postgresql, Mysql].freeze
8
+
9
+ def self.create(connection:)
10
+ adapter_class = detect_adapter(connection)
11
+
12
+ unless adapter_class
13
+ raise UnsupportedDatabaseError, "Unsupported database: #{connection.adapter_name}"
14
+ end
15
+
16
+ adapter_class.new(connection: connection)
17
+ end
18
+
19
+ def self.detect_adapter(connection)
20
+ ADAPTERS.find { |adapter_class| adapter_class.new(connection: connection).supported? }
21
+ end
22
+
23
+ def self.supported_databases
24
+ ADAPTERS.map do |adapter_class|
25
+ adapter_class.name.split('::').last.downcase
26
+ end
27
+ end
28
+ end
29
+
30
+ class UnsupportedDatabaseError < StandardError; end
31
+ end
32
+ end
@@ -0,0 +1,87 @@
1
+ require_relative 'base'
2
+
3
+ module QuickCount
4
+ module Adapters
5
+ class Mysql < Base
6
+ def supported?
7
+ connection.adapter_name.downcase.match?(/mysql/)
8
+ end
9
+
10
+ def quick_count(table_name, threshold: nil)
11
+ threshold ||= 500_000
12
+
13
+ # Get estimated count from INFORMATION_SCHEMA
14
+ estimated_count = get_table_rows_estimate(table_name)
15
+
16
+ if estimated_count < threshold
17
+ # Use exact count for small tables
18
+ result = execute_sql("SELECT COUNT(*) as count FROM #{quote_identifier(table_name)}")
19
+ result[0]['count'].to_i
20
+ else
21
+ estimated_count
22
+ end
23
+ end
24
+
25
+ def count_estimate(query)
26
+ # Use EXPLAIN to get row estimate
27
+ result = execute_sql("EXPLAIN #{query}")
28
+
29
+ # Parse the rows from EXPLAIN output
30
+ # MySQL EXPLAIN returns different formats, try to extract rows estimate
31
+ if result.respond_to?(:each)
32
+ result.each do |row|
33
+ if row['rows']
34
+ return row['rows'].to_i
35
+ elsif row['Extra'] && row['Extra'].match(/rows=(\d+)/)
36
+ return $1.to_i
37
+ end
38
+ end
39
+ end
40
+
41
+ # Fallback: try to extract from query if it's a simple SELECT
42
+ if query.match(/FROM\s+(\w+)/i)
43
+ table_name = $1
44
+ return get_table_rows_estimate(table_name)
45
+ end
46
+
47
+ # Last resort - return 0
48
+ 0
49
+ end
50
+
51
+ private
52
+
53
+ def current_database
54
+ connection.current_database
55
+ end
56
+
57
+ def get_table_rows_estimate(table_name)
58
+ database_name = current_database
59
+
60
+ # Try INNODB_TABLESTATS first (more accurate for InnoDB)
61
+ result = execute_sql(<<~SQL)
62
+ SELECT NUM_ROWS
63
+ FROM INFORMATION_SCHEMA.INNODB_TABLESTATS
64
+ WHERE NAME = '#{database_name}/#{table_name}'
65
+ SQL
66
+
67
+ if result && result.first && result.first['NUM_ROWS']
68
+ return result.first['NUM_ROWS'].to_i
69
+ end
70
+
71
+ # Fallback to INFORMATION_SCHEMA.TABLES
72
+ result = execute_sql(<<~SQL)
73
+ SELECT TABLE_ROWS
74
+ FROM INFORMATION_SCHEMA.TABLES
75
+ WHERE TABLE_SCHEMA = '#{database_name}'
76
+ AND TABLE_NAME = '#{table_name}'
77
+ SQL
78
+
79
+ if result && result.first && result.first['TABLE_ROWS']
80
+ result.first['TABLE_ROWS'].to_i
81
+ else
82
+ 0
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,85 @@
1
+ require_relative 'base'
2
+
3
+ module QuickCount
4
+ module Adapters
5
+ class Postgresql < Base
6
+ def supported?
7
+ connection.adapter_name.downcase.match?(/postg/)
8
+ end
9
+
10
+ def quick_count(table_name, threshold: nil)
11
+ threshold ||= 500_000
12
+
13
+ # Use direct SQL estimation without needing functions
14
+ estimated_count = get_table_estimate(table_name)
15
+
16
+ if estimated_count < threshold
17
+ # Use exact count for small tables
18
+ result = execute_sql("SELECT COUNT(*) as count FROM #{quote_identifier(table_name)}")
19
+ result[0]['count'].to_i
20
+ else
21
+ estimated_count
22
+ end
23
+ end
24
+
25
+ def count_estimate(query)
26
+ # Use EXPLAIN to get row estimate directly
27
+ result = execute_sql("EXPLAIN #{query}")
28
+
29
+ # Parse the rows from EXPLAIN output
30
+ if result.respond_to?(:each)
31
+ result.each do |row|
32
+ if row['QUERY PLAN'] && row['QUERY PLAN'].match(/rows=(\d+)/)
33
+ return $1.to_i
34
+ end
35
+ end
36
+ end
37
+
38
+ # Fallback: return 0 if we can't parse
39
+ 0
40
+ end
41
+
42
+ private
43
+
44
+ def get_table_estimate(table_name)
45
+ quoted_table = quote_value(table_name.to_s)
46
+
47
+ # Enhanced estimation using both reltuples and live stats
48
+ result = execute_sql(<<~SQL)
49
+ SELECT COALESCE(
50
+ -- Prefer live statistics when available
51
+ pg_stat.n_live_tup,
52
+ -- Fall back to enhanced planner-style estimation
53
+ CASE
54
+ WHEN pg_class.reltuples < 0 THEN NULL -- never vacuumed
55
+ WHEN pg_class.relpages = 0 THEN 0 -- empty table
56
+ ELSE (pg_class.reltuples / GREATEST(pg_class.relpages, 1)) *
57
+ (pg_relation_size(pg_class.oid) / current_setting('block_size')::int)
58
+ END
59
+ )::bigint AS estimated_count
60
+ FROM pg_class
61
+ LEFT JOIN pg_stat_user_tables pg_stat ON pg_stat.relid = pg_class.oid
62
+ WHERE pg_class.oid = #{quoted_table}::regclass
63
+
64
+ -- Handle partitioned tables
65
+ UNION ALL
66
+ SELECT sum(
67
+ COALESCE(
68
+ pg_stat.n_live_tup,
69
+ (pg_class.reltuples/GREATEST(pg_class.relpages,1)) *
70
+ (pg_relation_size(pg_class.oid)/current_setting('block_size')::int)
71
+ )
72
+ )::bigint as estimated_count
73
+ FROM pg_inherits
74
+ JOIN pg_class ON pg_inherits.inhrelid = pg_class.oid
75
+ LEFT JOIN pg_stat_user_tables pg_stat ON pg_stat.relid = pg_class.oid
76
+ WHERE pg_inherits.inhparent = #{quoted_table}::regclass
77
+ SQL
78
+
79
+ # Get the maximum estimate (handles both regular and partitioned tables)
80
+ estimates = result.map { |row| row['estimated_count']&.to_i || 0 }
81
+ estimates.max || 0
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,19 +1,12 @@
1
1
  require 'rails/railtie'
2
2
  require 'quick_count/active_record'
3
- require 'count_estimate/active_record'
4
3
 
5
4
  module QuickCount
6
5
  class Railtie < Rails::Railtie
7
-
8
- # rake_tasks do
9
- # load "../tasks/quick_count_tasks.rake"
10
- # end
11
-
12
6
  initializer 'quick_count.load' do |app|
13
7
  ActiveSupport.on_load(:active_record) do
14
8
  QuickCount.load
15
9
  end
16
10
  end
17
-
18
11
  end
19
12
  end
@@ -1,3 +1,3 @@
1
1
  module QuickCount
2
- VERSION = "0.1.2-2"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/quick_count.rb CHANGED
@@ -1,76 +1,35 @@
1
1
  require 'quick_count/version'
2
2
  require 'quick_count/railtie'
3
+ require 'quick_count/adapters/factory'
3
4
  require 'active_record'
4
5
 
5
6
  module QuickCount
6
-
7
7
  def self.root
8
8
  @root ||= Pathname.new(File.dirname(File.expand_path(File.dirname(__FILE__), '/../')))
9
9
  end
10
10
 
11
11
  def self.load
12
- ::ActiveRecord::Base.send :include, QuickCount::ActiveRecord
13
- ::ActiveRecord::Relation.send :include, CountEstimate::ActiveRecord
12
+ ::ActiveRecord::Base.include QuickCount::ActiveRecord
13
+ ::ActiveRecord::Relation.include QuickCount::ActiveRecord
14
14
  end
15
15
 
16
- def self.install(threshold: 500000, schema: 'public', connection: ::ActiveRecord::Base.connection)
17
- connection.execute(quick_count_sql(schema: schema, threshold: threshold))
18
- connection.execute(count_estimate_sql(schema: schema))
16
+ def self.quick_count(table_name, threshold: nil, connection: ::ActiveRecord::Base.connection)
17
+ adapter = create_adapter(connection: connection)
18
+ adapter.quick_count(table_name, threshold: threshold)
19
19
  end
20
20
 
21
- def self.uninstall(schema: 'public', connection: ::ActiveRecord::Base.connection)
22
- connection.execute("DROP FUNCTION IF EXISTS #{schema}.quick_count(text, bigint);")
23
- connection.execute("DROP FUNCTION IF EXISTS #{schema}.quick_count(text);")
24
- connection.execute("DROP FUNCTION IF EXISTS #{schema}.count_estimate(text);")
21
+ def self.count_estimate(query, connection: ::ActiveRecord::Base.connection)
22
+ adapter = create_adapter(connection: connection)
23
+ adapter.count_estimate(query)
25
24
  end
26
25
 
27
- private
28
-
29
- def self.quick_count_sql(threshold: 500000, schema: 'public')
30
- <<~SQL
31
- CREATE OR REPLACE FUNCTION #{schema}.quick_count(table_name text, threshold bigint default #{threshold}) RETURNS bigint AS
32
- $func$
33
- DECLARE count bigint;
34
- BEGIN
35
- EXECUTE 'SELECT
36
- CASE
37
- WHEN SUM(estimate)::integer < '|| threshold ||' THEN
38
- (SELECT COUNT(*) FROM "'|| table_name ||'")
39
- ELSE
40
- SUM(estimate)::integer
41
- END AS count
42
- FROM (
43
- SELECT
44
- ((SUM(child.reltuples::float)/greatest(SUM(child.relpages::float),1))) * (SUM(pg_relation_size(child.oid))::float / (current_setting(''block_size'')::float)) AS estimate
45
- FROM pg_inherits
46
- JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
47
- JOIN pg_class child ON pg_inherits.inhrelid = child.oid
48
- WHERE parent.relname = '''|| table_name ||'''
49
- UNION SELECT (reltuples::float/greatest(relpages::float, 1)) * (pg_relation_size(pg_class.oid)::float / (current_setting(''block_size'')::float)) AS estimate FROM pg_class where relname='''|| table_name ||'''
50
- ) AS tables' INTO count;
51
- RETURN count;
52
- END
53
- $func$ LANGUAGE plpgsql;
54
- SQL
26
+ def self.supported_databases
27
+ Adapters::Factory.supported_databases
55
28
  end
56
29
 
57
- def self.count_estimate_sql(schema: 'public')
58
- <<~SQL
59
- CREATE OR REPLACE FUNCTION #{schema}.count_estimate(query text) RETURNS integer AS
60
- $func$
61
- DECLARE
62
- rec record;
63
- rows integer;
64
- BEGIN
65
- FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
66
- rows := substring(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)');
67
- EXIT WHEN rows IS NOT NULL;
68
- END LOOP;
69
- RETURN rows;
70
- END
71
- $func$ LANGUAGE plpgsql;
72
- SQL
73
- end
30
+ private
74
31
 
32
+ def self.create_adapter(connection:)
33
+ Adapters::Factory.create(connection: connection)
34
+ end
75
35
  end
76
-
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quick_count
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2.pre.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dale Stevens
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2020-01-07 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: pg
@@ -30,54 +29,40 @@ dependencies:
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: '4'
32
+ version: '5.0'
34
33
  - - "<"
35
34
  - !ruby/object:Gem::Version
36
- version: '6'
35
+ version: '9'
37
36
  type: :runtime
38
37
  prerelease: false
39
38
  version_requirements: !ruby/object:Gem::Requirement
40
39
  requirements:
41
40
  - - ">="
42
41
  - !ruby/object:Gem::Version
43
- version: '4'
42
+ version: '5.0'
44
43
  - - "<"
45
44
  - !ruby/object:Gem::Version
46
- version: '6'
45
+ version: '9'
47
46
  - !ruby/object:Gem::Dependency
48
47
  name: railties
49
48
  requirement: !ruby/object:Gem::Requirement
50
49
  requirements:
51
50
  - - ">="
52
51
  - !ruby/object:Gem::Version
53
- version: '4'
52
+ version: '5.0'
54
53
  - - "<"
55
54
  - !ruby/object:Gem::Version
56
- version: '6'
55
+ version: '9'
57
56
  type: :runtime
58
57
  prerelease: false
59
58
  version_requirements: !ruby/object:Gem::Requirement
60
59
  requirements:
61
60
  - - ">="
62
61
  - !ruby/object:Gem::Version
63
- version: '4'
62
+ version: '5.0'
64
63
  - - "<"
65
64
  - !ruby/object:Gem::Version
66
- version: '6'
67
- - !ruby/object:Gem::Dependency
68
- name: pry-byebug
69
- requirement: !ruby/object:Gem::Requirement
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- version: '0'
74
- type: :development
75
- prerelease: false
76
- version_requirements: !ruby/object:Gem::Requirement
77
- requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: '0'
65
+ version: '9'
81
66
  - !ruby/object:Gem::Dependency
82
67
  name: bundler
83
68
  requirement: !ruby/object:Gem::Requirement
@@ -120,8 +105,8 @@ dependencies:
120
105
  - - ">="
121
106
  - !ruby/object:Gem::Version
122
107
  version: '0'
123
- description: Installs two database functions, `quick_count` and `count_estimate` for
124
- getting count estimations on large tables
108
+ description: Fast approximate row counts for large PostgreSQL tables using database
109
+ statistics instead of COUNT(*)
125
110
  email:
126
111
  - dale@twilightcoders.net
127
112
  executables: []
@@ -131,9 +116,12 @@ files:
131
116
  - CHANGELOG.md
132
117
  - LICENSE
133
118
  - README.md
134
- - lib/count_estimate/active_record.rb
135
119
  - lib/quick_count.rb
136
120
  - lib/quick_count/active_record.rb
121
+ - lib/quick_count/adapters/base.rb
122
+ - lib/quick_count/adapters/factory.rb
123
+ - lib/quick_count/adapters/mysql.rb
124
+ - lib/quick_count/adapters/postgresql.rb
137
125
  - lib/quick_count/railtie.rb
138
126
  - lib/quick_count/version.rb
139
127
  homepage: https://github.com/TwilightCoders/quick_count
@@ -141,24 +129,21 @@ licenses:
141
129
  - MIT
142
130
  metadata:
143
131
  allowed_push_host: https://rubygems.org
144
- post_install_message:
145
132
  rdoc_options: []
146
133
  require_paths:
147
134
  - lib
148
- - spec
149
135
  required_ruby_version: !ruby/object:Gem::Requirement
150
136
  requirements:
151
137
  - - ">="
152
138
  - !ruby/object:Gem::Version
153
- version: '2.0'
139
+ version: '2.5'
154
140
  required_rubygems_version: !ruby/object:Gem::Requirement
155
141
  requirements:
156
- - - ">"
142
+ - - ">="
157
143
  - !ruby/object:Gem::Version
158
- version: 1.3.1
144
+ version: '0'
159
145
  requirements: []
160
- rubygems_version: 3.1.2
161
- signing_key:
146
+ rubygems_version: 4.0.5
162
147
  specification_version: 4
163
148
  summary: Quickly get an accurate count estimation for large tables.
164
149
  test_files: []
@@ -1,14 +0,0 @@
1
- require 'active_support/concern'
2
- require 'pry'
3
-
4
- module CountEstimate
5
- module ActiveRecord
6
-
7
- def count_estimate
8
- my_statement = connection.quote(to_sql)
9
- result = connection.execute("SELECT count_estimate(#{my_statement})")
10
- result[0]["count_estimate"].to_i
11
- end
12
-
13
- end
14
- end