module Sequel::Fdbsql::DatabaseMethods

Methods shared by Database instances that connect to the FoundationDB SQL Layer

Constants

BOUND_VARIABLE_SQLTIME_FORMAT
BOUND_VARIABLE_TIMESTAMP_FORMAT

the literal methods put quotes around things, but when we bind a variable there shouldn't be quotes around it it should just be the timestamp, so we need whole new formats here.

DATABASE_ERROR_REGEXPS

This is a fallback used by the base class if the sqlstate fails to figure out what error type it is.

FOREIGN_KEY_CONSTRAINT_SQLSTATES
NOT_NULL_CONSTRAINT_SQLSTATES
STALE_STATEMENT_SQLSTATE
UNIQUE_CONSTRAINT_SQLSTATES

Attributes

conversion_procs[R]

A hash of conversion procs, keyed by type integer (oid) and having callable values for the conversion proc for that type.

Public Instance Methods

bound_variable_arg(arg, conn) click to toggle source

Convert given argument so that it can be used directly by pg. Currently, pg doesn't handle fractional seconds in Time/DateTime or blobs with “0”, and it won't ever handle Sequel::SQLTime values correctly. Only public for use by the adapter, shouldn't be used by external code.

# File lib/sequel/adapters/shared/fdbsql.rb, line 25
def bound_variable_arg(arg, conn)
  case arg
  # TODO TDD it:
  when Sequel::SQL::Blob
    # the 1 means treat this as a binary blob
    {:value => arg, :format => 1}
  when Sequel::SQLTime
    # the literal methods put quotes around things, but this is a bound variable, so we can't use those
    arg.strftime(BOUND_VARIABLE_SQLTIME_FORMAT)
  when DateTime, Time
    # the literal methods put quotes around things, but this is a bound variable, so we can't use those
    from_application_timestamp(arg).strftime(BOUND_VARIABLE_TIMESTAMP_FORMAT)
  else
     arg
  end
end
database_type() click to toggle source

Fdbsql uses the :fdbsql database type.

# File lib/sequel/adapters/shared/fdbsql.rb, line 43
def database_type
  :fdbsql
end
foreign_key_list(table, opts=OPTS) click to toggle source

Return full foreign key information, including Postgres returns hash like: {“b_e_fkey”=> {:name=>:b_e_fkey, :columns=>, :on_update=>:no_action, :on_delete=>:no_action, :deferrable=>false, :table=>:a, :key=>}}

# File lib/sequel/adapters/shared/fdbsql.rb, line 125
def foreign_key_list(table, opts=OPTS)
  out_identifier, in_identifier = identifier_convertors(opts)
  schema, table = schema_or_current_and_table(table, opts)
  sql_table = in_identifier.call(table)
  columns_dataset = metadata_dataset.
    select(:tc__table_name___table_name,
           :tc__table_schema___table_schema,
           :tc__is_deferrable___deferrable,
           :kc__column_name___column_name,
           :kc__constraint_schema___schema,
           :kc__constraint_name___name,
           :rc__update_rule___on_update,
           :rc__delete_rule___on_delete).
    from(Sequel.as(:information_schema__table_constraints, 'tc')).
    join(Sequel.as(:information_schema__key_column_usage, 'kc'),
         [:constraint_schema, :constraint_name]).
    join(Sequel.as(:information_schema__referential_constraints, 'rc'),
         [:constraint_name, :constraint_schema]).
    where(:tc__table_name => sql_table,
          :tc__table_schema => schema,
          :tc__constraint_type => 'FOREIGN KEY')

  keys_dataset = metadata_dataset.
    select(:rc__constraint_schema___schema,
           :rc__constraint_name___name,
           :kc__table_name___key_table,
           :kc__column_name___key_column).
    from(Sequel.as(:information_schema__table_constraints, 'tc')).
    join(Sequel.as(:information_schema__referential_constraints, 'rc'),
         [:constraint_schema, :constraint_name]).
    join(Sequel.as(:information_schema__key_column_usage, 'kc'),
         :kc__constraint_schema => :rc__unique_constraint_schema,
         :kc__constraint_name => :rc__unique_constraint_name).
    where(:tc__table_name => sql_table,
          :tc__table_schema => schema,
          :tc__constraint_type => 'FOREIGN KEY')
  foreign_keys = {}
  columns_dataset.each do |row|
    foreign_key = foreign_keys.fetch(row[:name]) do |key|
      foreign_keys[row[:name]] = row
      row[:name] = out_identifier.call(row[:name])
      row[:columns] = []
      row[:key] = []
      row
    end
    foreign_key[:columns] << out_identifier.call(row[:column_name])
  end
  keys_dataset.each do |row|
    foreign_key = foreign_keys[row[:name]]
    foreign_key[:table] = out_identifier.call(row[:key_table])
    foreign_key[:key] << out_identifier.call(row[:key_column])
  end
  foreign_keys.values
