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
-
#rename_constraint(table_name, old_name, new_name, if_exists: false) ⇒ Object
renames a constraint on a table see https://www.postgresql.org/docs/current/sql-altertable.html.
- #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 https://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 https://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)
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 120 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 result = nil transaction(requires_new: true) do self.schema_search_path += ",#{schema}" result = 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 result end end |
#alter_extension(extension, schema: nil, version: nil) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 66 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
53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 53 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
203 204 205 206 207 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 203 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
210 211 212 213 214 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 210 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
196 197 198 199 200 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 196 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
81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 81 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
103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 103 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
94 95 96 97 98 99 100 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 94 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
249 250 251 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 249 def in_recovery? select_value("SELECT pg_is_in_recovery()") end |
#last_wal_receive_lsn ⇒ Object
217 218 219 220 221 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 217 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
224 225 226 227 228 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 224 def last_wal_replay_lsn return nil unless wal? select_value("SELECT #{pre_pg10_wal_function_name("pg_last_wal_replay_lsn")}()") end |
#rename_constraint(table_name, old_name, new_name, if_exists: false) ⇒ Object
renames a constraint on a table see https://www.postgresql.org/docs/current/sql-altertable.html
32 33 34 35 36 37 38 39 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 32 def rename_constraint(table_name, old_name, new_name, if_exists: false) return if if_exists && !constraint_exists?(table_name, name: old_name) execute(<<~SQL.squish) ALTER TABLE #{quote_table_name(table_name)} RENAME CONSTRAINT #{quote_column_name(old_name)} TO #{quote_column_name(new_name)} SQL end |
#reset(configuration_parameter) ⇒ Object
258 259 260 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 258 def reset(configuration_parameter) execute("RESET #{configuration_parameter}") end |
#set(configuration_parameter, value, local: false) ⇒ Object
253 254 255 256 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 253 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 https://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
42 43 44 45 46 47 48 49 50 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 42 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
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 147 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
187 188 189 190 191 192 193 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 187 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 https://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
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 232 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
282 283 284 285 286 287 288 289 |
# File 'lib/active_record/pg_extensions/postgresql_adapter.rb', line 282 def with_statement_timeout(timeout = nil) timeout = 30 if timeout.nil? || timeout == true transaction do self.statement_timeout = timeout yield end end |