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
104 105 106 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 104 def apply_limit(sql, limit, offset = 0) "#{sql} LIMIT #{limit} OFFSET #{offset}" end |
#begin_transaction ⇒ Object
108 109 110 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 108 def begin_transaction @connection.exec("BEGIN") end |
#close ⇒ Object
32 33 34 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 32 def close @connection&.close end |
#columns(table_name) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 137 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
112 113 114 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 112 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 29 30 |
# 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) apply_result_type_map(@connection) @connection end |
#execute(sql, params = []) ⇒ Object
46 47 48 49 50 51 52 53 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 46 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
36 37 38 39 40 41 42 43 44 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 36 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
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 93 94 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 55 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
96 97 98 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 96 def placeholder "?" end |
#placeholders(count) ⇒ Object
100 101 102 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 100 def placeholders(count) (1..count).map { |i| "$#{i}" }.join(", ") end |
#rollback ⇒ Object
116 117 118 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 116 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.
122 123 124 125 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 122 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
127 128 129 130 131 132 133 134 135 |
# File 'lib/tina4/drivers/postgres_driver.rb', line 127 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 |