flydata 0.7.12 → 0.7.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/VERSION +1 -1
  4. data/flydata-core/lib/flydata-core/oracle/config.rb +25 -0
  5. data/flydata-core/lib/flydata-core/oracle/oracle_client.rb +48 -0
  6. data/flydata-core/lib/flydata-core/oracle/query_helper.rb +20 -0
  7. data/flydata-core/lib/flydata-core/oracle/source_pos.rb +63 -0
  8. data/flydata-core/lib/flydata-core/table_def/oracle_table_def.rb +167 -0
  9. data/flydata-core/spec/oracle/config_spec.rb +45 -0
  10. data/flydata-core/spec/oracle/source_pos_spec.rb +101 -0
  11. data/flydata.gemspec +0 -0
  12. data/lib/flydata/command/sync.rb +14 -4
  13. data/lib/flydata/source.rb +1 -0
  14. data/lib/flydata/source/sync_repair.rb +25 -0
  15. data/lib/flydata/source_mysql/generate_source_dump.rb +2 -1
  16. data/lib/flydata/source_mysql/mysql_accessible.rb +30 -0
  17. data/lib/flydata/source_mysql/parser/dump_parser.rb +0 -40
  18. data/lib/flydata/source_mysql/sync_database_size_check.rb +29 -0
  19. data/lib/flydata/source_mysql/sync_repair.rb +26 -0
  20. data/lib/flydata/source_oracle/data_entry.rb +24 -0
  21. data/lib/flydata/source_oracle/generate_source_dump.rb +184 -0
  22. data/lib/flydata/source_oracle/oracle_component.rb +12 -0
  23. data/lib/flydata/source_oracle/parse_dump_and_send.rb +128 -0
  24. data/lib/flydata/source_oracle/plugin_support/context.rb +13 -0
  25. data/lib/flydata/source_oracle/plugin_support/source_position_file.rb +14 -0
  26. data/lib/flydata/source_oracle/query_based_sync/diff_query_generator.rb +122 -0
  27. data/lib/flydata/source_oracle/setup.rb +24 -0
  28. data/lib/flydata/source_oracle/source_pos.rb +18 -0
  29. data/lib/flydata/source_oracle/sync.rb +15 -0
  30. data/lib/flydata/source_oracle/sync_generate_table_ddl.rb +64 -0
  31. data/lib/flydata/source_oracle/table_meta.rb +220 -0
  32. data/lib/flydata/source_postgresql/sync_repair.rb +13 -0
  33. data/spec/flydata/source_mysql/generate_source_dump_spec.rb +2 -2
  34. metadata +27 -3
