Class: Tina4::Drivers::PostgresDriver
- Inherits:
-
Object
- Object
- Tina4::Drivers::PostgresDriver
- Includes:
- SchemaSplit
- Defined in:
- lib/tina4/drivers/postgres_driver.rb
Instance Attribute Summary collapse
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
Instance Method Summary collapse
- #apply_limit(sql, limit, offset = 0) ⇒ Object
- #begin_transaction ⇒ Object
- #close ⇒ Object
- #columns(table_name) ⇒ Object
- #commit ⇒ Object
- #connect(connection_string, username: nil, password: nil) ⇒ Object
- #execute(sql, params = []) ⇒ Object
- #execute_query(sql, params = []) ⇒ Object
- #last_insert_id ⇒ Object
- #placeholder ⇒ Object
- #placeholders(count) ⇒ Object
- #rollback ⇒ Object
-
#table_exists?(name) ⇒ Boolean
v3.13.14 (#48): to_regclass resolves a (possibly schema-qualified) relation name and search_path like a FROM clause; nil if absent.
- #tables ⇒ Object
Methods included from SchemaSplit
Instance Attribute Details
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
9 10 11 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 9 def connection @connection end |
Instance Method Details
#apply_limit(sql, limit, offset = 0) ⇒ Object
102 103 104 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 102 def apply_limit(sql, limit, offset = 0) "#{sql} LIMIT #{limit} OFFSET #{offset}" end |
#begin_transaction ⇒ Object
106 107 108 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 106 def begin_transaction @connection.exec("BEGIN") end |
#close ⇒ Object
30 31 32 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 30 def close @connection&.close end |
#columns(table_name) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 135 def columns(table_name) # v3.13.14 (#48): honour a schema-qualified name; default to public. schema, tbl = split_schema(table_name) schema ||= "public" sql = "SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = $1 AND table_schema = $2" rows = execute_query(sql, [tbl, schema]) 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 |
#commit ⇒ Object
110 111 112 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 110 def commit @connection.exec("COMMIT") end |
#connect(connection_string, username: nil, password: nil) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 11 def connect(connection_string, username: nil, password: nil) begin require "pg" rescue LoadError raise LoadError, "The 'pg' gem is required for PostgreSQL connections. Install one of:\n" \ " bundle add pg # if your project uses Bundler\n" \ " gem install pg # bare driver" end 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
44 45 46 47 48 49 50 51 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 44 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
34 35 36 37 38 39 40 41 42 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 34 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_id ⇒ Object
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 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 53 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 |
#placeholder ⇒ Object
94 95 96 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 94 def placeholder "?" end |
#placeholders(count) ⇒ Object
98 99 100 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 98 def placeholders(count) (1..count).map { |i| "$#{i}" }.join(", ") end |
#rollback ⇒ Object
114 115 116 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 114 def rollback @connection.exec("ROLLBACK") end |
#table_exists?(name) ⇒ Boolean
v3.13.14 (#48): to_regclass resolves a (possibly schema-qualified) relation name and search_path like a FROM clause; nil if absent.
120 121 122 123 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 120 def table_exists?(name) rows = execute_query("SELECT to_regclass($1) AS oid", [name.to_s]) !rows.empty? && !rows[0][:oid].nil? end |
#tables ⇒ Object
125 126 127 128 129 130 131 132 133 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 125 def tables # v3.13.14 (#48): list every user schema; public tables stay bare, # others are returned schema-qualified. sql = "SELECT schemaname, tablename FROM pg_tables " \ "WHERE schemaname NOT IN ('pg_catalog', 'information_schema') " \ "ORDER BY schemaname, tablename" rows = execute_query(sql) rows.map { |r| r[:schemaname] == "public" ? r[:tablename] : "#{r[:schemaname]}.#{r[:tablename]}" } end |