Module: Ec::Pg::SchemaManager
- Defined in:
- lib/ec/pg/schema_manager.rb
Overview
Manages PostgreSQL schema switching via the search_path session variable.
When you have one Postgres database with a schema-per-tenant layout:
CREATE SCHEMA tenant_abc;
CREATE TABLE tenant_abc.records (...);
This manager updates the connection’s search_path so that ActiveRecord’s unqualified table references resolve to the correct tenant schema.
Usage
SchemaManager.with_schema("tenant_abc") { Record.all }
The search_path is reset to the previous value (or the configured default) when the block exits, even if an exception is raised.
Thread safety
with_schema stores the schema name in the thread context AND updates the underlying database connection. When using connection pooling, each thread checks out its own connection, so search_path changes are thread-safe.
Defined Under Namespace
Classes: InvalidSchema
Class Method Summary collapse
-
.active? ⇒ Boolean
Returns true if a non-default schema is active.
-
.apply!(schema_name) ⇒ Object
Applies the search_path on an existing connection without block scoping.
-
.apply_schema(schema_name) ⇒ Object
Builds the search_path string and executes SET search_path on the connection.
-
.current_schema ⇒ Object
Returns the schema currently active in thread context.
- .registered_connections ⇒ Object
-
.reset! ⇒ Object
Resets search_path to the configured default schema on
connection. -
.restore_schema ⇒ Object
Restores the search_path to the configured default schema(s).
- .validate_schema_name!(name) ⇒ Object
-
.with_schema(schema_name) { ... } ⇒ Object
Executes
blockwith the Postgres search_path set toschema_name, followed by anyshared_schemasfrom the configuration.
Class Method Details
.active? ⇒ Boolean
Returns true if a non-default schema is active.
60 61 62 |
# File 'lib/ec/pg/schema_manager.rb', line 60 def active? !Context.schema.nil? end |
.apply!(schema_name) ⇒ Object
Applies the search_path on an existing connection without block scoping. Useful in middleware or Rack apps that manage connection lifecycle manually.
66 67 68 69 70 71 72 |
# File 'lib/ec/pg/schema_manager.rb', line 66 def apply!(schema_name) schema_name = schema_name.to_s.strip validate_schema_name!(schema_name) Context.set(schema: schema_name) apply_schema(schema_name) end |
.apply_schema(schema_name) ⇒ Object
Builds the search_path string and executes SET search_path on the connection.
83 84 85 86 87 88 89 |
# File 'lib/ec/pg/schema_manager.rb', line 83 def apply_schema(schema_name) registered_connections.each do |connection| shared = Ec::Pg.configuration.shared_schemas path = ([schema_name] + shared).uniq.join(", ") connection.schema_search_path = path end end |
.current_schema ⇒ Object
Returns the schema currently active in thread context.
55 56 57 |
# File 'lib/ec/pg/schema_manager.rb', line 55 def current_schema Context.schema || Ec::Pg.configuration.default_schema end |
.registered_connections ⇒ Object
106 107 108 109 110 |
# File 'lib/ec/pg/schema_manager.rb', line 106 def registered_connections Ec::Pg::SchemaMixin .registered_models .map(&:connection) end |
.reset! ⇒ Object
Resets search_path to the configured default schema on connection.
75 76 77 78 |
# File 'lib/ec/pg/schema_manager.rb', line 75 def reset! Context.delete(:schema) restore_schema end |
.restore_schema ⇒ Object
Restores the search_path to the configured default schema(s).
92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/ec/pg/schema_manager.rb', line 92 def restore_schema default = Ec::Pg.configuration.default_schema shared = Ec::Pg.configuration.shared_schemas path = ([default] + shared).uniq.join(", ") registered_connections.each do |connection| if connection && connection.pool.present? connection.schema_search_path = path end end rescue StandardError # Swallow errors during cleanup (connection may have been returned to pool) end |
.validate_schema_name!(name) ⇒ Object
115 116 117 118 119 120 121 |
# File 'lib/ec/pg/schema_manager.rb', line 115 def validate_schema_name!(name) return if ValidSchemaRegex.match?(name) raise InvalidSchema, "Invalid PostgreSQL schema name: #{name.inspect}. " \ "Only alphanumeric characters and underscores are allowed." end |
.with_schema(schema_name) { ... } ⇒ Object
Executes block with the Postgres search_path set to schema_name, followed by any shared_schemas from the configuration.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/ec/pg/schema_manager.rb', line 39 def with_schema(schema_name, &block) schema_name = schema_name.to_s.strip validate_schema_name!(schema_name) Context.with(schema: schema_name) do apply_schema(schema_name) block.call end ensure # Restore previous schema on the same connection if we changed it. # Guard against the case where the connection was already returned to # the pool during the block. restore_schema end |