Module: Arfi::PostgreSQL::DatabaseStatementsPatch
- Defined in:
- lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rb,
sig/lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rbs
Overview
ActiveRecord adapter patch for PostgreSQL.
When a query fails with PG::UndefinedFunction, ARFI checks whether the missing function
is managed by ARFI (file exists under db/functions). If yes, ARFI loads function SQL files
via SqlFunctionLoader.load! and retries the failed query once.
Constant Summary collapse
- ARFI_UNDEFINED_FUNCTION =
/ function\s+([a-zA-Z0-9_."]+)\s*\(.*?\)\s+does\s+not\s+exist /ix.freeze
- THREAD_GUARD_KEY =
:arfi_reloading_functions
Instance Method Summary collapse
-
#arfi_extract_function_ident(message) ⇒ Array<nil>, Object
Extract the schema and function name from a PG::UndefinedFunction error message.
-
#arfi_has_function_file_for?(schema, fn) ⇒ Boolean, Object
Check whether a function file exists under db/functions for the given schema and function name.
-
#arfi_try_reload_and_retry?(e) ⇒ Boolean
Check if the error is caused by a missing ARFI-managed function and attempt to reload it.
-
#exec_query(*args, **kwargs) ⇒ Object
Rails 6/7: SELECT often goes through exec_query.
-
#execute(*args, **kwargs) ⇒ Object
DDL often goes through execute.
-
#internal_exec_query(*args, **kwargs) ⇒ Object
Execute an internal query, reloading missing ARFI-managed functions and retrying on PG::UndefinedFunction.
-
#raw_execute(*args, **kwargs) ⇒ Object
Rails 7+/8 may use raw_execute.
Instance Method Details
#arfi_extract_function_ident(message) ⇒ Array<nil>, Object
Extract the schema and function name from a PG::UndefinedFunction error message.
120 121 122 123 124 125 126 127 |
# File 'lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rb', line 120 def arfi_extract_function_ident() m = .to_s.match(ARFI_UNDEFINED_FUNCTION) return [nil, nil] unless m ident = m[1].to_s.delete('"') parts = ident.split(".", 2) parts.length == 2 ? [parts[0], parts[1]] : [nil, parts[0]] end |
#arfi_has_function_file_for?(schema, fn) ⇒ Boolean, Object
Check whether a function file exists under db/functions for the given schema and function name.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rb', line 135 def arfi_has_function_file_for?(schema, fn) return false if fn.nil? || fn.empty? root = Rails.root.join("db", "functions") return false unless root.directory? candidates = [] candidates << root.join("public", "#{fn}.sql") candidates << root.join("#{fn}.sql") # legacy generic pg_root = root.join("postgresql") if pg_root.directory? candidates << pg_root.join("public", "#{fn}.sql") candidates << pg_root.join("#{fn}.sql") # legacy adapter if schema && !schema.empty? candidates << pg_root.join(schema, "#{fn}.sql") else candidates.concat Dir.glob(pg_root.join("*", "#{fn}.sql").to_s) end end candidates.any? { |p| File.exist?(p) } end |
#arfi_try_reload_and_retry?(e) ⇒ Boolean
Check if the error is caused by a missing ARFI-managed function and attempt to reload it.
Uses a thread guard to prevent recursive retries.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rb', line 92 def arfi_try_reload_and_retry?(e) pg_error = e.cause || e return false unless pg_error.class.name == "PG::UndefinedFunction" return false if Thread.current[THREAD_GUARD_KEY] schema, fn = arfi_extract_function_ident(pg_error.) return false unless arfi_has_function_file_for?(schema, fn) Thread.current[THREAD_GUARD_KEY] = true begin Arfi::SqlFunctionLoader.load!( task_name: "arfi:runtime", connection: self, # same connection clear_active_connections: false, # runtime retry path verbose: false ) ensure Thread.current[THREAD_GUARD_KEY] = false end true end |
#exec_query(*args, **kwargs) ⇒ Object
Rails 6/7: SELECT often goes through exec_query
34 35 36 37 38 39 |
# File 'lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rb', line 34 def exec_query(*args, **kwargs) super rescue StandardError => e raise unless arfi_try_reload_and_retry?(e) retry end |
#execute(*args, **kwargs) ⇒ Object
DDL often goes through execute
48 49 50 51 52 53 |
# File 'lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rb', line 48 def execute(*args, **kwargs) super rescue StandardError => e raise unless arfi_try_reload_and_retry?(e) retry end |
#internal_exec_query(*args, **kwargs) ⇒ Object
Execute an internal query, reloading missing ARFI-managed functions and retrying on PG::UndefinedFunction.
76 77 78 79 80 81 |
# File 'lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rb', line 76 def internal_exec_query(*args, **kwargs) super rescue StandardError => e raise unless arfi_try_reload_and_retry?(e) retry end |
#raw_execute(*args, **kwargs) ⇒ Object
Rails 7+/8 may use raw_execute
62 63 64 65 66 67 |
# File 'lib/arfi/extensions/active_record/connection_adapters/postgresql/database_statements.rb', line 62 def raw_execute(*args, **kwargs) super rescue StandardError => e raise unless arfi_try_reload_and_retry?(e) retry end |