Class: PgMultitenantSchemas::SchemaSwitcher

Inherits:
Object
  • Object
show all
Defined in:
lib/pg_multitenant_schemas/schema_switcher.rb

Overview

Low-level PostgreSQL schema switching and management

The SchemaSwitcher class provides direct access to PostgreSQL schema operations. It handles both Rails and raw PG connections transparently.

Examples:

Switching schemas

PgMultitenantSchemas::SchemaSwitcher.switch_schema('tenant_123')

Creating a schema

PgMultitenantSchemas::SchemaSwitcher.create_schema('new_tenant')

Listing all schemas

PgMultitenantSchemas::SchemaSwitcher.list_schemas
#=> ["public", "tenant_123", "tenant_456"]

Class Method Summary collapse

Class Method Details

.connectionActiveRecord::ConnectionAdapters::AbstractAdapter, PG::Connection

Get the database connection from the configured connection class

Returns:

  • (ActiveRecord::ConnectionAdapters::AbstractAdapter, PG::Connection)

Raises:



34
35
36
37
38
39
40
41
42
43
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 34

def connection
  connection_class = PgMultitenantSchemas.configuration.connection_class
  if connection_class.is_a?(String)
    Object.const_get(connection_class).connection
  else
    connection_class.connection
  end
rescue StandardError => e
  raise ConnectionError, "Failed to get database connection: #{e.message}"
end

.create_schema(schema_name) ⇒ void

This method returns an undefined value.

Create a new PostgreSQL schema

Uses CREATE SCHEMA IF NOT EXISTS for idempotency.

Examples:

PgMultitenantSchemas::SchemaSwitcher.create_schema('tenant_123')

Parameters:

  • schema_name (String)

    The schema name to create

Raises:

  • (ArgumentError)

    if schema name is blank



81
82
83
84
85
86
87
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 81

def create_schema(schema_name)
  raise ArgumentError, "Schema name cannot be empty" if schema_name.nil? || schema_name.strip.empty?

  conn = connection
  quoted_schema = "\"#{schema_name.gsub('"', '""')}\""
  execute_sql(conn, "CREATE SCHEMA IF NOT EXISTS #{quoted_schema};")
end

.current_schemaString

Get the current PostgreSQL schema

Examples:

PgMultitenantSchemas::SchemaSwitcher.current_schema
#=> "tenant_123"

Returns:

  • (String)

    The current schema name



138
139
140
141
142
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 138

def current_schema
  conn = connection
  result = execute_sql(conn, "SELECT current_schema()")
  get_result_value(result, 0, 0)
end

.drop_schema(schema_name, cascade: true) ⇒ void

This method returns an undefined value.

Drop a PostgreSQL schema

Examples:

PgMultitenantSchemas::SchemaSwitcher.drop_schema('old_tenant')

With CASCADE option

PgMultitenantSchemas::SchemaSwitcher.drop_schema('old_tenant', cascade: true)

Parameters:

  • schema_name (String)

    The schema name to drop

  • cascade (Boolean) (defaults to: true)

    Whether to drop dependent objects (default: true)

Raises:

  • (ArgumentError)

    if schema name is blank



99
100
101
102
103
104
105
106
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 99

def drop_schema(schema_name, cascade: true)
  raise ArgumentError, "Schema name cannot be empty" if schema_name.nil? || schema_name.strip.empty?

  conn = connection
  cascade_option = cascade ? "CASCADE" : "RESTRICT"
  quoted_schema = "\"#{schema_name.gsub('"', '""')}\""
  execute_sql(conn, "DROP SCHEMA IF EXISTS #{quoted_schema} #{cascade_option};")
end

.initialize_connectionvoid

This method returns an undefined value.

Initialize connection management

Called by the Railtie when Rails starts. No special initialization needed.



25
26
27
28
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 25

def initialize_connection
  # This is called by the Railtie when Rails starts
  # No special initialization needed as we use the configured connection class
end

.list_schemasArray<String>

List all schemas in the database

Examples:

PgMultitenantSchemas::SchemaSwitcher.list_schemas
#=> ["public", "tenant_123", "tenant_456"]

Returns:

  • (Array<String>)

    Array of schema names



150
151
152
153
154
155
156
157
158
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 150

def list_schemas
  conn = connection
  result = execute_sql(conn, <<~SQL)
    SELECT schema_name FROM information_schema.schemata#{" "}
    ORDER BY schema_name
  SQL

  extract_schemas_from_result(result)
end

.reset_schemavoid

This method returns an undefined value.

Reset to the default PostgreSQL schema

Sets search_path back to 'public'.



67
68
69
70
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 67

def reset_schema
  conn = connection
  execute_sql(conn, "SET search_path TO public;")
end

.schema_exists?(schema_name) ⇒ Boolean

Check if a schema exists in the database

Examples:

if PgMultitenantSchemas::SchemaSwitcher.schema_exists?('tenant_123')
  # Schema exists
end

Parameters:

  • schema_name (String)

    The schema name to check

Returns:

  • (Boolean)

    true if the schema exists, false otherwise



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 116

def schema_exists?(schema_name)
  return false if schema_name.nil? || schema_name.strip.empty?

  conn = connection
  result = execute_sql(conn, <<~SQL)
    SELECT EXISTS(
      SELECT 1 FROM information_schema.schemata#{" "}
      WHERE schema_name = '#{schema_name}'
    ) AS schema_exists
  SQL

  value = get_result_value(result, 0, 0)
  # Handle both boolean values and string representations
  [true, "t", "true"].include?(value)
end

.switch_schema(schema_name) ⇒ void

This method returns an undefined value.

Switch the current PostgreSQL schema

Sets the search_path to the specified schema using SET search_path.

Examples:

PgMultitenantSchemas::SchemaSwitcher.switch_schema('tenant_123')

Parameters:

  • schema_name (String)

    The schema name to switch to

Raises:

  • (ArgumentError)

    if schema name is blank



54
55
56
57
58
59
60
# File 'lib/pg_multitenant_schemas/schema_switcher.rb', line 54

def switch_schema(schema_name)
  raise ArgumentError, "Schema name cannot be empty" if schema_name.nil? || schema_name.strip.empty?

  conn = connection
  quoted_schema = "\"#{schema_name.gsub('"', '""')}\""
  execute_sql(conn, "SET search_path TO #{quoted_schema};")
end