Module: ArJdbc::Firebird

Included in:
ActiveRecord::ConnectionAdapters::FirebirdAdapter
Defined in:
lib/arjdbc/firebird/adapter.rb

Defined Under Namespace

Modules: Column

Constant Summary collapse

ADAPTER_NAME =
'Firebird'.freeze
NATIVE_DATABASE_TYPES =
{
  :primary_key => "integer not null primary key",
  :string => { :name => "varchar", :limit => 255 },
  :text => { :name => "blob sub_type text" },
  :integer => { :name => "integer" },
  :float => { :name => "float" },
  :datetime => { :name => "timestamp" },
  :timestamp => { :name => "timestamp" },
  :time => { :name => "time" },
  :date => { :name => "date" },
  :binary => { :name => "blob" },
  :boolean => { :name => 'char', :limit => 1 },
  :numeric => { :name => "numeric" },
  :decimal => { :name => "decimal" },
  :char => { :name => "char" },
}
IDENTIFIER_LENGTH =

usual DB meta-identifier: 31 chars maximum

31
@@update_lob_values =
true

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.arel2_visitors(config = nil) ⇒ Object

Deprecated.

no longer used



71
72
73
# File 'lib/arjdbc/firebird/adapter.rb', line 71

def self.arel2_visitors(config = nil)
  { 'firebird' => arel_visitor_type, 'firebirdsql' => arel_visitor_type }
end

.arel_visitor_type(config = nil) ⇒ Object

See Also:

  • ArelHelper::ClassMethods#arel_visitor_type


66
67
68
# File 'lib/arjdbc/firebird/adapter.rb', line 66

def self.arel_visitor_type(config = nil)
  require 'arel/visitors/firebird'; ::Arel::Visitors::Firebird
end

.column_selectorObject

See Also:

  • ActiveRecord::ConnectionAdapters::JdbcColumn#column_types


26
27
28
# File 'lib/arjdbc/firebird/adapter.rb', line 26

def self.column_selector
  [ /firebird/i, lambda { |cfg, column| column.extend(Column) } ]
end

.emulate_booleansObject

Deprecated.

Use #emulate_booleans? instead.



84
# File 'lib/arjdbc/firebird/adapter.rb', line 84

def self.emulate_booleans; @@emulate_booleans; end

.emulate_booleans=(emulate) ⇒ Object

See Also:

  • #emulate_booleans?


86
# File 'lib/arjdbc/firebird/adapter.rb', line 86

def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end

.emulate_booleans?Boolean

Boolean emulation can be disabled using :

ArJdbc::Firebird.emulate_booleans = false

Returns:

  • (Boolean)


82
# File 'lib/arjdbc/firebird/adapter.rb', line 82

def self.emulate_booleans?; @@emulate_booleans; end

.jdbc_connection_classObject



21
22
23
# File 'lib/arjdbc/firebird/adapter.rb', line 21

def self.jdbc_connection_class
  ::ActiveRecord::ConnectionAdapters::FirebirdJdbcConnection
end

.update_lob_values=(update) ⇒ Object



97
# File 'lib/arjdbc/firebird/adapter.rb', line 97

def self.update_lob_values=(update); @@update_lob_values = update; end

.update_lob_values?Boolean

Updating records with LOB values (binary/text columns) in a separate statement can be disabled using :

ArJdbc::Firebird.update_lob_values = false

Returns:

  • (Boolean)


95
# File 'lib/arjdbc/firebird/adapter.rb', line 95

def self.update_lob_values?; @@update_lob_values; end

Instance Method Details

#adapter_nameObject



108
109
110
# File 'lib/arjdbc/firebird/adapter.rb', line 108

def adapter_name
  ADAPTER_NAME
end

#add_limit_offset!(sql, options) ⇒ Object



250
251
252
253
254
# File 'lib/arjdbc/firebird/adapter.rb', line 250

def add_limit_offset!(sql, options)
  if limit = options[:limit]
    insert_limit_offset!(sql, limit, options[:offset])
  end
end

#change_column(table_name, column_name, type, options = {}) ⇒ Object



