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.
- 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
|