require 'flydata-core/errors' module FlydataCore module TableDef class Base def self.create(source_data, options = {}) params = _create(source_data, options) params ? self.new(*params, options) : nil end def initialize(table_def, table_name, columns, column_def, unique_keys, default_charset, default_source_charset, comment, options = {}) @table_def = table_def @table_name = table_name @columns = columns @column_def = column_def @unique_keys = unique_keys @default_charset = default_charset @default_source_charset = default_source_charset @comment = comment # example of opts # { :skip_primary_key_check => true, # :pk_override => { "table_3" => ["id", "group_id"], "table_5" => ["product_id"] } # } opts = [:skip_primary_key_check, :pk_override].inject({}) do |h, key| v = options[key] v ||= options[key.to_s] h[key] = v if v h end @pk_override, @pk_status = get_pk_override_with_status(@table_name, @columns, @unique_keys, opts) self.class.check_pk(@table_name, @columns, opts) end attr_reader :columns, :column_def, :table_name, :default_source_charset def to_flydata_tabledef tabledef = { table_name: @table_name, columns: @columns, } tabledef[:default_charset] = @default_charset if @default_charset tabledef[:comment] = @comment if @comment tabledef[:src_ddl] = @table_def tabledef[:pk_override] = @pk_override tabledef end def column_names @columns.collect{|col| col[:column]} end def pk_columns @columns.select{|col| col.has_key?(:primary_key) }. sort{|a,b| a[:primary_key] <=> b[:primary_key]}. collect{|col| col[:column]} end def get_pk_override_as_hash @pk_override ? { @table_name => @pk_override } : nil end def converting_uk_to_pk? @pk_status == :pk_unique end private def self._create(source_data, options) raise "subclass must implement" end # Get pk_override with pk_status # [Arguments] # - table_name (String): # - columns (Hash): # columns that includes all column info from source DB # - unique_keys (Hash): # valid unique keys on the table which can use as a primary key # - opts (Hash): # including pk_override values. See a comment above. # [Return values] # pk_status (Symbol) # :pk_original using original pk from source DB # :pk_overridden using user_pk_override from web server # :pk_unique converting unique key (uk) to pk. Need to update the table attributes (UK_AS_PK_OVERRIDE) on web server # :pk_none none of above # pk_override (Hash): # pk_override or nil def get_pk_override_with_status(table_name, columns, unique_keys, opts) pk_override = self.class.override_pk(table_name, columns, opts) if pk_override pk_status = :pk_overridden elsif self.class.pk_exist?(@columns) # real primary key exist. Not using pk_override pk_status = :pk_original else # use an unique key as a pk_override if any. new_pk_override = self.class.uk_to_pk_override(table_name, columns, unique_keys) if new_pk_override pk_override = self.class.override_pk(table_name, columns, new_pk_override) pk_status = :pk_unique else pk_status = :pk_none end end [pk_override, pk_status] end def self.override_pk(table_name, columns, options) pk_override = options[:pk_override] return nil unless pk_override.kind_of?(Hash) && pk_override[table_name] pkcols = pk_override[table_name] pkcols_found = [] columns.each do |column| if pkcols.include?(column[:column]) column[:primary_key] = true pkcols_found << column[:column] else column.delete(:primary_key) if column.has_key?(:primary_key) end end pkcols_missing = pkcols - pkcols_found unless pkcols_missing.empty? raise "Column(s) is specified as a primary key but the column does not exist in the table '#{table_name}': #{pkcols_missing.collect{|pc| "'#{pc}'"}.join(",")}" end pkcols end def self.check_pk(table_name, columns, options) unless pk_exist?(columns) || options[:skip_primary_key_check] raise TableDefError, {error: "no primary key defined", table: table_name} end end def self.pk_exist?(columns) columns.any? {|column| column[:primary_key]} end def self.uk_exist?(columns, unique_keys) unique_keys.any? end def self.uk_to_pk_override(table_name, columns, unique_keys) return nil unless uk_exist?(columns, unique_keys) # use the first unique key as a substitue of primary key new_pk_override = {} new_pk_override[:pk_override] = { table_name => unique_keys.first } $log.info %Q|Using unique key as primary key: {"#{table_name}"=>#{new_pk_override[:pk_override][table_name].inspect}}| new_pk_override end end end end