pg_aggregates 0.2.1 → 0.2.3

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.
@@ -2,9 +2,18 @@
2
2
 
3
3
  module PgAggregates
4
4
  module SchemaStatements
5
+ # Reserved words to skip when checking function dependencies
6
+ RESERVED_WORDS = ["public"].freeze
7
+
5
8
  def create_aggregate(name, version: nil, sql_definition: nil)
6
9
  raise ArgumentError, "Must provide either sql_definition or version" if sql_definition.nil? && version.nil?
7
10
 
11
+ # First, check if the function already exists to avoid duplicate creation attempts
12
+ return if aggregate_exists?(name)
13
+
14
+ # Check if dependent functions exist before attempting to create
15
+ check_dependent_functions(sql_definition || read_aggregate_definition(name, version))
16
+
8
17
  if sql_definition
9
18
  execute sql_definition
10
19
  else
@@ -18,6 +27,14 @@ module PgAggregates
18
27
 
19
28
  execute aggregate_definition.to_sql
20
29
  end
30
+ rescue ActiveRecord::StatementInvalid => e
31
+ raise unless /function .* does not exist/.match?(e.message)
32
+
33
+ puts "WARNING: Failed to create aggregate #{name} because a required function does not exist."
34
+ puts " This could indicate a dependency ordering issue."
35
+ puts " Error: #{e.message}"
36
+
37
+ raise
21
38
  end
22
39
 
23
40
  def drop_aggregate(name, *arg_types, force: false)
@@ -25,5 +42,59 @@ module PgAggregates
25
42
  force_clause = force ? " CASCADE" : ""
26
43
  execute "DROP AGGREGATE IF EXISTS #{name}#{arg_types_sql}#{force_clause}"
27
44
  end
45
+
46
+ private
47
+
48
+ def aggregate_exists?(name)
49
+ sql = <<-SQL
50
+ SELECT 1
51
+ FROM pg_catalog.pg_proc p
52
+ JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
53
+ WHERE p.proname = '#{name}'
54
+ AND p.prokind = 'a'
55
+ SQL
56
+
57
+ result = execute(sql)
58
+ result.any?
59
+ rescue StandardError
60
+ false
61
+ end
62
+
63
+ def read_aggregate_definition(name, version)
64
+ aggregate_definition = PgAggregates::AggregateDefinition.new(name, version: version)
65
+ File.read(aggregate_definition.path) if File.exist?(aggregate_definition.path)
66
+ end
67
+
68
+ def check_dependent_functions(sql_definition)
69
+ return unless sql_definition
70
+
71
+ # Extract function names mentioned in the aggregate definition
72
+ # Be careful to extract only the function name, not schema qualifiers
73
+ function_matches = sql_definition.scan(/sfunc\s*=\s*['"]?(?:(?:[a-zA-Z0-9_]+\.)?([a-zA-Z0-9_]+))['"]?/i)
74
+ function_names = function_matches.flatten.compact.uniq
75
+
76
+ # For each referenced function, check if it exists
77
+ function_names.each do |function_name|
78
+ # Skip if function_name is empty or a reserved word
79
+ next if function_name.nil? || function_name.empty? || RESERVED_WORDS.include?(function_name.downcase)
80
+
81
+ check_sql = <<-SQL
82
+ SELECT 1#{" "}
83
+ FROM pg_catalog.pg_proc p
84
+ JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
85
+ WHERE p.proname = '#{function_name}'
86
+ AND n.nspname = 'public'
87
+ SQL
88
+
89
+ result = execute(check_sql)
90
+ unless result.any?
91
+ puts "WARNING: Aggregate depends on function '#{function_name}' which does not exist"
92
+ puts " This will likely fail. Create the function first."
93
+ end
94
+ end
95
+ rescue StandardError => e
96
+ # Log the error but continue
97
+ puts "WARNING: Error checking function dependencies: #{e.message}"
98
+ end
28
99
  end
29
100
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgAggregates
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  end
data/lib/pg_aggregates.rb CHANGED
@@ -10,11 +10,27 @@ require_relative "pg_aggregates/schema_dumper"
10
10
  require_relative "pg_aggregates/railtie"
11
11
 
12
12
  module PgAggregates
13
+ module_function
14
+
13
15
  class Error < StandardError; end
14
16
 
15
- class << self
16
- def database
17
- ActiveRecord::Base.connection
18
- end
17
+ def load
18
+ # This is crucial - we must ensure proper load order:
19
+ # 1. extensions
20
+ # 2. types
21
+ # 3. functions
22
+ # 4. aggregates
23
+ # 5. tables
24
+
25
+ # Add schema statements and command recorder
26
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.include PgAggregates::SchemaStatements
27
+ ActiveRecord::Migration::CommandRecorder.include PgAggregates::CommandRecorder
28
+
29
+ # Hook into the schema dumper, with dependency awareness
30
+ ActiveRecord::SchemaDumper.prepend PgAggregates::SchemaDumper
31
+ end
32
+
33
+ def database
34
+ ActiveRecord::Base.connection
19
35
  end
20
36
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_aggregates
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - mhenrixon
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-11-05 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: activerecord
@@ -72,6 +71,8 @@ files:
72
71
  - gemfiles/rails_7.0.gemfile.lock
73
72
  - gemfiles/rails_7.1.gemfile
74
73
  - gemfiles/rails_7.1.gemfile.lock
74
+ - gemfiles/rails_8.0.gemfile
75
+ - gemfiles/rails_8.0.gemfile.lock
75
76
  - lib/generators/pg/aggregate/aggregate_generator.rb
76
77
  - lib/generators/pg/aggregate/templates/aggregate.sql.erb
77
78
  - lib/generators/pg/aggregate/templates/migration.rb.erb
@@ -92,7 +93,6 @@ metadata:
92
93
  source_code_uri: https://github.com/mhenrixon/pg_aggregates
93
94
  changelog_uri: https://github.com/mhenrixon/pg_aggregates/blob/main/CHANGELOG.md
94
95
  rubygems_mfa_required: 'true'
95
- post_install_message:
96
96
  rdoc_options: []
97
97
  require_paths:
98
98
  - lib
@@ -107,8 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
107
  - !ruby/object:Gem::Version
108
108
  version: '0'
109
109
  requirements: []
110
- rubygems_version: 3.2.33
111
- signing_key:
110
+ rubygems_version: 3.6.7
112
111
  specification_version: 4
113
112
  summary: Rails integration for PostgreSQL aggregate functions
114
113
  test_files: []