end
global_index_namespace?() click to toggle source

indexes are namespaced per table

# File lib/sequel/adapters/shared/fdbsql.rb, line 54
def global_index_namespace?
  false
end
indexes(table, opts=OPTS) click to toggle source

Return indexes for the table postgres returns: {:blah_blah_index=>{:columns=>, :unique=>true, :deferrable=>nil},

:items_n_a_index=>{:columns=>[:n, :a], :unique=>false, :deferrable=>nil}}
# File lib/sequel/adapters/shared/fdbsql.rb, line 184
def indexes(table, opts=OPTS)
  out_identifier, in_identifier = identifier_convertors(opts)
  schema, table = schema_or_current_and_table(table, opts)
  dataset = metadata_dataset.
    select(:is__is_unique,
           Sequel.as({:is__is_unique => 'YES'}, 'unique'),
           :is__index_name,
           :ic__column_name).
    from(Sequel.as(:information_schema__indexes, 'is')).
    join(Sequel.as(:information_schema__index_columns, 'ic'),
         :ic__index_table_schema => :is__table_schema,
         :ic__index_table_name => :is__table_name,
         :ic__index_name => :is__index_name).
    where(:is__table_schema => schema,
          :is__table_name => in_identifier.call(table)).
    exclude(:is__index_type => 'PRIMARY')
  indexes = {}
  dataset.each do |row|
    index = indexes.fetch(out_identifier.call(row[:index_name])) do |key|
      h = { :unique => row[:unique], :columns => [] }
      indexes[key] = h
      h
    end
    index[:columns] << out_identifier.call(row[:column_name])
  end
  indexes
end
primary_key(table_name, opts=OPTS) click to toggle source

Return primary key for the given table.

# File lib/sequel/adapters/shared/fdbsql.rb, line 59
def primary_key(table_name, opts=OPTS)
  quoted_table = quote_schema_table(table_name)
  Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
  out_identifier, in_identifier = identifier_convertors(opts)
  schema, table = schema_or_current_and_table(table_name, opts)
  dataset = metadata_dataset.
    select(:kc__column_name).
    from(Sequel.as(:information_schema__key_column_usage, 'kc')).
    join(Sequel.as(:information_schema__table_constraints, 'tc'),
         [:table_name, :table_schema, :constraint_name]).
    where(:kc__table_name => in_identifier.call(table),
          :kc__table_schema => schema,
          :tc__constraint_type => 'PRIMARY KEY')
  value = dataset.map do |row|
    out_identifier.call(row.delete(:column_name))
  end
  value = case value.size
            when 0 then nil
            when 1 then value.first
            else value
          end
  Sequel.synchronize{@primary_keys[quoted_table] = value}
end
serial_primary_key_options() click to toggle source

like PostgreSQL fdbsql uses SERIAL psuedo-type instead of AUTOINCREMENT for managing incrementing primary keys.

# File lib/sequel/adapters/shared/fdbsql.rb, line 49
def serial_primary_key_options
  {:primary_key => true, :serial => true, :type=>Integer}
end
supports_create_table_if_not_exists?() click to toggle source

the sql layer supports CREATE TABLE IF NOT EXISTS syntax,

# File lib/sequel/adapters/shared/fdbsql.rb, line 84
def supports_create_table_if_not_exists?
  true
end
supports_deferrable_foreign_key_constraints?() click to toggle source

Fdbsql supports deferrable fk constraints

# File lib/sequel/adapters/shared/fdbsql.rb, line 89
def supports_deferrable_foreign_key_constraints?
  true
end
supports_drop_table_if_exists?() click to toggle source

the sql layer supports DROP TABLE IF EXISTS

# File lib/sequel/adapters/shared/fdbsql.rb, line 94
def supports_drop_table_if_exists?
  true
end
tables(opts=OPTS, &block) click to toggle source

Array of symbols specifying table names in the current database. The dataset used is yielded to the block if one is provided, otherwise, an array of symbols of table names is returned.

Options:

