Class: ActiveRecord::ConnectionAdapters::FirebirdAdapter
- Inherits:
-
AbstractAdapter
- Object
- AbstractAdapter
- ActiveRecord::ConnectionAdapters::FirebirdAdapter
- Defined in:
- lib/active_record/connection_adapters/firebird_adapter.rb
Overview
The Firebird adapter relies on the FireRuby extension, version 0.4.0 or later (available as a gem or from RubyForge). FireRuby works with Firebird 1.5.x on Linux, OS X and Win32 platforms.
Usage Notes
Sequence (Generator) Names
The Firebird adapter supports the same approach adopted for the Oracle adapter. See ActiveRecord::Base#set_sequence_name for more details.
Note that in general there is no need to create a BEFORE INSERT
trigger corresponding to a Firebird sequence generator when using ActiveRecord. In other words, you don’t have to try to make Firebird simulate an AUTO_INCREMENT
or IDENTITY
column. When saving a new record, ActiveRecord pre-fetches the next sequence value for the table and explicitly includes it in the INSERT
statement. (Pre-fetching the next primary key value is the only reliable method for the Firebird adapter to report back the id
after a successful insert.)
BOOLEAN Domain
Firebird 1.5 does not provide a native BOOLEAN
type. But you can easily define a BOOLEAN
domain for this purpose, e.g.:
CREATE DOMAIN D_BOOLEAN AS SMALLINT CHECK (VALUE IN (0, 1));
When the Firebird adapter encounters a column that is based on a domain that includes “BOOLEAN” in the domain name, it will attempt to treat the column as a BOOLEAN
.
By default, the Firebird adapter will assume that the BOOLEAN domain is defined as above. This can be modified if needed. For example, if you have a legacy schema with the following BOOLEAN
domain defined:
CREATE DOMAIN BOOLEAN AS CHAR(1) CHECK (VALUE IN ('T', 'F'));
…you can add the following line to your environment.rb
file:
ActiveRecord::ConnectionAdapters::FirebirdAdapter.boolean_domain = { :true => 'T', :false => 'F' }
BLOB Elements
The Firebird adapter currently provides only limited support for BLOB
columns. You cannot currently retrieve or insert a BLOB
as an IO stream. When selecting a BLOB
, the entire element is converted into a String. When inserting or updating a BLOB
, the entire value is included in-line in the SQL statement, limiting you to values <= 32KB in size.
Column Name Case Semantics
Firebird and ActiveRecord have somewhat conflicting case semantics for column names.
- Firebird
-
The standard practice is to use unquoted column names, which can be thought of as case-insensitive. (In fact, Firebird converts them to uppercase.) Quoted column names (not typically used) are case-sensitive.
- ActiveRecord
-
Attribute accessors corresponding to column names are case-sensitive. The defaults for primary key and inheritance columns are lowercase, and in general, people use lowercase attribute names.
In order to map between the differing semantics in a way that conforms to common usage for both Firebird and ActiveRecord, uppercase column names in Firebird are converted to lowercase attribute names in ActiveRecord, and vice-versa. Mixed-case column names retain their case in both directions. Lowercase (quoted) Firebird column names are not supported. This is similar to the solutions adopted by other adapters.
In general, the best approach is to use unqouted (case-insensitive) column names in your Firebird DDL (or if you must quote, use uppercase column names). These will correspond to lowercase attributes in ActiveRecord.
For example, a Firebird table based on the following DDL:
CREATE TABLE products (
id BIGINT NOT NULL PRIMARY KEY,
"TYPE" VARCHAR(50),
name VARCHAR(255) );
…will correspond to an ActiveRecord model class called Product
with the following attributes: id
, type
, name
.
Quoting "TYPE"
and other Firebird reserved words:
In ActiveRecord, the default inheritance column name is type
. The word type is a Firebird reserved word, so it must be quoted in any Firebird SQL statements. Because of the case mapping described above, you should always reference this column using quoted-uppercase syntax ("TYPE"
) within Firebird DDL or other SQL statements (as in the example above). This holds true for any other Firebird reserved words used as column names as well.
Migrations
The Firebird adapter does not currently support Migrations. I hope to add this feature in the near future.
Connection Options
The following options are supported by the Firebird adapter. None of the options have default values.
:database
-
Required option. Specifies one of: (i) a Firebird database alias; (ii) the full path of a database file; or (iii) a full Firebird connection string. Do not specify
:host
,:service
or:port
as separate options when using a full connection string. :host
-
Set to
"remote.host.name"
for remote database connections. May be omitted for local connections if a full database path is specified for:database
. Some platforms require a value of"localhost"
for local connections when using a Firebird database alias. :service
-
Specifies a service name for the connection. Only used if
:host
is provided. Required when connecting to a non-standard service. :port
-
Specifies the connection port. Only used if
:host
is provided and:service
is not. Required when connecting to a non-standard port and:service
is not defined. :username
-
Specifies the database user. May be omitted or set to
nil
(together with:password
) to use the underlying operating system user credentials on supported platforms. :password
-
Specifies the database password. Must be provided if
:username
is explicitly specified; should be omitted if OS user credentials are are being used. :charset
-
Specifies the character set to be used by the connection. Refer to Firebird documentation for valid options.
Constant Summary collapse
- @@boolean_domain =
{ :true => 1, :false => 0 }
Instance Method Summary collapse
-
#active? ⇒ Boolean
CONNECTION MANAGEMENT ====================================.
-
#adapter_name ⇒ Object
:nodoc:.
-
#add_limit_offset!(sql, options) ⇒ Object
:nodoc:.
-
#begin_db_transaction ⇒ Object
:nodoc:.
-
#columns(table_name, name = nil) ⇒ Object
SCHEMA STATEMENTS ========================================.
-
#commit_db_transaction ⇒ Object
:nodoc:.
-
#default_sequence_name(table_name, primary_key) ⇒ Object
:nodoc:.
-
#execute(sql, name = nil, &block) ⇒ Object
(also: #update, #delete)
:nodoc:.
-
#initialize(connection, logger, connection_params = nil) ⇒ FirebirdAdapter
constructor
A new instance of FirebirdAdapter.
-
#insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) ⇒ Object
:nodoc:.
-
#next_sequence_value(sequence_name) ⇒ Object
Returns the next sequence value from a sequence generator.
-
#prefetch_primary_key?(table_name = nil) ⇒ Boolean
Returns true for Firebird adapter (since Firebird requires primary key values to be pre-fetched before insert).
-
#quote(value, column = nil) ⇒ Object
QUOTING ==================================================.
-
#quote_column_name(column_name) ⇒ Object
:nodoc:.
-
#quote_string(string) ⇒ Object
:nodoc:.
-
#quoted_false ⇒ Object
:nodoc:.
-
#quoted_true ⇒ Object
:nodoc:.
- #reconnect! ⇒ Object
-
#rollback_db_transaction ⇒ Object
:nodoc:.
-
#select_all(sql, name = nil) ⇒ Object
DATABASE STATEMENTS ======================================.
-
#select_one(sql, name = nil) ⇒ Object
:nodoc:.
Methods inherited from AbstractAdapter
#disconnect!, #raw_connection, #reset_runtime, #supports_count_distinct?, #supports_migrations?, #verify!
Methods included from Quoting
Methods included from DatabaseStatements
#add_limit!, #reset_sequence!, #select_value, #select_values, #transaction
Methods included from SchemaStatements
#add_column, #add_column_options!, #add_index, #change_column, #change_column_default, #create_table, #drop_table, #dump_schema_information, #index_name, #initialize_schema_information, #native_database_types, #remove_column, #remove_index, #rename_column, #rename_table, #structure_dump, #table_alias_for, #table_alias_length, #type_to_sql
Constructor Details
#initialize(connection, logger, connection_params = nil) ⇒ FirebirdAdapter
Returns a new instance of FirebirdAdapter.
244 245 246 247 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 244 def initialize(connection, logger, connection_params=nil) super(connection, logger) @connection_params = connection_params end |
Instance Method Details
#active? ⇒ Boolean
CONNECTION MANAGEMENT ====================================
293 294 295 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 293 def active? not @connection.closed? end |
#adapter_name ⇒ Object
:nodoc:
249 250 251 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 249 def adapter_name # :nodoc: 'Firebird' end |
#add_limit_offset!(sql, options) ⇒ Object
:nodoc:
348 349 350 351 352 353 354 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 348 def add_limit_offset!(sql, ) # :nodoc: if [:limit] limit_string = "FIRST #{[:limit]}" limit_string << " SKIP #{[:offset]}" if [:offset] sql.sub!(/\A(\s*SELECT\s)/i, '\&' + limit_string + ' ') end end |
#begin_db_transaction ⇒ Object
:nodoc:
332 333 334 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 332 def begin_db_transaction() # :nodoc: @transaction = @connection.start_transaction end |
#columns(table_name, name = nil) ⇒ Object
SCHEMA STATEMENTS ========================================
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 366 def columns(table_name, name = nil) # :nodoc: sql = <<-END_SQL SELECT r.rdb$field_name, r.rdb$field_source, f.rdb$field_type, f.rdb$field_sub_type, f.rdb$field_length, f.rdb$field_precision, f.rdb$field_scale, COALESCE(r.rdb$default_source, f.rdb$default_source) rdb$default_source, COALESCE(r.rdb$null_flag, f.rdb$null_flag) rdb$null_flag FROM rdb$relation_fields r JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name WHERE r.rdb$relation_name = '#{table_name.to_s.upcase}' ORDER BY r.rdb$field_position END_SQL execute(sql, name).collect do |field| field_values = field.values.collect do |value| case value when String then value.rstrip when FireRuby::Blob then value.to_s else value end end FirebirdColumn.new(*field_values) end end |
#commit_db_transaction ⇒ Object
:nodoc:
336 337 338 339 340 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 336 def commit_db_transaction() # :nodoc: @transaction.commit ensure @transaction = nil end |
#default_sequence_name(table_name, primary_key) ⇒ Object
:nodoc:
259 260 261 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 259 def default_sequence_name(table_name, primary_key) # :nodoc: "#{table_name}_seq" end |
#execute(sql, name = nil, &block) ⇒ Object Also known as: update, delete
:nodoc:
314 315 316 317 318 319 320 321 322 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 314 def execute(sql, name = nil, &block) # :nodoc: log(sql, name) do if @transaction @connection.execute(sql, @transaction, &block) else @connection.execute_immediate(sql, &block) end end end |
#insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) ⇒ Object
:nodoc:
324 325 326 327 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 324 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) # :nodoc: execute(sql, name) id_value end |
#next_sequence_value(sequence_name) ⇒ Object
Returns the next sequence value from a sequence generator. Not generally called directly; used by ActiveRecord to get the next primary key value when inserting a new database record (see #prefetch_primary_key?).
359 360 361 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 359 def next_sequence_value(sequence_name) FireRuby::Generator.new(sequence_name, @connection).next(1) end |
#prefetch_primary_key?(table_name = nil) ⇒ Boolean
Returns true for Firebird adapter (since Firebird requires primary key values to be pre-fetched before insert). See also #next_sequence_value.
255 256 257 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 255 def prefetch_primary_key?(table_name = nil) true end |
#quote(value, column = nil) ⇒ Object
QUOTING ==================================================
266 267 268 269 270 271 272 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 266 def quote(value, column = nil) # :nodoc: if [Time, DateTime].include?(value.class) "CAST('#{value.strftime("%Y-%m-%d %H:%M:%S")}' AS TIMESTAMP)" else super end end |
#quote_column_name(column_name) ⇒ Object
:nodoc:
278 279 280 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 278 def quote_column_name(column_name) # :nodoc: %Q("#{ar_to_fb_case(column_name)}") end |
#quote_string(string) ⇒ Object
:nodoc:
274 275 276 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 274 def quote_string(string) # :nodoc: string.gsub(/'/, "''") end |
#quoted_false ⇒ Object
:nodoc:
286 287 288 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 286 def quoted_false # :nodoc: quote(boolean_domain[:false]) end |
#quoted_true ⇒ Object
:nodoc:
282 283 284 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 282 def quoted_true # :nodoc: quote(boolean_domain[:true]) end |
#reconnect! ⇒ Object
297 298 299 300 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 297 def reconnect! @connection.close @connection = @connection.database.connect(*@connection_params) end |
#rollback_db_transaction ⇒ Object
:nodoc:
342 343 344 345 346 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 342 def rollback_db_transaction() # :nodoc: @transaction.rollback ensure @transaction = nil end |
#select_all(sql, name = nil) ⇒ Object
DATABASE STATEMENTS ======================================
305 306 307 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 305 def select_all(sql, name = nil) # :nodoc: select(sql, name) end |
#select_one(sql, name = nil) ⇒ Object
:nodoc:
309 310 311 312 |
# File 'lib/active_record/connection_adapters/firebird_adapter.rb', line 309 def select_one(sql, name = nil) # :nodoc: result = select(sql, name) result.nil? ? nil : result.first end |