@@ -0,0 +1,12 @@
1
+ module Flydata
2
+ module SourceOracle
3
+
4
+ # Postgresql specific Component helper methods
5
+ module OracleComponent
6
+ def de_prefs
7
+ de['oracle_data_entry_preference']
8
+ end
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,128 @@
1
+ require 'flydata/source/parse_dump_and_send'
2
+ require 'flydata/source_oracle/oracle_component'
3
+ require 'flydata-core/table_def/oracle_table_def'
4
+
5
+ module Flydata
6
+ module SourceOracle
7
+
8
+ class ParseDumpAndSend < Source::ParseDumpAndSend
9
+ include OracleComponent
10
+
11
+ def value_converters
12
+ FlydataCore::TableDef::OracleTableDef::VALUE_CONVERTERS
13
+ end
14
+
15
+ # dump format
16
+ # dump file is in msgpack. Each table data starts with a source table hash
17
+ # followed by row arrays.
18
+ #
19
+ # {"table_name"=>"users", "columns"=>{"id"=>{"column_name"=>"id", "format_type"=>"bigint"}, "name"=>{"column_name"=>"name", "format_type"=>"character varying"}, "another_id"=>{"column_name"=>"another_id", "format_type"=>"integer"}}}
20
+ # ["2", "hay", "1"]
21
+ # ["3", "hoe", "2"]
22
+ def parse_dump(dump_pos_info, dmpio, create_table_block, insert_record_block,
23
+ check_point_block)
24
+ parser = DumpParser.new(dump_pos_info, dmpio, create_table_block,
25
+ insert_record_block, check_point_block)
26
+ parser.parse_all
27
+ end
28
+ end
29
+
30
+ class DumpParser
31
+ MAX_ROW_BYTES = 1 * 1024 * 1024 # Parser holds rows until the total byte size
32
+ # reaches this number
33
+
34
+ def initialize(dump_pos_info, dmpio, create_table_block, insert_record_block,
35
+ check_point_block)
36
+ @source_pos = dump_pos_info[:source_pos]
37
+ raise ArgumentError.new("source position is required") unless @source_pos
38
+ @current_table = nil
39
+ @last_pos = 0
40
+ @row_head_pos = nil
41
+ @rows = []
42
+ @dmpio = dmpio
43
+ @create_table_block = create_table_block
44
+ @insert_record_block = insert_record_block
45
+ @check_point_block = check_point_block
46
+
47
+ resume(dump_pos_info)
48
+ end
49
+
50
+ def parse_all
51
+ u = MessagePack::Unpacker.new(@dmpio)
52
+ u.each do |obj|
53
+ @last_pos = @dmpio.pos - u.buffer.size
54
+ parse(obj)
55
+ end
56
+ close
57
+ end
58
+
59
+ def parse(obj)
60
+ obj.kind_of?(Hash) ? handle_table_info(obj) : handle_data_row(obj)
61
+ end
62
+
63
+ def close
64
+ unless @rows.empty?
65
+ call_insert_record_block
66
+ # Core logic expects a check point callback with CREATE_TABLE at the
67
+ # end of table data insertion of each table. #handle_table_info takes
68
+ # care of all tables but the last table. This is for the last table.
69
+ call_check_point_block(Parser::State::CREATE_TABLE)
70
+ end
71
+ end
72
+
73
+
74
+ private
75
+
76
+ def resume(dump_pos_info)
77
+ last_pos = dump_pos_info[:last_pos] ? dump_pos_info[:last_pos].to_i : -1
78
+ if last_pos == -1
79
+ # no resume point
80
+ return
81
+ end
82
+
83
+ @last_pos = last_pos
84
+ @dmpio.pos = last_pos
85
+ @current_table = dump_pos_info[:source_table]
86
+ end
87
+
88
+ # {"table_name"=>"users", "columns"=>{"id"=>{"column_name"=>"id", "format_type"=>"bigint"}, "name"=>{"column_name"=>"name", "format_type"=>"character varying"}, "another_id"=>{"column_name"=>"another_id", "format_type"=>"integer"}}}
89
+ def handle_table_info(table_info)
90
+ call_insert_record_block # flush rows if any
91
+ call_check_point_block(Parser::State::CREATE_TABLE)
92
+ columns = table_info["columns"].inject({}) do |h, (k, v)|
93
+ h[k] = %w(column_name format_type).inject({}) do |hh, kk|
94
+ hh[kk.to_sym] = v[kk]
95
+ hh
96
+ end
97
+ h
98
+ end
99
+ @current_table = Parser::SourceTable.new(table_info["table_name"], columns)
100
+ @create_table_block.call(@current_table)
101
+ call_check_point_block(Parser::State::INSERT_RECORD)
102
+ end
103
+
104
+ def handle_data_row(row)
105
+ @row_head_pos = @last_pos unless @row_head_pos
106
+ @rows << row
107
+ if @last_pos - @row_head_pos > MAX_ROW_BYTES
108
+ call_insert_record_block
109
+ end
110
+ end
111
+
112
+ def call_check_point_block(state)
113
+ @check_point_block.call(@current_table, @last_pos, @last_pos, @source_pos,
114
+ state)
115
+ end
116
+
117
+ def call_insert_record_block
118
+ return if @rows.empty?
119
+ if @insert_record_block.call(@current_table, @rows)
120
+ call_check_point_block(Parser::State::INSERT_RECORD)
121
+ end
122
+ @rows = []
123
+ @row_head_pos = nil
124
+ end
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,13 @@
1
+ require 'flydata/plugin_support/context'
2
+ require 'flydata-core/oracle/source_pos'
3
+
4
+ module Flydata
5
+ module SourceOracle
6
+ module PluginSupport
7
+ class Context < ::Flydata::PluginSupport::Context
8
+ register_mandatory_opts :database, :dbconf
9
+ SOURCE_POS_CLASS = ::FlydataCore::Oracle::SourcePos
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'flydata/plugin_support/source_position_file'
2
+ require 'flydata-core/oracle/source_pos'
3
+
4
+ module Flydata
5
+ module SourceOracle
6
+
7
+ module PluginSupport
8
+ class SourcePositionFile < Flydata::PluginSupport::SourcePositionFile
9
+ SOURCE_POS_CLASS = FlydataCore::Oracle::SourcePos
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,122 @@
1
+ require 'flydata-core/oracle/oracle_client'
2
+ require 'flydata-core/table_def/oracle_table_def'
3
+ require 'flydata-core/table_def/value_conv'
4
+
5
+ module Flydata
6
+ module SourceOracle
7
+ module QueryBasedSync
8
+
9
+ class DiffQueryGenerator
10
+
11
+ DEFAULT_LIMIT = 10000
12
+
13
+ FETCH_RECORDS_SQL = <<EOS
14
+ SELECT %{columns} FROM %{table_name} AS OF SCN %{scn} WHERE %{where_clause} ORDER BY %{order_by_clause}
15
+ EOS
16
+
17
+ def initialize(table, schema, query_cond = {})
18
+ @table = table
19
+ @schema = schema
20
+
21
+ @columns = query_cond[:columns] or raise ArgumentError, "columns is required"
22
+ @to_scn = query_cond[:to_scn] or raise ArgumentError, "to_scn is required"
23
+ @pk_columns = query_cond[:pk_columns] or raise ArgumentError, "pk_columns is required"
24
+ @last_pks = query_cond[:last_pks]
25
+ @limit = query_cond[:limit] || DEFAULT_LIMIT
26
+ end
27
+
28
+ def build_query(binding_param_enabled = true)
29
+ column_names, types = @columns.inject([[],[]]) do |arys, h|
30
+ arys[0] << h[:column]; arys[1] << h[:type].gsub(/\(.+?\)/,''); arys
31
+ end
32
+ columns = column_list(column_names, types)
33
+
34
+ query = FETCH_RECORDS_SQL % {
35
+ columns: columns,
36
+ table_name: fq_table_name(@table, @schema),
37
+ scn: @to_scn,
38
+ where_clause: build_where_clause(@pk_columns, @last_pks, @limit),
39
+ order_by_clause: build_order_by_clause(@pk_columns),
40
+ }
41
+
42
+ end
43
+
44
+ private
45
+
46
+ def build_where_clause(pk_columns, last_pks, limit, binding_param_enabled = false)
47
+ clauses = []
48
+ if last_pks && !last_pks.empty?
49
+ clauses << pk_conditions(pk_columns, last_pks, binding_param_enabled)
50
+ end
51
+ clauses << "ROWNUM <= #{limit}"
52
+ clauses.join(" AND ")
53
+ end
54
+
55
+ def build_order_by_clause(pk_columns)
56
+ "#{pk_columns.collect{|col| %Q|"#{col}"| }.join(",")}"
57
+ end
58
+
59
+ def pk_conditions(pk_columns, last_pks, binding_param_enabled)
60
+ i = pk_columns.size - 1
61
+ str = nil
62
+ while i >= 0
63
+ colname = pk_columns[i]
64
+ val = value_in_where(i, last_pks, binding_param_enabled)
65
+ if str
66
+ str = %Q|"#{colname}" > #{val} OR ("#{colname}" = #{val} AND (#{str}))|
67
+ else
68
+ str = %Q|"#{colname}" > #{val}|
69
+ end
70
+ i -= 1
71
+ end
72
+ "(#{str})"
73
+ end
74
+
75
+ def value_in_where(index, last_pks, binding_param_enabled)
76
+ if binding_param_enabled
77
+ "$#{index + 1}"
78
+ else
79
+ %Q|'#{last_pks[index]}'|
80
+ end
81
+ end
82
+
83
+ def fq_table_name(table, schema)
84
+ !schema.to_s.empty? ? %Q|"#{schema.upcase}"."#{table}"| : %Q|"#{table}"|
85
+ end
86
+
87
+ # return a list of columns with necessary conversion as a string
88
+ QUERY_VAL_CONV_RULES = Hash.new do |h, k|
89
+ h[k] =
90
+ case k
91
+ when "bytea"; %Q['0x' || encode("%<column>s", 'hex') AS "%<column>s"]
92
+ else %Q["%<column>s"]
93
+ end
94
+ end
95
+
96
+ VALUE_OVERRIDERS = Hash.new do |h, k|
97
+ h[k] =
98
+ case k
99
+ when "bit", "varbit"; -> (v) { v ? '%d' % v.to_i(2) : nil }
100
+ when "boolean"; -> (v) { FlydataCore::TableDef::OracleTableDef::to_boolean(v) }
101
+ when "money"; -> (v) { v ? FlydataCore::TableDef::ValueConv.strip_currency_format(v) : nil }
102
+ else
103
+ nil
104
+ end
105
+ end
106
+
107
+ def column_list(columns, types)
108
+ columns.each_with_index.collect{|c, i| QUERY_VAL_CONV_RULES[types[i]] % {column:c}}.join(", ")
109
+ end
110
+
111
+ def build_value_overriders(columns, types)
112
+ columns.each_with_index.inject({}) do |h, (c, i)|
113
+ overrider = VALUE_OVERRIDERS[types[i]]
114
+ h[c] = overrider if overrider
115
+ h
116
+ end
117
+ end
118
+ end
119
+
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,24 @@
1
+ require 'flydata/source/setup'
2
+ require 'flydata/sync_file_manager'
3
+
4
+ module Flydata
5
+ module SourceOracle
6
+
7
+ class Setup < Source::Setup
8
+ def initial_run_need_restart?
9
+ overinstall?
10
+ end
11
+
12
+ def initial_run_complete_message
13
+ overinstall? ? :all_done : :initial_sync
14
+ end
15
+
16
+ private
17
+
18
+ def overinstall?
19
+ File.exists?(Flydata::SyncFileManager.new(de).source_pos_path)
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ require 'flydata/source/source_pos'
2
+ require 'flydata-core/oracle/source_pos'
3
+
4
+ module Flydata
5
+ module SourceOracle
6
+
7
+ class SourcePos < Source::SourcePos
8
+ def create_source_pos(source_pos_str)
9
+ FlydataCore::Oracle::SourcePos.load(source_pos_str)
10
+ end
11
+
12
+ def resume_pos(source_pos)
13
+ source_pos
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ require 'flydata/source/sync'
2
+
3
+ module Flydata
4
+ module SourceOracle
5
+
6
+ class Sync < Source::Sync
7
+ SOURCE_PREFERENCE_NAME = 'oracle_data_entry_preference'
8
+
9
+ def supported?
10
+ true
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,64 @@
1
+ require 'flydata/source/sync_generate_table_ddl'
2
+ require 'flydata/source_oracle/table_meta'
3
+
4
+ module Flydata
5
+ module SourceOracle
6
+
7
+ class SyncGenerateTableDdl < Source::SyncGenerateTableDdl
8
+ def run_compatibility_check
9
+ # do nothing for now
10
+ end
11
+
12
+ def data_source_type_display_name
13
+ "Oracle database"
14
+ end
15
+
16
+ def data_entry_prefs
17
+ de['oracle_data_entry_preference']
18
+ end
19
+
20
+ def each_source_tabledef(tables, options, &block)
21
+ # Oracle options.
22
+ tables = tables.clone
23
+ if tables.to_s == '' || tables.to_s == '[]'
24
+ raise ArgumentError, "tables is nil or empty"
25
+ end
26
+ _each_tabledef(tables, options, &block)
27
+ end
28
+
29
+ private
30
+
31
+ def table_meta(tables, options)
32
+ Flydata::SourceOracle::TableMeta.new(options, tables)
33
+ end
34
+
35
+ def _each_tabledef(tables, options, &block)
36
+ all_table_meta = options[:table_meta]
37
+ unless all_table_meta
38
+ all_table_meta = table_meta(tables, options)
39
+ all_table_meta.reload
40
+ end
41
+
42
+ missing_tables = []
43
+
44
+ tables.each do |table|
45
+ t_meta = all_table_meta[table]
46
+ unless t_meta && !t_meta.empty?
47
+ missing_tables << table
48
+ next
49
+ end
50
+
51
+ table_def = t_meta[:table_def]
52
+ if table_def
53
+ yield(table_def, nil)
54
+ else
55
+ yield(nil, t_meta[:table_def_err])
56
+ end
57
+ end
58
+
59
+ missing_tables
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,220 @@
1
+ require 'flydata/table_meta'
2
+ require 'flydata-core/oracle/oracle_client'
3
+ require 'flydata-core/table_def/oracle_table_def'
4
+
5
+
6
+ module Flydata
7
+ module SourceOracle
8
+
9
+ # Fetch and keep table meta information
10
+ #
11
+ # <table-name(Symbol)>:
12
+ # table_name: <String> # Table name
13
+ # table_schema: <String> or nil # Schema name
14
+ # primary_keys: <Array of String> # Set primary key names. ex: ['group_id', 'category_id']
15
+ # pk_positions: <Array of Integer> # Set the ordinal position of primary keys. ex: [1, 3]
16
+ # max_row_size: <Integer> # byte, calculated based on column size
17
+ # max_num_rows_per_query>: <Integer> # max number of rows per query
18
+ # raw_columns: <Hash> # raw information schema data
19
+ # columns: table_def.columns
20
+ # table_def:
21
+ # columns:
22
+ # table:
23
+ # column:
24
+ # type:
25
+ # not_null:
26
+ # primary_key:
27
+ # default:
28
+ # column_size: (new) # Set in `OracleTableDef.parse_one_column_def`
29
+ #
30
+ class TableMeta < Flydata::TableMeta
31
+
32
+ GET_TABLE_META_SQL = <<EOS
33
+ SELECT
34
+ tbls.OWNER AS schema
35
+ ,cols.TABLE_NAME AS table_name
36
+ ,cols.COLUMN_NAME AS column_name
37
+ ,cols.COLUMN_ID AS ordinal_position
38
+ ,(CASE
39
+ WHEN pk_cols.CONSTRAINT_NAME IS NULL THEN
40
+ 'f'
41
+ ELSE
42
+ 't'
43
+ END) AS is_primary
44
+ ,pk_cols.CONSTRAINT_NAME AS pk_constraint_name
45
+ ,pk_cols.KEY_ORDINAL AS pk_key_ordinal
46
+ ,(CASE
47
+ WHEN uq_cols.CONSTRAINT_NAME IS NULL THEN
48
+ 'f'
49
+ ELSE
50
+ 't'
51
+ END) AS is_unique
52
+ ,uq_cols.CONSTRAINT_NAME AS uq_constraint_name
53
+ ,uq_cols.KEY_ORDINAL AS uq_key_ordinal
54
+ ,cols.DATA_TYPE AS column_data_type
55
+ ,cols.DATA_DEFAULT AS column_default
56
+ ,cols.NULLABLE AS is_nullable
57
+ ,cols.DATA_LENGTH AS max_length
58
+ ,cols.DATA_PRECISION AS precision
59
+ ,cols.DATA_SCALE AS scale
60
+ FROM
61
+ (SELECT * FROM ALL_TABLES WHERE OWNER = %{schema}) tbls
62
+ INNER JOIN ALL_TAB_COLUMNS cols
63
+ ON cols.OWNER = tbls.OWNER
64
+ AND cols.TABLE_NAME = tbls.TABLE_NAME
65
+ LEFT OUTER JOIN (
66
+ SELECT
67
+ cons_inner.CONSTRAINT_NAME AS constraint_name
68
+ ,cons_columns_inner.POSITION AS key_ordinal
69
+ ,tbls_inner.TABLE_NAME AS table_name
70
+ ,cols_inner.COLUMN_NAME AS col_name
71
+ ,cols_inner.COLUMN_ID AS col_id
72
+ FROM
73
+ (SELECT * FROM ALL_TABLES WHERE OWNER = %{schema}) tbls_inner
74
+ INNER JOIN ALL_TAB_COLUMNS cols_inner
75
+ ON cols_inner.OWNER = tbls_inner.OWNER
76
+ AND cols_inner.TABLE_NAME = tbls_inner.TABLE_NAME
77
+ INNER JOIN ALL_CONSTRAINTS cons_inner
78
+ ON cons_inner.OWNER = cols_inner.OWNER
79
+ AND cons_inner.CONSTRAINT_TYPE = 'P'
80
+ AND cons_inner.TABLE_NAME = tbls_inner.TABLE_NAME
81
+ INNER JOIN ALL_CONS_COLUMNS cons_columns_inner
82
+ ON cons_columns_inner.OWNER = cons_inner.OWNER
83
+ AND cons_columns_inner.CONSTRAINT_NAME = cons_inner.CONSTRAINT_NAME
84
+ AND cons_columns_inner.TABLE_NAME = cons_inner.TABLE_NAME
85
+ AND cons_columns_inner.COLUMN_NAME = cols_inner.COLUMN_NAME
86
+ ) pk_cols
87
+ ON pk_cols.TABLE_NAME = cols.TABLE_NAME
88
+ AND pk_cols.COL_NAME = cols.COLUMN_NAME
89
+ AND pk_cols.COL_ID = cols.COLUMN_ID
90
+ LEFT OUTER JOIN (
91
+ SELECT
92
+ cons_inner.CONSTRAINT_NAME AS constraint_name
93
+ ,cons_columns_inner.POSITION AS key_ordinal
94
+ ,tbls_inner.TABLE_NAME AS table_name
95
+ ,cols_inner.COLUMN_NAME AS col_name
96
+ ,cols_inner.COLUMN_ID AS col_id
97
+ FROM
98
+ (SELECT * FROM ALL_TABLES WHERE OWNER = %{schema}) tbls_inner
99
+ INNER JOIN ALL_TAB_COLUMNS cols_inner
100
+ ON cols_inner.OWNER = tbls_inner.OWNER
101
+ AND cols_inner.TABLE_NAME = tbls_inner.TABLE_NAME
102
+ INNER JOIN ALL_CONSTRAINTS cons_inner
103
+ ON cons_inner.OWNER = cols_inner.OWNER
104
+ AND cons_inner.CONSTRAINT_TYPE = 'U'
105
+ AND cons_inner.TABLE_NAME = tbls_inner.TABLE_NAME
106
+ INNER JOIN ALL_CONS_COLUMNS cons_columns_inner
107
+ ON cons_columns_inner.OWNER = cons_inner.OWNER
108
+ AND cons_columns_inner.CONSTRAINT_NAME = cons_inner.CONSTRAINT_NAME
109
+ AND cons_columns_inner.TABLE_NAME = cons_inner.TABLE_NAME
110
+ AND cons_columns_inner.COLUMN_NAME = cols_inner.COLUMN_NAME
111
+ ) uq_cols
112
+ ON uq_cols.TABLE_NAME = cols.TABLE_NAME
113
+ AND uq_cols.COL_NAME = cols.COLUMN_NAME
114
+ AND uq_cols.COL_ID = cols.COLUMN_ID
115
+ ORDER BY
116
+ table_name, ordinal_position
117
+ EOS
118
+
119
+
120
+ GET_CURRENT_SCN_SQL = "SELECT current_scn from v$database"
121
+
122
+ DEFAULT_MAX_FETCH_RECORD_SIZE = 50000
123
+ #DEFAULT_MAX_FETCH_RECORD_SIZE = 8
124
+
125
+ def initialize(options, tables, schema = nil)
126
+ @options = options
127
+ @database = options[:dbname] || options[:database] || options['database']
128
+ @tables = tables
129
+ username = options[:username] || options['username']
130
+ @schema = (schema || options[:schema] || options['schema'] || username).to_s.strip.upcase
131
+ end
132
+
133
+ attr_reader :current_scn
134
+
135
+ def reload(oracle_client = nil)
136
+ schema = {
137
+ database: "'#{@database}'",
138
+ schema: "'#{@schema}'",
139
+ tables: @tables.collect{|t| "'#{t}'"}.join(','),
140
+ }
141
+
142
+ conn = oracle_client
143
+ if conn.nil?
144
+ local_conn = conn = FlydataCore::Oracle::OracleClient.new(@options)
145
+ end
146
+
147
+ sql = GET_TABLE_META_SQL % schema
148
+
149
+ # Set table_meta
150
+ columns = conn.query(sql)
151
+ @table_meta = build_table_meta(columns)
152
+
153
+ # Set current scn
154
+ current_scn_str = conn.query(GET_CURRENT_SCN_SQL).fetch.first
155
+
156
+ raise "Invalid scn is retrieved" if current_scn_str.nil?
157
+ @current_scn = current_scn_str.to_i
158
+
159
+ self
160
+ ensure
161
+ local_conn.close rescue nil if local_conn
162
+ end
163
+
164
+ def build_table_meta(columns)
165
+ ret = Hash.new{|h,k| h[k]={} }
166
+
167
+ # Put ret[<table-name-sym>][:raw_columns]
168
+ while (col = columns.fetch_hash)
169
+ column_name = col['COLUMN_NAME'].to_sym
170
+ table_name = col['TABLE_NAME'].to_sym
171
+ t_meta = ret[table_name]
172
+ t_meta[:raw_columns] = Hash.new {|h,k| h[k] = []} unless t_meta[:raw_columns]
173
+ t_meta[:raw_columns][column_name] << col
174
+ end
175
+
176
+ ret.each do |table_name, t_meta|
177
+ begin
178
+ table_def = FlydataCore::TableDef::OracleTableDef.create(
179
+ t_meta[:raw_columns].values, @options)
180
+ rescue FlydataCore::TableDefError => e
181
+ t_meta.merge!(
182
+ table_name: table_name,
183
+ table_def_err: e,
184
+ )
185
+ # Skip when getting an error when parsing the columns
186
+ next
187
+ end
188
+
189
+ primary_keys = []
190
+ pk_positions = []
191
+ table_def.columns.each.with_index(1) do |col, index|
192
+ col_name = col[:column]
193
+ if col["CONSTRAINT_TYPE"] == 'P'
194
+ primary_keys << col_name
195
+ pk_positions << index.to_s
196
+ end
197
+ end
198
+
199
+ t_meta.merge!(
200
+ table_name: table_name.to_s,
201
+ table_schema: @schema,
202
+ primary_keys: primary_keys,
203
+ pk_positions: pk_positions,
204
+ max_num_rows_per_query: DEFAULT_MAX_FETCH_RECORD_SIZE,
205
+ columns: table_def.columns,
206
+ table_def: table_def,
207
+ )
208
+ end
209
+
210
+ ret
211
+ end
212
+
213
+ def calc_column_size(column)
214
+ #TODO: Implement the check logic based on column type
215
+ 124
216
+ end
217
+ end
218
+
219
+ end
220
+ end