:qualify

Return the tables as Sequel::SQL::QualifiedIdentifier instances, using the schema the table is located in as the qualifier.

:schema

The schema to search

:server

The server to use

# File lib/sequel/adapters/shared/fdbsql.rb, line 107
def tables(opts=OPTS, &block)
  tables_or_views('TABLE', opts, &block)
end
views(opts=OPTS, &block) click to toggle source

Array of symbols specifying view names in the current database.

Options:

:qualify

Return the views as Sequel::SQL::QualifiedIdentifier instances, using the schema the view is located in as the qualifier.

:schema

The schema to search

:server

The server to use

# File lib/sequel/adapters/shared/fdbsql.rb, line 118
def views(opts=OPTS, &block)
  tables_or_views('VIEW', opts, &block)
end

Private Instance Methods

adapter_initialize() click to toggle source
# File lib/sequel/adapters/shared/fdbsql.rb, line 219
def adapter_initialize
  @primary_keys = {}
  # Postgres supports named types in the db, if we want to support anything that's not built in, this
  # will have to be changed to not be a constant
  @conversion_procs = Sequel::Postgres::PG_TYPES.dup
  @conversion_procs[16] = Proc.new {|s| s == 'true'}
  @conversion_procs[1184] = @conversion_procs[1114] = method(:to_application_timestamp)
  @conversion_procs.freeze
end
alter_table_op_sql(table, op) click to toggle source
Calls superclass method
# File lib/sequel/adapters/shared/fdbsql.rb, line 229
def alter_table_op_sql(table, op)
  quoted_name = quote_identifier(op[:name]) if op[:name]
  case op[:op]
  when :set_column_type
    "ALTER COLUMN #{quoted_name} SET DATA TYPE #{type_literal(op)}"
  when :set_column_null
    "ALTER COLUMN #{quoted_name} #{op[:null] ? '' : 'NOT'} NULL"
  else
    super
  end
end
check_database_errors() { || ... } click to toggle source

Convert exceptions raised from the block into DatabaseErrors.

# File lib/sequel/adapters/shared/fdbsql.rb, line 242
def check_database_errors
  begin
    yield
  rescue => e
    raise_error(e, :classes=>CONVERTED_EXCEPTIONS)
  end
end
column_schema_normalize_default(default, type) click to toggle source
# File lib/sequel/adapters/shared/fdbsql.rb, line 250
def column_schema_normalize_default(default, type)
  # the default value returned by schema parsing is not escaped or quoted
  # in any way, it's just the value of the string
  # the base implementation assumes it would come back "'my ''default'' value'"
  # fdbsql returns "my 'default' value" (Not including double quotes for either)
  return default
end
create_table_as_sql(name, sql, options) click to toggle source

FDBSQL requires parens around the SELECT, and the WITH DATA syntax.

# File lib/sequel/adapters/shared/fdbsql.rb, line 259
def create_table_as_sql(name, sql, options)
  "#{create_table_prefix_sql(name, options)} AS (#{sql}) WITH DATA"
end
database_error_classes() click to toggle source
# File lib/sequel/adapters/shared/fdbsql.rb, line 263
def database_error_classes
  CONVERTED_EXCEPTIONS
end
database_error_regexps() click to toggle source
# File lib/sequel/adapters/shared/fdbsql.rb, line 297
def database_error_regexps
  DATABASE_ERROR_REGEXPS
end
database_specific_error_class_from_sqlstate(sqlstate) click to toggle source

Given the SQLState, return the appropriate DatabaseError subclass.

# File lib/sequel/adapters/shared/fdbsql.rb, line 273
def database_specific_error_class_from_sqlstate(sqlstate)
  # There is also a CheckConstraintViolation in Sequel, but the sql layer doesn't support check constraints
  case sqlstate
  when *NOT_NULL_CONSTRAINT_SQLSTATES
    NotNullConstraintViolation
  when *FOREIGN_KEY_CONSTRAINT_SQLSTATES
    ForeignKeyConstraintViolation
  when *UNIQUE_CONSTRAINT_SQLSTATES
    UniqueConstraintViolation
  end
end
identifier_convertors(opts=OPTS) click to toggle source
# File lib/sequel/adapters/shared/fdbsql.rb, line 301
def identifier_convertors(opts=OPTS)
  [output_identifier_meth(opts[:dataset]), input_identifier_meth(opts[:dataset])]
end
identifier_input_method_default() click to toggle source

