flydata 0.7.12 → 0.7.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/VERSION +1 -1
- data/flydata-core/lib/flydata-core/oracle/config.rb +25 -0
- data/flydata-core/lib/flydata-core/oracle/oracle_client.rb +48 -0
- data/flydata-core/lib/flydata-core/oracle/query_helper.rb +20 -0
- data/flydata-core/lib/flydata-core/oracle/source_pos.rb +63 -0
- data/flydata-core/lib/flydata-core/table_def/oracle_table_def.rb +167 -0
- data/flydata-core/spec/oracle/config_spec.rb +45 -0
- data/flydata-core/spec/oracle/source_pos_spec.rb +101 -0
- data/flydata.gemspec +0 -0
- data/lib/flydata/command/sync.rb +14 -4
- data/lib/flydata/source.rb +1 -0
- data/lib/flydata/source/sync_repair.rb +25 -0
- data/lib/flydata/source_mysql/generate_source_dump.rb +2 -1
- data/lib/flydata/source_mysql/mysql_accessible.rb +30 -0
- data/lib/flydata/source_mysql/parser/dump_parser.rb +0 -40
- data/lib/flydata/source_mysql/sync_database_size_check.rb +29 -0
- data/lib/flydata/source_mysql/sync_repair.rb +26 -0
- data/lib/flydata/source_oracle/data_entry.rb +24 -0
- data/lib/flydata/source_oracle/generate_source_dump.rb +184 -0
- data/lib/flydata/source_oracle/oracle_component.rb +12 -0
- data/lib/flydata/source_oracle/parse_dump_and_send.rb +128 -0
- data/lib/flydata/source_oracle/plugin_support/context.rb +13 -0
- data/lib/flydata/source_oracle/plugin_support/source_position_file.rb +14 -0
- data/lib/flydata/source_oracle/query_based_sync/diff_query_generator.rb +122 -0
- data/lib/flydata/source_oracle/setup.rb +24 -0
- data/lib/flydata/source_oracle/source_pos.rb +18 -0
- data/lib/flydata/source_oracle/sync.rb +15 -0
- data/lib/flydata/source_oracle/sync_generate_table_ddl.rb +64 -0
- data/lib/flydata/source_oracle/table_meta.rb +220 -0
- data/lib/flydata/source_postgresql/sync_repair.rb +13 -0
- data/spec/flydata/source_mysql/generate_source_dump_spec.rb +2 -2
- metadata +27 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30343349c710d665850b71f57d39308b6db3ac9c
|
4
|
+
data.tar.gz: 5c4ecdd659a4dc2e5a20bd12746bd2903315e71d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cffa5e91a09f5a7e9091dcd198db4e15d3de9c9666d753b888e06b1a8735de494371733353b1503f07a0dff41c8704ed29b8b143177d46649397f6580e7af548
|
7
|
+
data.tar.gz: c13cb4a781ab2a263a9c88f52700a4d21e8ee11c1ab38b5c92620f2718846eae13c28394d9714e20ff58b6b81c6c8eb9611a764e00567541f33f83b91fdef0db
|
data/Gemfile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.13
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module FlydataCore
|
2
|
+
module Oracle
|
3
|
+
class Config
|
4
|
+
def self.build_oci_uri(db_conf)
|
5
|
+
db_opts = [:uri,
|
6
|
+
:host,
|
7
|
+
:port,
|
8
|
+
:username,
|
9
|
+
:password,
|
10
|
+
:database,
|
11
|
+
].inject({}) { |h, sym|
|
12
|
+
if db_conf.has_key?(sym)
|
13
|
+
h[sym] = db_conf[sym]
|
14
|
+
elsif db_conf[sym.to_s]
|
15
|
+
h[sym] = db_conf[sym.to_s]
|
16
|
+
end
|
17
|
+
h
|
18
|
+
}
|
19
|
+
|
20
|
+
db_opts[:uri] ||
|
21
|
+
"#{db_opts[:username]}/#{db_opts[:password]}@#{db_opts[:host]}:#{db_opts[:port]}/#{db_opts[:database]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'oci8'
|
2
|
+
require 'flydata-core/oracle/config'
|
3
|
+
|
4
|
+
module FlydataCore
|
5
|
+
module Oracle
|
6
|
+
|
7
|
+
class OracleClient
|
8
|
+
def initialize(dbconf)
|
9
|
+
@dbconf = dbconf
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :dbconf
|
13
|
+
|
14
|
+
def establish_connection
|
15
|
+
@conn = create_connection if @conn.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def query(query, params = {})
|
19
|
+
establish_connection
|
20
|
+
|
21
|
+
cursor = @conn.parse(query)
|
22
|
+
case params
|
23
|
+
when Hash
|
24
|
+
params.each {|k, value| cursor.bind_param(k, value) }
|
25
|
+
when Array
|
26
|
+
params.each.with_index(1) {|value, i| cursor.bind_param(i, value) }
|
27
|
+
end
|
28
|
+
cursor.exec
|
29
|
+
cursor
|
30
|
+
end
|
31
|
+
|
32
|
+
def close
|
33
|
+
if @conn
|
34
|
+
@conn.logoff
|
35
|
+
@conn = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def create_connection
|
42
|
+
uri = FlydataCore::Oracle::Config.build_oci_uri(@dbconf)
|
43
|
+
OCI8.new(uri)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FlydataCore
|
2
|
+
module Oracle
|
3
|
+
|
4
|
+
class QueryHelper
|
5
|
+
# Set default schema if a schmema name is not given
|
6
|
+
def self.schema_as_value(schema, user)
|
7
|
+
s = schema.to_s.strip
|
8
|
+
if s.empty?
|
9
|
+
s = user.to_s.strip
|
10
|
+
end
|
11
|
+
"'#{s.upcase}'"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.tables_as_value(tables)
|
15
|
+
tables.collect{|t| "'#{t.upcase}'"}.join(",")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module FlydataCore
|
4
|
+
module Oracle
|
5
|
+
|
6
|
+
class SourcePos
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
# Source Position data for Oracle
|
10
|
+
#
|
11
|
+
# Use system change number (SCN) for managing a consitent check point in
|
12
|
+
# Oracle database. SCN is a unsigned integer value.
|
13
|
+
def initialize(scn_or_obj, pk_values = nil)
|
14
|
+
if scn_or_obj.kind_of?(self.class)
|
15
|
+
scn_or_obj.tap do |s|
|
16
|
+
@scn = s.scn
|
17
|
+
@pk_values = s.pk_values
|
18
|
+
end
|
19
|
+
else
|
20
|
+
@scn = scn_or_obj.to_i if scn_or_obj
|
21
|
+
@pk_values = pk_values # must be array or nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :scn
|
26
|
+
attr_reader :pk_values
|
27
|
+
|
28
|
+
def empty?
|
29
|
+
@scn.to_s.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
pk_values = @pk_values ? @pk_values.to_json : ''
|
34
|
+
"#{@scn}\t#{pk_values}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def <=>(other)
|
38
|
+
if @scn != other.scn
|
39
|
+
return @scn <=> other.scn
|
40
|
+
elsif @pk_values.nil? && !other.pk_values.nil?
|
41
|
+
1
|
42
|
+
elsif !@pk_values.nil? && other.pk_values.nil?
|
43
|
+
-1
|
44
|
+
elsif @pk_values == other.pk_values
|
45
|
+
0
|
46
|
+
else
|
47
|
+
@pk_values.to_s <=> other.pk_values.to_s
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.load(str)
|
52
|
+
scn, pk_values = str.split("\t").collect{|v| v.strip}
|
53
|
+
pk_values = if pk_values.to_s.empty?
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
JSON.parse(pk_values)
|
57
|
+
end
|
58
|
+
self.new(scn, pk_values)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'flydata-core/table_def/base'
|
2
|
+
|
3
|
+
module FlydataCore
|
4
|
+
module TableDef
|
5
|
+
|
6
|
+
class OracleTableDef < Base
|
7
|
+
|
8
|
+
VALUE_CONVERTERS = {}
|
9
|
+
|
10
|
+
#TODO: Check supported data format one by one
|
11
|
+
TYPE_MAP_O2F = {
|
12
|
+
'NUMBER' => {type: 'int8'},
|
13
|
+
'BIGINT' => {type: 'int8'},
|
14
|
+
'INT8' => {type: 'int8'},
|
15
|
+
'SERIAL8' => {type: 'serial8'},
|
16
|
+
'CHARACTER' => {type: 'varchar', width_attrs:["MAX_LENGTH"], def_width:[1]},
|
17
|
+
'CHARACTER varying' => {type: 'varchar',
|
18
|
+
width_attrs:["MAX_LENGTH"],
|
19
|
+
def_width:[1]},
|
20
|
+
'VARCHAR' => {type: 'varchar',
|
21
|
+
width_attrs:["MAX_LENGTH"],
|
22
|
+
def_width:[1]},
|
23
|
+
'VARCHAR2' => {type: 'varchar',
|
24
|
+
width_attrs:["MAX_LENGTH"],
|
25
|
+
def_width:[1]},
|
26
|
+
'DATE' => {type: 'date'},
|
27
|
+
'DOUBLE PRECISION' => {type: 'float8'},
|
28
|
+
'FLOAT8' => {type: 'float8'},
|
29
|
+
'INTEGER' => {type: 'int4'},
|
30
|
+
'INT' => {type: 'int4'},
|
31
|
+
'INT4' => {type: 'int4'},
|
32
|
+
'NUMERIC' => {type: 'numeric',
|
33
|
+
width_attrs:["PRECISION", "SCALE"]
|
34
|
+
#can be no width values
|
35
|
+
},
|
36
|
+
'REAL' => {type: 'float4'},
|
37
|
+
'FLOAT4' => {type: 'float4'},
|
38
|
+
'SMALLINT' => {type: 'int2'},
|
39
|
+
'INT2' => {type: 'int2'},
|
40
|
+
'SMALLSERIAL' => {type: 'serial2'},
|
41
|
+
'TEXT' => {type: 'text'},
|
42
|
+
'TIMESTAMP' => {type: 'datetime'},
|
43
|
+
:default => {type: '_unsupported'},
|
44
|
+
}
|
45
|
+
|
46
|
+
def self.convert_to_flydata_type(information_schema_columns)
|
47
|
+
ora_type = information_schema_columns["COLUMN_DATA_TYPE"]
|
48
|
+
raise "Unknown Oracle type or internal error. type:#{ora_type}" unless ora_type
|
49
|
+
unless TYPE_MAP_O2F.has_key?(ora_type)
|
50
|
+
ora_type = :default
|
51
|
+
end
|
52
|
+
type_hash = TYPE_MAP_O2F[ora_type]
|
53
|
+
flydata_type = type_hash[:type]
|
54
|
+
ret_type = flydata_type
|
55
|
+
|
56
|
+
width_values = get_width_values(information_schema_columns, type_hash)
|
57
|
+
if width_values
|
58
|
+
ret_type += "(#{width_values.join(",")})"
|
59
|
+
end
|
60
|
+
|
61
|
+
if type_hash[:override]
|
62
|
+
ret_type = type_hash[:override].call(ret_type, ora_type, flydata_type)
|
63
|
+
end
|
64
|
+
ret_type
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def self.get_width_values(information_schema_columns, type_hash)
|
70
|
+
values = []
|
71
|
+
if type_hash.has_key?(:width_attrs)
|
72
|
+
values = type_hash[:width_attrs].collect{|attr| information_schema_columns[attr].to_i }
|
73
|
+
end
|
74
|
+
|
75
|
+
if type_hash.has_key?(:def_width)
|
76
|
+
if values.nil? || values.size != type_hash[:def_width].size
|
77
|
+
raise "The number of the default values must match the number of width attributes. column:#{information_schema_columns[:column_name]} type:#{type_hash[:type]} def_width:#{type_hash[:def_width].inspect} width_attrs:#{type_hash[:width_attrs].inspect}"
|
78
|
+
end
|
79
|
+
values = values.each_with_index.collect {|v, i| v ? v : type_hash[:def_width][i]}
|
80
|
+
end
|
81
|
+
values.pop until values.empty? || values.last # remove trailing nil
|
82
|
+
if values.any?{|v| v.nil?}
|
83
|
+
raise "nil value is not allowed"
|
84
|
+
end
|
85
|
+
values.empty? ? nil : values
|
86
|
+
end
|
87
|
+
|
88
|
+
def self._create(information_schema_columns, options)
|
89
|
+
table_def = information_schema_columns.collect {|iscol| iscol.first}.inspect
|
90
|
+
table_name = nil
|
91
|
+
columns = []
|
92
|
+
column_def = {}
|
93
|
+
# TODO: Set UTF-8 to NLS_LANG whne creating a connection
|
94
|
+
# Otherwise the user will see "Warning: NLS_LANG is not set. fallback to US7ASCII"
|
95
|
+
#
|
96
|
+
# SourceOracle uses UTF8 client encoding no matter what the server
|
97
|
+
# encoding is.
|
98
|
+
default_charset = 'UTF_8'
|
99
|
+
default_charset_oracle = 'UTF8'
|
100
|
+
comment = nil
|
101
|
+
unique_keys_hash = Hash.new {|h, k| h[k] = []}
|
102
|
+
|
103
|
+
information_schema_columns.each do |iscol_arr|
|
104
|
+
# An iscol_arr represents a column. Column information in all elements in iscol_arr is the same.
|
105
|
+
# Only difference between elements is index information.
|
106
|
+
iscol = iscol_arr.first
|
107
|
+
column = parse_one_column_def(iscol)
|
108
|
+
if table_name
|
109
|
+
unless table_name == column[:table]
|
110
|
+
raise "Table name must match through all columns. Got `#{table_name}` and `#{column[:table]}`"
|
111
|
+
end
|
112
|
+
else
|
113
|
+
table_name = column[:table]
|
114
|
+
end
|
115
|
+
columns << column
|
116
|
+
coldef = iscol.dup.tap{|c|
|
117
|
+
c.each{|k, v| c[k] = v.to_i if v.kind_of?(BigDecimal) }
|
118
|
+
}.inspect
|
119
|
+
column_def[column[:column]] = coldef
|
120
|
+
|
121
|
+
# TODO: Handle unique keys
|
122
|
+
#iscol_arr.each do |iscol|
|
123
|
+
# # gather information for unique keys that this column belongs to.
|
124
|
+
# if iscol['IS_PRIMARY'] == 'f' && iscol['IS_UNIQUE'] == 't'
|
125
|
+
# unique_keys_hash[iscol['CONSTRAINT_NAME'].to_sym] << iscol['COLUMN_NAME']
|
126
|
+
# end
|
127
|
+
#end
|
128
|
+
end
|
129
|
+
unique_keys = unique_keys_hash.values
|
130
|
+
|
131
|
+
[table_def, table_name, columns, column_def, unique_keys, default_charset, default_charset_oracle, comment]
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.parse_one_column_def(information_schema_column)
|
135
|
+
column = {}
|
136
|
+
column[:table] = information_schema_column["TABLE_NAME"]
|
137
|
+
column[:column] = information_schema_column["COLUMN_NAME"]
|
138
|
+
column[:type] = convert_to_flydata_type(information_schema_column)
|
139
|
+
column[:not_null] = true if information_schema_column["IS_NULLABLE"] == "N"
|
140
|
+
column[:primary_key] = true if information_schema_column["IS_PRIMARY"] == "t"
|
141
|
+
column[:default] = case column[:type]
|
142
|
+
when 'boolean'
|
143
|
+
to_boolean(information_schema_column["COLUMN_DEFAULT"])
|
144
|
+
when '_unsupported'
|
145
|
+
# omit default value
|
146
|
+
nil
|
147
|
+
else
|
148
|
+
information_schema_column["COLUMN_DEFAULT"] # TODO nil handling
|
149
|
+
end
|
150
|
+
column
|
151
|
+
end
|
152
|
+
|
153
|
+
#TODO: Revisit how to handle boolean default value
|
154
|
+
def self.to_boolean(col_value)
|
155
|
+
return nil if col_value.nil?
|
156
|
+
# Catch all possible literal for boolean type in Oracle.
|
157
|
+
# Actual col_value coming in here is:
|
158
|
+
# 'true' or 'false' for default value
|
159
|
+
# 't' or 'f' for column value
|
160
|
+
return true if col_value.to_s =~ /^(t|true|y|yes|on|1)$/i
|
161
|
+
return false if col_value.to_s =~ /^(f|false|n|no|off|0)$/i
|
162
|
+
raise "Invalid default value for Oracle boolean type:`#{col_value}`"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata-core/oracle/config'
|
3
|
+
|
4
|
+
module FlydataCore
|
5
|
+
module Oracle
|
6
|
+
describe Config do
|
7
|
+
describe '.build_oci_uri' do
|
8
|
+
subject { described_class.build_oci_uri(conf) }
|
9
|
+
|
10
|
+
context 'with uri conf' do
|
11
|
+
let(:expected_uri) { "admin/welcome@localhost:1521/ORCL" }
|
12
|
+
let(:conf) {
|
13
|
+
{
|
14
|
+
uri: expected_uri,
|
15
|
+
host: 'localhost',
|
16
|
+
port: 1234,
|
17
|
+
username: 'testuser',
|
18
|
+
password: 'password',
|
19
|
+
database: 'testdb',
|
20
|
+
user: 'testuser',
|
21
|
+
dbname: 'testdb',
|
22
|
+
}
|
23
|
+
}
|
24
|
+
it { is_expected.to eq(expected_uri) }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with conf having dbname' do
|
28
|
+
let(:expected_uri) { "testuser/password@localhost:1234/testdb" }
|
29
|
+
let(:conf) {
|
30
|
+
{
|
31
|
+
host: 'localhost',
|
32
|
+
port: 1234,
|
33
|
+
username: 'testuser',
|
34
|
+
password: 'password',
|
35
|
+
database: 'testdb',
|
36
|
+
user: 'testuser',
|
37
|
+
dbname: 'testdb',
|
38
|
+
}
|
39
|
+
}
|
40
|
+
it { is_expected.to eq(expected_uri) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'flydata-core/oracle/source_pos'
|
2
|
+
|
3
|
+
module FlydataCore
|
4
|
+
module Oracle
|
5
|
+
|
6
|
+
describe SourcePos do
|
7
|
+
let(:subject_object) { described_class.new(scn, pk_values) }
|
8
|
+
|
9
|
+
let(:scn) { "123456" }
|
10
|
+
let(:pk_values) { nil }
|
11
|
+
|
12
|
+
describe '#initialize' do
|
13
|
+
context 'with scn and pk_values' do
|
14
|
+
it { expect(subject_object.scn).to eq(scn.to_i) }
|
15
|
+
it { expect(subject_object.pk_values).to eq(pk_values) }
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'with source pos object' do
|
19
|
+
let(:scn) { '123456' }
|
20
|
+
let(:pk_values) { [{'id' => '1000'}] }
|
21
|
+
let(:obj) { described_class.new(scn, pk_values) }
|
22
|
+
subject { described_class.new(obj) }
|
23
|
+
|
24
|
+
it { expect(subject.scn).to eq(scn.to_i) }
|
25
|
+
it { expect(subject.pk_values).to eq(pk_values) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#to_s' do
|
30
|
+
subject { subject_object.to_s }
|
31
|
+
|
32
|
+
context 'when pk_values is nil' do
|
33
|
+
let(:pk_values) { nil }
|
34
|
+
it { is_expected.to eq %Q|#{scn}\t| }
|
35
|
+
end
|
36
|
+
context 'when pk_values is not nil' do
|
37
|
+
let(:pk_values) { { "user_id" => 1, "address_id" => 3 } }
|
38
|
+
it { is_expected.to eq %Q|#{scn}\t{"user_id":1,"address_id":3}| }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '.load' do
|
43
|
+
subject { described_class.load(str) }
|
44
|
+
|
45
|
+
context 'without pk_values' do
|
46
|
+
let(:str) { %Q|#{scn}\t| }
|
47
|
+
it { is_expected.to eq described_class.new(scn) }
|
48
|
+
end
|
49
|
+
context 'with pk_values' do
|
50
|
+
let(:str) { %Q|#{scn}\t{"user_id":1,"address_id":3}| }
|
51
|
+
it do
|
52
|
+
is_expected.to eq described_class.new(scn,
|
53
|
+
"user_id" => 1, "address_id" => 3)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#<=>' do
|
59
|
+
def src_pos(scn, pk_values = nil)
|
60
|
+
described_class.new(scn, pk_values)
|
61
|
+
end
|
62
|
+
context 'when both have no pk_values' do
|
63
|
+
it { expect(src_pos('1111') == src_pos('1111')).to be(true) }
|
64
|
+
it { expect(src_pos('1111') == src_pos('1112')).to be(false) }
|
65
|
+
it { expect(src_pos('1111') < src_pos('1112')).to be(true) }
|
66
|
+
it { expect(src_pos('1111') < src_pos('1110')).to be(false) }
|
67
|
+
it { expect(src_pos('1111') > src_pos('1110')).to be(true) }
|
68
|
+
it { expect(src_pos('1111') > src_pos('1112')).to be(false) }
|
69
|
+
it { expect(src_pos('1111') > src_pos('999')).to be(true) }
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'when one of them has pk_values' do
|
73
|
+
let(:pkv) { [{'id'=>'10'}] }
|
74
|
+
it { expect(src_pos('1111',pkv) == src_pos('1111')).to be(false) }
|
75
|
+
it { expect(src_pos('1111',pkv) == src_pos('1112')).to be(false) }
|
76
|
+
it { expect(src_pos('1111',pkv) < src_pos('1111')).to be(true) }
|
77
|
+
it { expect(src_pos('1111',pkv) < src_pos('1112')).to be(true) }
|
78
|
+
it { expect(src_pos('1111',pkv) < src_pos('1110')).to be(false) }
|
79
|
+
it { expect(src_pos('1111',pkv) > src_pos('1110')).to be(true) }
|
80
|
+
it { expect(src_pos('1111',pkv) > src_pos('1112')).to be(false) }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when both have pk_values' do
|
84
|
+
let(:pkv1) { [{'id'=>'10'}] }
|
85
|
+
let(:pkv2) { [{'name'=>'akira'}] }
|
86
|
+
it { expect(src_pos('1111',pkv1) == src_pos('1111',pkv1)).to be(true) }
|
87
|
+
it { expect(src_pos('1111',pkv1) == src_pos('1111',pkv2)).to be(false) }
|
88
|
+
it { expect(src_pos('1111',pkv1) == src_pos('1112',pkv2)).to be(false) }
|
89
|
+
it { expect(src_pos('1111',pkv1) < src_pos('1111',pkv2)).to be(true) }
|
90
|
+
it { expect(src_pos('1111',pkv1) < src_pos('1111',pkv1)).to be(false) }
|
91
|
+
it { expect(src_pos('1111',pkv1) < src_pos('1112',pkv2)).to be(true) }
|
92
|
+
it { expect(src_pos('1111',pkv1) < src_pos('1110',pkv2)).to be(false) }
|
93
|
+
it { expect(src_pos('1111',pkv1) > src_pos('1110',pkv2)).to be(true) }
|
94
|
+
it { expect(src_pos('1111',pkv1) > src_pos('1112',pkv2)).to be(false) }
|
95
|
+
it { expect(src_pos('1111',pkv1) > src_pos('1111',pkv2)).to be(false) }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|