Class: Tina4::Drivers::FirebirdDriver
- Inherits:
-
Object
- Object
- Tina4::Drivers::FirebirdDriver
- Defined in:
- lib/tina4/drivers/firebird_driver.rb
Constant Summary collapse
- DEAD_CONN_MARKERS =
Substring markers (lowercased) that identify a dead-socket Firebird error worth reconnecting for. Idle Firebird connections die silently behind NAT timeouts, server-side ConnectionIdleTimeout, or Docker network rotation; without this the next prepare crashes the request.
[ "error writing data to the connection", "error reading data from the connection", "connection shutdown", "connection lost", "network error", "connection is not active", "broken pipe" ].freeze
Instance Attribute Summary collapse
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
Class Method Summary collapse
-
.dead_connection?(error_or_message) ⇒ Boolean
Public so specs (and curious operators) can verify the matcher behaviour without poking private methods.
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
- #tables ⇒ Object
Instance Attribute Details
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
6 7 8 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 6 def connection @connection end |
Class Method Details
.dead_connection?(error_or_message) ⇒ Boolean
Public so specs (and curious operators) can verify the matcher behaviour without poking private methods.
76 77 78 79 80 81 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 76 def self.dead_connection?() msg = .respond_to?(:message) ? . : .to_s return false if msg.nil? || msg.empty? lower = msg.downcase DEAD_CONN_MARKERS.any? { |m| lower.include?(m) } end |
Instance Method Details
#apply_limit(sql, limit, offset = 0) ⇒ Object
95 96 97 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 95 def apply_limit(sql, limit, offset = 0) "SELECT FIRST #{limit} SKIP #{offset} * FROM (#{sql})" end |
#begin_transaction ⇒ Object
99 100 101 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 99 def begin_transaction @transaction = @connection.transaction end |
#close ⇒ Object
49 50 51 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 49 def close @connection&.close end |
#columns(table_name) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 117 def columns(table_name) sql = "SELECT RF.RDB\$FIELD_NAME, F.RDB\$FIELD_TYPE, RF.RDB\$NULL_FLAG, RF.RDB\$DEFAULT_SOURCE " \ "FROM RDB\$RELATION_FIELDS RF " \ "JOIN RDB\$FIELDS F ON RF.RDB\$FIELD_SOURCE = F.RDB\$FIELD_NAME " \ "WHERE RF.RDB\$RELATION_NAME = ?" rows = execute_query(sql, [table_name.upcase]) rows.map do |r| { name: (r["RDB\$FIELD_NAME"] || r["rdb\$field_name"] || "").strip, type: r["RDB\$FIELD_TYPE"] || r["rdb\$field_type"], nullable: (r["RDB\$NULL_FLAG"] || r["rdb\$null_flag"]).nil?, default: r["RDB\$DEFAULT_SOURCE"] || r["rdb\$default_source"], primary_key: false } end end |
#commit ⇒ Object
103 104 105 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 103 def commit @transaction&.commit end |
#connect(connection_string, username: nil, password: nil) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 22 def connect(connection_string, username: nil, password: nil) require "fb" require "uri" uri = URI.parse(connection_string) host = uri.host port = uri.port || 3050 db_path = uri.path&.sub(/^\//, "") db_user = username || uri.user db_pass = password || uri.password database = if host "#{host}/#{port}:#{db_path}" else db_path || connection_string.sub(/^firebird:\/\//, "") end # Cache for transparent reconnect — never logged, lives only in # driver memory alongside the connection it owns. @connect_opts = { database: database } @connect_opts[:username] = db_user if db_user @connect_opts[:password] = db_pass if db_pass open_connection rescue LoadError raise "Firebird driver requires the 'fb' gem. Install it with: gem install fb" end |
#execute(sql, params = []) ⇒ Object
64 65 66 67 68 69 70 71 72 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 64 def execute(sql, params = []) with_reconnect do if params.empty? @connection.execute(sql) else @connection.execute(sql, *params) end end end |
#execute_query(sql, params = []) ⇒ Object
53 54 55 56 57 58 59 60 61 62 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 53 def execute_query(sql, params = []) rows = with_reconnect do if params.empty? @connection.query(:hash, sql) else @connection.query(:hash, sql, *params) end end rows.map { |row| decode_blobs(stringify_keys(row)) } end |
#last_insert_id ⇒ Object
83 84 85 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 83 def last_insert_id nil end |
#placeholder ⇒ Object
87 88 89 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 87 def placeholder "?" end |
#placeholders(count) ⇒ Object
91 92 93 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 91 def placeholders(count) (["?"] * count).join(", ") end |
#rollback ⇒ Object
107 108 109 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 107 def rollback @transaction&.rollback end |
#tables ⇒ Object
111 112 113 114 115 |
# File 'lib/tina4/drivers/firebird_driver.rb', line 111 def tables sql = "SELECT RDB\$RELATION_NAME FROM RDB\$RELATIONS WHERE RDB\$SYSTEM_FLAG = 0 AND RDB\$VIEW_BLR IS NULL" rows = execute_query(sql) rows.map { |r| (r["RDB\$RELATION_NAME"] || r["rdb\$relation_name"] || "").strip } end |