Module: ActiveRecord::PGExtensions::PostgreSQLAdapter
- Defined in:
- lib/active_record/pg_extensions/extension.rb,
lib/active_record/pg_extensions/postgresql_adapter.rb
Overview
Contains general additions to the PostgreSQLAdapter
Defined Under Namespace
Classes: Extension
Constant Summary collapse
- TIMEOUTS =
%i[lock_timeout statement_timeout idle_in_transaction_session_timeout].freeze
Instance Method Summary collapse
-
#add_schema_to_search_path(schema) ⇒ Object
temporarily adds schema to the search_path (i.e. so you can use an extension that won’t work without being on the search path, such as postgis).
- #alter_extension(extension, schema: nil, version: nil) ⇒ Object
- #create_extension(extension, if_not_exists: false, schema: nil, version: nil, cascade: false) ⇒ Object
- #current_wal_flush_lsn ⇒ Object
- #current_wal_insert_lsn ⇒ Object
- #current_wal_lsn ⇒ Object
-
#defer_constraints(*constraints) ⇒ Object
defers constraints, yields to the caller, and then resets back to immediate note that the reset back to immediate is not in an ensure block, since any error thrown would likely mean the transaction is rolled back, and setting constraint checking back to immediate would also fail.
- #drop_extension(*extensions, if_exists: false, cascade: false) ⇒ Object
-
#extension(extension) ⇒ Object
returns an Extension object for a particular extension.
-
#extension_available?(extension, version = nil) ⇒ Boolean
check if a particular extension can be installed.
- #in_recovery? ⇒ Boolean
- #last_wal_receive_lsn ⇒ Object
- #last_wal_replay_lsn ⇒ Object
- #reset(configuration_parameter) ⇒ Object
- #set(configuration_parameter, value, local: false) ⇒ Object
-
#set_constraints(deferred, *constraints) ⇒ Object
set constraint check timing for the current transaction see www.postgresql.org/docs/current/sql-set-constraints.html.
- #set_replica_identity(table, identity = :default) ⇒ Object
- #vacuum(*table_and_columns, full: false, freeze: false, verbose: false, analyze: false, disable_page_skipping: false, skip_locked: false, index_cleanup: false, truncate: false, parallel: nil) ⇒ Object
-
#wal? ⇒ Boolean
Amazon Aurora doesn’t have a WAL.
-
#wal_lsn_diff(lsn1 = :current, lsn2 = :last_replay) ⇒ Object
see www.postgresql.org/docs/current/functions-admin.html#id-1.5.8.33.5.5.2.2.4.1.1.1 lsns can be literals, or :current, :current_flush, :current_insert, :last_receive, or :last_replay.
-
#with_statement_timeout(timeout = nil) ⇒ Object
@deprecated: manage the transaction yourself and set statement_timeout directly.
Instance Method Details
#add_schema_to_search_path(schema) ⇒ Object
temporarily adds schema to the search_path (i.e. so you can use an extension that won’t work without being on the search path, such as postgis)
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 109 def add_schema_to_search_path(schema) if schema_search_path.split(",").include?(schema) yield else old_search_path = schema_search_path manual_rollback = false transaction(requires_new: true) do self.schema_search_path += ",#{schema}" yield self.schema_search_path = old_search_path rescue ActiveRecord::StatementInvalid, ActiveRecord::Rollback => e # the transaction rolling back will revert the search path change; # we don't need to do another query to set it @schema_search_path = old_search_path manual_rollback = e if e.is_a?(ActiveRecord::Rollback) raise end # the transaction call will swallow ActiveRecord::Rollback, # but we want it this method to be transparent raise manual_rollback if manual_rollback end end |
#alter_extension(extension, schema: nil, version: nil) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 55 def alter_extension(extension, schema: nil, version: nil) if schema && version raise ArgumentError, "Cannot change schema and upgrade to a particular version in a single statement" end sql = +"ALTER EXTENSION #{extension}" sql << " UPDATE" if version sql << " TO #{quote(version)}" if version && version != true sql << " SET SCHEMA #{schema}" if schema execute(sql) reload_type_map @extensions&.delete(extension.to_s) end |
#create_extension(extension, if_not_exists: false, schema: nil, version: nil, cascade: false) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 42 def create_extension(extension, if_not_exists: false, schema: nil, version: nil, cascade: false) sql = +"CREATE EXTENSION " sql <<= "IF NOT EXISTS " if if_not_exists sql << extension.to_s sql << " SCHEMA #{schema}" if schema sql << " VERSION #{quote(version)}" if version sql << " CASCADE" if cascade execute(sql) reload_type_map @extensions&.delete(extension.to_s) end |
#current_wal_flush_lsn ⇒ Object
189 190 191 192 193 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 189 def current_wal_flush_lsn return nil unless wal? select_value("SELECT #{pre_pg10_wal_function_name("pg_current_wal_flush_lsn")}()") end |
#current_wal_insert_lsn ⇒ Object
196 197 198 199 200 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 196 def current_wal_insert_lsn return nil unless wal? select_value("SELECT #{pre_pg10_wal_function_name("pg_current_wal_insert_lsn")}()") end |
#current_wal_lsn ⇒ Object
182 183 184 185 186 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 182 def current_wal_lsn return nil unless wal? select_value("SELECT #{pre_pg10_wal_function_name("pg_current_wal_lsn")}()") end |
#defer_constraints(*constraints) ⇒ Object
defers constraints, yields to the caller, and then resets back to immediate note that the reset back to immediate is not in an ensure block, since any error thrown would likely mean the transaction is rolled back, and setting constraint checking back to immediate would also fail
24 25 26 27 28 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 24 def defer_constraints(*constraints) set_constraints(:deferred, *constraints) yield set_constraints(:immediate, *constraints) end |
#drop_extension(*extensions, if_exists: false, cascade: false) ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 70 def drop_extension(*extensions, if_exists: false, cascade: false) raise ArgumentError, "wrong number of arguments (given 0, expected 1+)" if extensions.empty? sql = +"DROP EXTENSION " sql << "IF EXISTS " if if_exists sql << extensions.join(", ") sql << " CASCADE" if cascade execute(sql) reload_type_map @extensions&.except!(*extensions.map(&:to_s)) end |
#extension(extension) ⇒ Object
returns an Extension object for a particular extension
92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 92 def extension(extension) @extensions ||= {} @extensions.fetch(extension.to_s) do rows = select_rows(<<~SQL, "SCHEMA") SELECT nspname, extversion FROM pg_extension INNER JOIN pg_namespace ON extnamespace=pg_namespace.oid WHERE extname=#{quote(extension)} SQL next nil if rows.empty? Extension.new(extension.to_s, rows[0][0], rows[0][1]) end end |
#extension_available?(extension, version = nil) ⇒ Boolean
check if a particular extension can be installed
83 84 85 86 87 88 89 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 83 def extension_available?(extension, version = nil) sql = +"SELECT 1 FROM " sql << (version ? "pg_available_extension_versions" : "pg_available_extensions") sql << " WHERE name=#{quote(extension)}" sql << " AND version=#{quote(version)}" if version select_value(sql).to_i == 1 end |
#in_recovery? ⇒ Boolean
235 236 237 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 235 def in_recovery? select_value("SELECT pg_is_in_recovery()") end |
#last_wal_receive_lsn ⇒ Object
203 204 205 206 207 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 203 def last_wal_receive_lsn return nil unless wal? select_value("SELECT #{pre_pg10_wal_function_name("pg_last_wal_receive_lsn")}()") end |
#last_wal_replay_lsn ⇒ Object
210 211 212 213 214 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 210 def last_wal_replay_lsn return nil unless wal? select_value("SELECT #{pre_pg10_wal_function_name("pg_last_wal_replay_lsn")}()") end |
#reset(configuration_parameter) ⇒ Object
244 245 246 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 244 def reset(configuration_parameter) execute("RESET #{configuration_parameter}") end |
#set(configuration_parameter, value, local: false) ⇒ Object
239 240 241 242 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 239 def set(configuration_parameter, value, local: false) value = value.nil? ? "DEFAULT" : quote(value) execute("SET#{" LOCAL" if local} #{configuration_parameter} TO #{value}") end |
#set_constraints(deferred, *constraints) ⇒ Object
set constraint check timing for the current transaction see www.postgresql.org/docs/current/sql-set-constraints.html
11 12 13 14 15 16 17 18 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 11 def set_constraints(deferred, *constraints) raise ArgumentError, "deferred must be :deferred or :immediate" unless %i[deferred immediate].include?(deferred.to_sym) constraints = constraints.map { |c| quote_table_name(c) }.join(", ") constraints = "ALL" if constraints.empty? execute("SET CONSTRAINTS #{constraints} #{deferred.to_s.upcase}") end |
#set_replica_identity(table, identity = :default) ⇒ Object
31 32 33 34 35 36 37 38 39 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 31 def set_replica_identity(table, identity = :default) identity_clause = case identity when :default, :full, :nothing identity.to_s.upcase else "USING INDEX #{quote_column_name(identity)}" end execute("ALTER TABLE #{quote_table_name(table)} REPLICA IDENTITY #{identity_clause}") end |
#vacuum(*table_and_columns, full: false, freeze: false, verbose: false, analyze: false, disable_page_skipping: false, skip_locked: false, index_cleanup: false, truncate: false, parallel: nil) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 133 def vacuum(*table_and_columns, full: false, freeze: false, verbose: false, analyze: false, disable_page_skipping: false, skip_locked: false, index_cleanup: false, truncate: false, parallel: nil) if parallel && !(parallel.is_a?(Integer) && parallel.positive?) raise ArgumentError, "parallel must be a positive integer" end sql = +"VACUUM" sql << " FULL" if full sql << " FREEZE" if freeze sql << " VERBOSE" if verbose sql << " ANALYZE" if analyze sql << " DISABLE_PAGE_SKIPPING" if disable_page_skipping sql << " SKIP_LOCKED" if skip_locked sql << " INDEX_CLEANUP" if index_cleanup sql << " TRUNCATE" if truncate sql << " PARALLEL #{parallel}" if parallel sql << " " unless table_and_columns.empty? sql << table_and_columns.map do |table| if table.is_a?(Hash) raise ArgumentError, "columns may only be specified if a analyze is specified" unless analyze table.map do |table_name, columns| "#{quote_table_name(table_name)} (#{Array.wrap(columns).map { |c| quote_column_name(c) }.join(", ")})" end.join(", ") else quote_table_name(table) end end.join(", ") execute(sql) end |
#wal? ⇒ Boolean
Amazon Aurora doesn’t have a WAL
173 174 175 176 177 178 179 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 173 def wal? unless instance_variable_defined?(:@has_wal) function_name = pre_pg10_wal_function_name("pg_current_wal_lsn") @has_wal = select_value("SELECT true FROM pg_proc WHERE proname='#{function_name}' LIMIT 1") end @has_wal end |
#wal_lsn_diff(lsn1 = :current, lsn2 = :last_replay) ⇒ Object
see www.postgresql.org/docs/current/functions-admin.html#id-1.5.8.33.5.5.2.2.4.1.1.1 lsns can be literals, or :current, :current_flush, :current_insert, :last_receive, or :last_replay
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 218 def wal_lsn_diff(lsn1 = :current, lsn2 = :last_replay) return nil unless wal? lsns = [lsn1, lsn2].map do |lsn| case lsn when :current then pre_pg10_wal_function_name("pg_current_wal_lsn()") when :current_flush then pre_pg10_wal_function_name("pg_current_flush_wal_lsn()") when :current_insert then pre_pg10_wal_function_name("pg_current_insert_wal_lsn()") when :last_receive then pre_pg10_wal_function_name("pg_last_wal_receive_lsn()") when :last_replay then pre_pg10_wal_function_name("pg_last_wal_replay_lsn()") else; quote(lsn) end end select_value("SELECT #{pre_pg10_wal_function_name("pg_wal_lsn_diff")}(#{lsns[0]}, #{lsns[1]})") end |
#with_statement_timeout(timeout = nil) ⇒ Object
@deprecated: manage the transaction yourself and set statement_timeout directly
otherwise, if you’re already in a transaction, or you nest with_statement_timeout, the value will unexpectedly “stick” even after the block returns
268 269 270 271 272 273 274 275 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 268 def with_statement_timeout(timeout = nil) timeout = 30 if timeout.nil? || timeout == true transaction do self.statement_timeout = timeout yield end end |