Class: Tina4::Drivers::PostgresDriver

Inherits:
Object
  • Object
show all
Defined in:
lib/tina4/drivers/postgres_driver.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#connectionObject (readonly)

Returns the value of attribute connection.



6
7
8
# File 'lib/tina4/drivers/postgres_driver.rb', line 6

def connection
  @connection
end

Instance Method Details

#apply_limit(sql, limit, offset = 0) ⇒ Object



92
93
94
# File 'lib/tina4/drivers/postgres_driver.rb', line 92

def apply_limit(sql, limit, offset = 0)
  "#{sql} LIMIT #{limit} OFFSET #{offset}"
end

#begin_transactionObject



96
97
98
# File 'lib/tina4/drivers/postgres_driver.rb', line 96

def begin_transaction
  @connection.exec("BEGIN")
end

#closeObject



20
21
22
# File 'lib/tina4/drivers/postgres_driver.rb', line 20

def close
  @connection&.close
end

#columns(table_name) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/tina4/drivers/postgres_driver.rb', line 114

def columns(table_name)
  sql = "SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = $1"
  rows = execute_query(sql, [table_name])
  rows.map do |r|
    {
      name: r[:column_name],
      type: r[:data_type],
      nullable: r[:is_nullable] == "YES",
      default: r[:column_default],
      primary_key: false
    }
  end
end

#commitObject



100
101
102
# File 'lib/tina4/drivers/postgres_driver.rb', line 100

def commit
  @connection.exec("COMMIT")
end

#connect(connection_string, username: nil, password: nil) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
# File 'lib/tina4/drivers/postgres_driver.rb', line 8

def connect(connection_string, username: nil, password: nil)
  require "pg"
  url = connection_string
  if username || password
    uri = URI.parse(url)
    uri.user = username if username
    uri.password = password if password
    url = uri.to_s
  end
  @connection = PG.connect(url)
end

#execute(sql, params = []) ⇒ Object



34
35
36
37
38
39
40
41
# File 'lib/tina4/drivers/postgres_driver.rb', line 34

def execute(sql, params = [])
  converted_sql = convert_placeholders(sql)
  if params.empty?
    @connection.exec(converted_sql)
  else
    @connection.exec_params(converted_sql, params)
  end
end

#execute_query(sql, params = []) ⇒ Object



24
25
26
27
28
29
30
31
32
# File 'lib/tina4/drivers/postgres_driver.rb', line 24

def execute_query(sql, params = [])
  converted_sql = convert_placeholders(sql)
  result = if params.empty?
             @connection.exec(converted_sql)
           else
             @connection.exec_params(converted_sql, params)
           end
  result.map { |row| decode_blobs(symbolize_keys(row)) }
end

#last_insert_idObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/tina4/drivers/postgres_driver.rb', line 43

def last_insert_id
  # Issue #38: ``SELECT lastval()`` raises on tables with no sequence
  # (UUID, ULID, hash PKs etc.). The exception itself isn't fatal,
  # but the pg gem marks the whole transaction as aborted, so every
  # subsequent statement on this connection fails with
  # ``PG::InFailedSqlTransaction`` — far away from the real cause.
  #
  # Fix: wrap the probe in a SAVEPOINT. If ``lastval()`` raises, we
  # ROLLBACK TO SAVEPOINT and the outer transaction stays usable;
  # ``last_insert_id`` just returns ``nil`` (same as before for
  # tables without a sequence). On success we RELEASE SAVEPOINT.
  begin
    @connection.exec("SAVEPOINT _t4_lastval_probe")
  rescue PG::Error
    # No active transaction (autocommit/idle) — fall back to a plain
    # probe; psycopg2-style transaction abort can't happen here.
    begin
      result = @connection.exec("SELECT lastval()")
      return result.first["lastval"].to_i
    rescue PG::Error
      return nil
    end
  end

  begin
    result = @connection.exec("SELECT lastval()")
    @connection.exec("RELEASE SAVEPOINT _t4_lastval_probe")
    result.first["lastval"].to_i
  rescue PG::Error
    begin
      @connection.exec("ROLLBACK TO SAVEPOINT _t4_lastval_probe")
      @connection.exec("RELEASE SAVEPOINT _t4_lastval_probe")
    rescue PG::Error
      # If even the rollback fails, there's nothing we can do — the
      # connection is in a state we can't recover. Surface nil so
      # callers don't get a half-set last_id.
    end
    nil
  end
end

#placeholderObject



84
85
86
# File 'lib/tina4/drivers/postgres_driver.rb', line 84

def placeholder
  "?"
end

#placeholders(count) ⇒ Object



88
89
90
# File 'lib/tina4/drivers/postgres_driver.rb', line 88

def placeholders(count)
  (1..count).map { |i| "$#{i}" }.join(", ")
end

#rollbackObject



104
105
106
# File 'lib/tina4/drivers/postgres_driver.rb', line 104

def rollback
  @connection.exec("ROLLBACK")
end

#tablesObject



108
109
110
111
112
# File 'lib/tina4/drivers/postgres_driver.rb', line 108

def tables
  sql = "SELECT tablename FROM pg_tables WHERE schemaname = 'public'"
  rows = execute_query(sql)
  rows.map { |r| r[:tablename] }
end