Like PostgreSQL fdbsql folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.

# File lib/sequel/adapters/shared/fdbsql.rb, line 306
def identifier_input_method_default
  nil
end
identifier_output_method_default() click to toggle source

Like PostgreSQL fdbsql folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.

# File lib/sequel/adapters/shared/fdbsql.rb, line 311
def identifier_output_method_default
  nil
end
normalize_decimal_to_integer(type, scale) click to toggle source

If the given type is DECIMAL with scale 0, say that it's an integer

# File lib/sequel/adapters/shared/fdbsql.rb, line 316
def normalize_decimal_to_integer(type, scale)
  if (type == 'DECIMAL' and scale == 0)
    'integer'
  else
    type
  end
end
remove_cached_schema(table) click to toggle source

Remove the cached entries for primary keys and sequences when a table is changed.

Calls superclass method
# File lib/sequel/adapters/shared/fdbsql.rb, line 326
def remove_cached_schema(table)
  tab = quote_schema_table(table)
  Sequel.synchronize do
    @primary_keys.delete(tab)
  end
  super
end
schema_or_current_and_table(table, opts=OPTS) click to toggle source
# File lib/sequel/adapters/shared/fdbsql.rb, line 334
def schema_or_current_and_table(table, opts=OPTS)
  schema, table = schema_and_table(table)
  schema = opts.fetch(:schema, schema || Sequel.lit('CURRENT_SCHEMA'))
  [schema, table]
end
schema_parse_table(table, opts = {}) click to toggle source

returns an array of column information with each column being of the form:

:column_name, {:db_type=>“integer”, :default=>nil, :allow_null=>false, :primary_key=>true, :type=>:integer}
# File lib/sequel/adapters/shared/fdbsql.rb, line 342
def schema_parse_table(table, opts = {})
  out_identifier, in_identifier = identifier_convertors(opts)
  schema, table = schema_or_current_and_table(table, opts)
  dataset = metadata_dataset.
    select(:c__column_name,
           Sequel.as({:c__is_nullable => 'YES'}, 'allow_null'),
           :c__column_default___default,
           :c__data_type___db_type,
           :c__character_maximum_length___max_length,
           :c__numeric_scale,
           Sequel.as({:tc__constraint_type => 'PRIMARY KEY'}, 'primary_key')).
    from(Sequel.as(:information_schema__key_column_usage, 'kc')).
    join(Sequel.as(:information_schema__table_constraints, 'tc'),
         :tc__constraint_type => 'PRIMARY KEY',
         :tc__table_name => :kc__table_name,
         :tc__table_schema => :kc__table_schema,
         :tc__constraint_name => :kc__constraint_name).
    right_outer_join(Sequel.as(:information_schema__columns, 'c'),
                     [:table_name, :table_schema, :column_name]).
    where(:c__table_name => in_identifier.call(table),
          :c__table_schema => schema)
  dataset.map do |row|
    row[:default] = nil if blank_object?(row[:default])
    row[:type] = schema_column_type(normalize_decimal_to_integer(row[:db_type], row[:numeric_scale]))
    [out_identifier.call(row.delete(:column_name)), row]
  end
end
tables_or_views(type, opts) { |dataset| ... } click to toggle source
# File lib/sequel/adapters/shared/fdbsql.rb, line 370
def tables_or_views(type, opts, &block)
  schema = opts[:schema] || Sequel.lit('CURRENT_SCHEMA')
  m = output_identifier_meth
  dataset = metadata_dataset.server(opts[:server]).select(:table_name).
    from(Sequel.qualify('information_schema','tables')).
    where(:table_schema => schema,
          :table_type => type)
  if block_given?
    yield(dataset)
  elsif opts[:qualify]
    dataset.select_append(:table_schema).map{|r| Sequel.qualify(m.call(r[:table_schema]), m.call(r[:table_name])) }
  else
    dataset.map{|r| m.call(r[:table_name])}
  end
end
type_literal_generic_bignum(column) click to toggle source

Handle bigserial type if :serial option is present

Calls superclass method
# File lib/sequel/adapters/shared/fdbsql.rb, line 387
def type_literal_generic_bignum(column)
  column[:serial] ? :bigserial : super
end
type_literal_generic_integer(column) click to toggle source

Handle serial type if :serial option is present

Calls superclass method
# File lib/sequel/adapters/shared/fdbsql.rb, line 392
def type_literal_generic_integer(column)
  column[:serial] ? :serial : super
end