316
317
318
# File 'lib/arjdbc/firebird/adapter.rb', line 316

def change_column(table_name, column_name, type, options = {})
  execute "ALTER TABLE #{table_name} ALTER  #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
end

#clear_cache!Object



173
174
175
176
# File 'lib/arjdbc/firebird/adapter.rb', line 173

def clear_cache!
  super
  reload_type_map
end

#column_name_lengthObject



283
# File 'lib/arjdbc/firebird/adapter.rb', line 283

def column_name_length; IDENTIFIER_LENGTH; end

#create_table(name, options = {}) ⇒ Object



300
301
302
303
# File 'lib/arjdbc/firebird/adapter.rb', line 300

def create_table(name, options = {})
  super(name, options)
  execute "CREATE GENERATOR #{default_sequence_name(name)}"
end

#default_sequence_name(table_name, column = nil) ⇒ Object



285
286
287
288
# File 'lib/arjdbc/firebird/adapter.rb', line 285

def default_sequence_name(table_name, column = nil)
  len = IDENTIFIER_LENGTH - 4
  table_name.to_s.gsub (/(^|\.)([\w$-]{1,#{len}})([\w$-]*)$/), '\1\2_seq'
end

#drop_table(name, options = {}) ⇒ Object



311
312
313
314
# File 'lib/arjdbc/firebird/adapter.rb', line 311

def drop_table(name, options = {})
  super(name)
  execute_quietly "DROP GENERATOR #{default_sequence_name(name)}"
end

#ids_in_list_limitObject

Does this adapter restrict the number of IDs you can use in a list. Oracle has a limit of 1000.



241
242
243
# File 'lib/arjdbc/firebird/adapter.rb', line 241

def ids_in_list_limit
  1499
end

#index_name_lengthObject



282
# File 'lib/arjdbc/firebird/adapter.rb', line 282

def index_name_length;  IDENTIFIER_LENGTH; end

#initialize_type_map(m) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/arjdbc/firebird/adapter.rb', line 133

def initialize_type_map(m)
  register_class_with_limit m, %r(binary)i, ActiveRecord::Type::Binary
  register_class_with_limit m, %r(text)i,   ActiveRecord::Type::Text

  register_class_with_limit m, %r(date(?:\(.*?\))?$)i, DateType
  register_class_with_limit m, %r(time(?:\(.*?\))?$)i, ActiveRecord::Type::Time

  register_class_with_limit m, %r(float)i, ActiveRecord::Type::Float
  register_class_with_limit m, %r(int)i,   ActiveRecord::Type::Integer

  m.alias_type %r(blob)i,   'binary'
  m.alias_type %r(clob)i,   'text'
  m.alias_type %r(double)i, 'float'

  m.register_type(%r(decimal)i) do |sql_type|
    scale = extract_scale(sql_type)
    precision = extract_precision(sql_type)
    if scale == 0
      ActiveRecord::Type::Integer.new(precision: precision)
    else
      ActiveRecord::Type::Decimal.new(precision: precision, scale: scale)
    end
  end
  m.alias_type %r(numeric)i, 'decimal'

  register_class_with_limit m, %r(varchar)i, ActiveRecord::Type::String

  m.register_type(%r(^char)i) do |sql_type|
    precision = extract_precision(sql_type)
    if Firebird.emulate_booleans? && precision == 1
      ActiveRecord::Type::Boolean.new
    else
      ActiveRecord::Type::String.new(:precision => precision)
    end
  end

  register_class_with_limit m, %r(datetime)i, ActiveRecord::Type::DateTime
  register_class_with_limit m, %r(timestamp)i, TimestampType
end

#insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) ⇒ Object



245
246
247
248
# File 'lib/arjdbc/firebird/adapter.rb', line 245

def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
  execute(sql, name, binds)
  id_value
end

#insert_limit_offset!(sql, limit, offset) ⇒ Object



259
260
261
262
263
264
265
266
# File 'lib/arjdbc/firebird/adapter.rb', line 259

def insert_limit_offset!(sql, limit, offset)
  lim_off = ''
  lim_off << "FIRST #{limit}"  if limit
  lim_off << " SKIP #{offset}" if offset
  lim_off.strip!

  sql.sub!(SELECT_RE, "\\&#{lim_off} ") unless lim_off.empty?
end

#jdbc_column_classObject



63
# File 'lib/arjdbc/firebird/adapter.rb', line 63

def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::FirebirdColumn end

#native_database_typesObject



129
130
131
# File 'lib/arjdbc/firebird/adapter.rb', line 129

def native_database_types
  NATIVE_DATABASE_TYPES
end

#next_sequence_value(sequence_name) ⇒ Object



296
297
298
# File 'lib/arjdbc/firebird/adapter.rb', line 296

def next_sequence_value(sequence_name)
  select_one("SELECT GEN_ID(#{sequence_name}, 1 ) FROM RDB$DATABASE;")["gen_id"]
end

#prefetch_primary_key?(table_name = nil) ⇒ Boolean

Should primary key values be selected from their corresponding sequence before the insert statement?

Returns:

  • (Boolean)

See Also:



272
273
274
275
276
# File 'lib/arjdbc/firebird/adapter.rb', line 272

def prefetch_primary_key?(table_name = nil)
  return true if table_name.nil?
  primary_keys(table_name.to_s).size == 1
  # columns(table_name).count { |column| column.primary } == 1
end

#quote(value, column = nil) ⇒ Object



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/arjdbc/firebird/adapter.rb', line 329

def quote(value, column = nil)
  return value.quoted_id if value.respond_to?(:quoted_id)
  return value if sql_literal?(value)

  type = column && column.type

  # BLOBs are updated separately by an after_save trigger.
  if type == :binary || type == :text
    if update_lob_values?
      return value.nil? ? "NULL" : BLOB_VALUE_MARKER
    else
      return "'#{quote_string(value)}'"
    end
  end

  case value
  when String, ActiveSupport::Multibyte::Chars
    value = value.to_s
    if type == :integer
      value.to_i.to_s
    elsif type == :float
      value.to_f.to_s
    else
      "'#{quote_string(value)}'"
    end
  when NilClass then 'NULL'
  when TrueClass then (type == :integer ? '1' : quoted_true)
  when FalseClass then (type == :integer ? '0' : quoted_false)
  when Float, Fixnum, Bignum then value.to_s
  # BigDecimals need to be output in a non-normalized form and quoted.
  when BigDecimal then value.to_s('F')
  when Symbol then "'#{quote_string(value.to_s)}'"
  else
    if type == :time && value.acts_like?(:time)
      return "'#{get_time(value).strftime("%H:%M:%S")}'"
    end
    if type == :date && value.acts_like?(:date)
      return "'#{value.strftime("%Y-%m-%d")}'"
    end
    super
  end
end

#quote_column_name(column_name) ⇒ Object



404
405
406
407
# File 'lib/arjdbc/firebird/adapter.rb', line 404

def quote_column_name(column_name)
  column_name = column_name.to_s
  %Q("#{column_name =~ /[[:upper:]]/ ? column_name : column_name.upcase}")
end

#quote_string(string) ⇒ Object



384
385
386
# File 'lib/arjdbc/firebird/adapter.rb', line 384

def quote_string(string)
  string.gsub(/'/, "''")
end

#quote_table_name_for_assignment(table, attr) ⇒ Object



399
400
401
# File 'lib/arjdbc/firebird/adapter.rb', line 399

def quote_table_name_for_assignment(table, attr)
  quote_column_name(attr)
end

#quoted_date(value) ⇒ Object



373
374
375
376
377
378
379
380
381
# File 'lib/arjdbc/firebird/adapter.rb', line 373

def quoted_date(value)
  if value.acts_like?(:time) && value.respond_to?(:usec)
    usec = sprintf "%04d", (value.usec / 100.0).round
    value = ::ActiveRecord::Base.default_timezone == :utc ? value.getutc : value.getlocal
    "#{value.strftime("%Y-%m-%d %H:%M:%S")}.#{usec}"
  else
    super
  end
end

#quoted_falseObject



394
395
396
# File 'lib/arjdbc/firebird/adapter.rb', line 394

def quoted_false
  quote(0)
end

#quoted_trueObject



389
390
391
# File 'lib/arjdbc/firebird/adapter.rb', line 389

def quoted_true
  quote(1)
end

#remove_index(table_name, options) ⇒ Object



324
325
326
# File 'lib/arjdbc/firebird/adapter.rb', line 324

def remove_index(table_name, options)
  execute "DROP INDEX #{index_name(table_name, options)}"
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object



320
321
322
# File 'lib/arjdbc/firebird/adapter.rb', line 320

def rename_column(table_name, column_name, new_column_name)
  execute "ALTER TABLE #{table_name} ALTER  #{column_name} TO #{new_column_name}"
end

#rename_table(name, new_name) ⇒ Object



305
306
307
308
309
# File 'lib/arjdbc/firebird/adapter.rb', line 305

def rename_table(name, new_name)
  execute "RENAME #{name} TO #{new_name}"
  name_seq, new_name_seq = default_sequence_name(name), default_sequence_name(new_name)
  execute_quietly "UPDATE RDB$GENERATORS SET RDB$GENERATOR_NAME='#{new_name_seq}' WHERE RDB$GENERATOR_NAME='#{name_seq}'"
end

#reset_sequence!(table, column, sequence = nil) ⇒ Object

Set the sequence to the max value of the table's column.



291
292
293
294
# File 'lib/arjdbc/firebird/adapter.rb', line 291

def reset_sequence!(table, column, sequence = nil)
  max_id = select_value("SELECT max(#{column}) FROM #{table}")
  execute("ALTER SEQUENCE #{default_sequence_name(table, column)} RESTART WITH #{max_id}")
end

#supports_count_distinct?Boolean

Does this adapter support using DISTINCT within COUNT?

Returns:

  • (Boolean)


228
229
230
# File 'lib/arjdbc/firebird/adapter.rb', line 228

def supports_count_distinct?
  true
end

#supports_ddl_transactions?Boolean

Does this adapter support DDL rollbacks in transactions? That is, would CREATE TABLE or ALTER TABLE get rolled back by a transaction? PostgreSQL, SQL Server, and others support this. MySQL and others do not.

Returns:

  • (Boolean)


235
236
237
# File 'lib/arjdbc/firebird/adapter.rb', line 235

def supports_ddl_transactions?
  false
end

#supports_migrations?Boolean

Does this adapter support migrations?

Returns:

  • (Boolean)


217
218
219
# File 'lib/arjdbc/firebird/adapter.rb', line 217

def supports_migrations?
  true
end

#supports_primary_key?Boolean

Can this adapter determine the primary key for tables not attached to an Active Record class, such as join tables?

Returns:

  • (Boolean)


223
224
225
# File 'lib/arjdbc/firebird/adapter.rb', line 223

def supports_primary_key?
  true
end

#table_alias_lengthObject



280
# File 'lib/arjdbc/firebird/adapter.rb', line 280

def table_alias_length; IDENTIFIER_LENGTH; end

#table_name_lengthObject



281
# File 'lib/arjdbc/firebird/adapter.rb', line 281

def table_name_length;  IDENTIFIER_LENGTH; end

#type_to_sql(type, limit = nil, precision = nil, scale = nil) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/arjdbc/firebird/adapter.rb', line 195

def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  case type
  when :integer
    case limit
      when nil  then 'integer'
      when 1..2 then 'smallint'
      when 3..4 then 'integer'
      when 5..8 then 'bigint'
      else raise(ActiveRecordError, "No integer type has byte size #{limit}. "<<
                                    "Use a NUMERIC with PRECISION 0 instead.")
    end
  when :float
    if limit.nil? || limit <= 4
      'float'
    else
      'double precision'
    end
  else super
  end
end

#update_lob_values?Boolean

Returns:

  • (Boolean)

See Also:



100
# File 'lib/arjdbc/firebird/adapter.rb', line 100

def update_lob_values?; Firebird.update_lob_values?; end