flydata 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/ext/flydata/source_mysql/parser/sql_parser.cpp +25 -25
- data/flydata-core/lib/flydata-core/postgresql/compatibility_checker.rb +4 -9
- data/flydata-core/lib/flydata-core/postgresql/pg_client.rb +1 -0
- data/flydata-core/lib/flydata-core/postgresql/query_helper.rb +17 -0
- data/flydata-core/lib/flydata-core/postgresql/snapshot.rb +11 -0
- data/flydata-core/lib/flydata-core/postgresql/source_pos.rb +4 -0
- data/flydata-core/lib/flydata-core/table_def/postgresql_table_def.rb +6 -6
- data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +25 -26
- data/flydata-core/lib/flydata-core/table_def/value_conv.rb +29 -0
- data/flydata-core/spec/postgresql/compatibility_checker_spec.rb +3 -3
- data/flydata-core/spec/postgresql/query_helper_spec.rb +29 -0
- data/flydata-core/spec/postgresql/snapshot_spec.rb +25 -0
- data/flydata-core/spec/table_def/redshift_table_def_spec.rb +59 -7
- data/flydata-core/spec/table_def/value_conv_spec.rb +33 -0
- data/flydata.gemspec +0 -0
- data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +1 -1
- data/lib/flydata/source_mysql/data_entry.rb +1 -0
- data/lib/flydata/source_postgresql/command/psql.rb +48 -0
- data/lib/flydata/source_postgresql/data_entry.rb +1 -0
- data/lib/flydata/source_postgresql/generate_source_dump.rb +10 -7
- data/lib/flydata/source_postgresql/query_based_sync/diff_query_generator.rb +9 -2
- data/lib/flydata/source_postgresql/table_meta.rb +7 -3
- data/spec/flydata/fluent-plugins/in_mysql_binlog_flydata_spec.rb +1 -0
- data/spec/flydata/fluent-plugins/in_postgresql_query_based_flydata_spec.rb +2 -1
- data/spec/flydata/source_postgresql/query_based_sync/diff_query_generator_spec.rb +45 -11
- data/spec/flydata/source_postgresql/table_meta_spec.rb +19 -0
- data/tmpl/redshift_postgresql_data_entry.conf.tmpl +13 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c1e7d064581a7d76441fa77bc814c1ebba986cd
|
4
|
+
data.tar.gz: bb14ff1bbf83246092ddbc44332dadc42725e1f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3fee3b20cdb7ef01f45a05d3485e637fcb794c15c4bb31e2027535241db6ee31de9c7b97e8b5a4ab1892b415a1a26a336c525d8a9a19b960c2d4b5e5677006e
|
7
|
+
data.tar.gz: 477339f6ca63c80d13a9d499e062c5e42cae23a99a24d058f37fdbea01829084c9f137e7c00baa88bc56ff5389fd858119fc2c4ce1583358505e9dd7bfac4d05
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.1
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#include <iostream>
|
2
2
|
#include "sql_parser.h"
|
3
3
|
|
4
|
-
|
4
|
+
#define DEBUG 0
|
5
5
|
|
6
6
|
enum status_t {
|
7
7
|
start,
|
@@ -54,7 +54,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
54
54
|
break;
|
55
55
|
|
56
56
|
case value:
|
57
|
-
if (
|
57
|
+
if (DEBUG) std::cout << "value[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
58
58
|
if (*current == '\'') {
|
59
59
|
status = first_char;
|
60
60
|
} else if (*current == 'N') {
|
@@ -76,7 +76,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
76
76
|
break;
|
77
77
|
|
78
78
|
case first_zero:
|
79
|
-
if (
|
79
|
+
if (DEBUG) std::cout << "first_zero[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
80
80
|
if (*current == ',') {
|
81
81
|
ch.value_callback(mark, value_len - 1, true);//value_callback :end_value
|
82
82
|
mark = 0; value_len = 0; //mark_reset
|
@@ -102,7 +102,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
102
102
|
break;
|
103
103
|
|
104
104
|
case leading_zero:
|
105
|
-
if (
|
105
|
+
if (DEBUG) std::cout << "leading_zero[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
106
106
|
if (*current == ',') {
|
107
107
|
ch.value_callback(mark, value_len - 1, true);//value_callback :end_value
|
108
108
|
mark = 0; value_len = 0; //mark_reset
|
@@ -126,7 +126,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
126
126
|
break;
|
127
127
|
|
128
128
|
case number:
|
129
|
-
if (
|
129
|
+
if (DEBUG) std::cout << "number[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
130
130
|
if ((*current >= '0' && *current <= '9') || *current == '.' || *current == 'e' || *current == 'E' || *current == '-') {
|
131
131
|
status = number;
|
132
132
|
} else if (*current == ',') {
|
@@ -144,7 +144,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
144
144
|
break;
|
145
145
|
|
146
146
|
case null_n:
|
147
|
-
if (
|
147
|
+
if (DEBUG) std::cout << "null_n[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
148
148
|
if (*current == 'U') {
|
149
149
|
status = null_u;
|
150
150
|
} else {
|
@@ -153,7 +153,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
153
153
|
break;
|
154
154
|
|
155
155
|
case null_u:
|
156
|
-
if (
|
156
|
+
if (DEBUG) std::cout << "null_u[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
157
157
|
if (*current == 'L') {
|
158
158
|
status = null_l1;
|
159
159
|
} else {
|
@@ -162,7 +162,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
162
162
|
break;
|
163
163
|
|
164
164
|
case null_l1:
|
165
|
-
if (
|
165
|
+
if (DEBUG) std::cout << "null_l1[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
166
166
|
if (*current == 'L') {
|
167
167
|
status = null_l2;
|
168
168
|
} else {
|
@@ -171,7 +171,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
171
171
|
break;
|
172
172
|
|
173
173
|
case null_l2:
|
174
|
-
if (
|
174
|
+
if (DEBUG) std::cout << "null_l2[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
175
175
|
if (*current == ',') {
|
176
176
|
//# 0 is NULL pointer
|
177
177
|
ch.value_callback(0, 0, true);//value_callback 0 :end_value
|
@@ -189,7 +189,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
189
189
|
break;
|
190
190
|
|
191
191
|
case hex_blob:
|
192
|
-
if (
|
192
|
+
if (DEBUG) std::cout << "hex_blob[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
193
193
|
if ((*current >= '0' && *current <= '9') ||
|
194
194
|
(*current >= 'A' && *current <= 'F') ||
|
195
195
|
(*current >= 'a' && *current <= 'f')) {
|
@@ -200,7 +200,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
200
200
|
break;
|
201
201
|
|
202
202
|
case hex_blob_number:
|
203
|
-
if (
|
203
|
+
if (DEBUG) std::cout << "hex_blob_number[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
204
204
|
if ((*current >= '0' && *current <= '9') ||
|
205
205
|
(*current >= 'A' && *current <= 'F') ||
|
206
206
|
(*current >= 'a' && *current <= 'f')) {
|
@@ -220,7 +220,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
220
220
|
break;
|
221
221
|
|
222
222
|
case first_char:
|
223
|
-
if (
|
223
|
+
if (DEBUG) std::cout << "first_char[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
224
224
|
if (*current == '\'') {
|
225
225
|
mark = current; value_len = 1; //mark
|
226
226
|
ch.value_callback(mark, value_len - 1, true);//value_callback, :end_value
|
@@ -248,7 +248,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
248
248
|
break;
|
249
249
|
|
250
250
|
case utf8_2_firstbyte:
|
251
|
-
if (
|
251
|
+
if (DEBUG) std::cout << "utf8_2_firstbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
252
252
|
if ((*current & 0xc0) == 0x80) {
|
253
253
|
temp_mark = 0; //temp_mark reset)
|
254
254
|
status = following_char;
|
@@ -262,7 +262,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
262
262
|
break;
|
263
263
|
|
264
264
|
case utf8_3_firstbyte:
|
265
|
-
if (
|
265
|
+
if (DEBUG) std::cout << "utf8_3_firstbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
266
266
|
if ((*current & 0xc0) == 0x80) {
|
267
267
|
status = utf8_3_secondbyte;
|
268
268
|
} else {
|
@@ -271,7 +271,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
271
271
|
break;
|
272
272
|
|
273
273
|
case utf8_3_secondbyte:
|
274
|
-
if (
|
274
|
+
if (DEBUG) std::cout << "utf8_3_secondbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
275
275
|
if ((*current & 0xc0) == 0x80) {
|
276
276
|
temp_mark = 0; //temp_mark reset)
|
277
277
|
status = following_char;
|
@@ -285,7 +285,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
285
285
|
break;
|
286
286
|
|
287
287
|
case utf8_3_err_secondbyte:
|
288
|
-
if (
|
288
|
+
if (DEBUG) std::cout << "utf8_3_err_secondbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
289
289
|
ch.value_callback(mark, temp_mark - mark, false);//value_callback mark...temp_mark
|
290
290
|
mark = 0; value_len = 0; //mark_reset
|
291
291
|
ch.value_callback("\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd", 9, false); //value_callback "\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd"
|
@@ -294,7 +294,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
294
294
|
break;
|
295
295
|
|
296
296
|
case utf8_4_firstbyte:
|
297
|
-
if (
|
297
|
+
if (DEBUG) std::cout << "utf8_4_firstbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
298
298
|
if ((*current & 0xc0) == 0x80) {
|
299
299
|
status = utf8_4_secondbyte;
|
300
300
|
} else {
|
@@ -303,7 +303,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
303
303
|
break;
|
304
304
|
|
305
305
|
case utf8_4_secondbyte:
|
306
|
-
if (
|
306
|
+
if (DEBUG) std::cout << "utf8_4_secondbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
307
307
|
if ((*current & 0xc0) == 0x80) {
|
308
308
|
status = utf8_4_thirdbyte;
|
309
309
|
} else {
|
@@ -312,12 +312,12 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
312
312
|
break;
|
313
313
|
|
314
314
|
case utf8_4_err_secondbyte:
|
315
|
-
if (
|
315
|
+
if (DEBUG) std::cout << "utf8_4_err_secondbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
316
316
|
status = utf8_4_err_thirdbyte;
|
317
317
|
break;
|
318
318
|
|
319
319
|
case utf8_4_thirdbyte:
|
320
|
-
if (
|
320
|
+
if (DEBUG) std::cout << "utf8_4_thirdbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
321
321
|
if ((*current & 0xc0) == 0x80) {
|
322
322
|
temp_mark = 0; //temp_mark reset)
|
323
323
|
status = following_char;
|
@@ -331,7 +331,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
331
331
|
break;
|
332
332
|
|
333
333
|
case utf8_4_err_thirdbyte:
|
334
|
-
if (
|
334
|
+
if (DEBUG) std::cout << "utf8_4_err_thirdbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
335
335
|
ch.value_callback(mark, temp_mark - mark, false);//value_callback mark...temp_mark
|
336
336
|
mark = 0; value_len = 0; //mark_reset
|
337
337
|
ch.value_callback("\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd", 12, false); //value_callback "\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd"
|
@@ -340,7 +340,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
340
340
|
break;
|
341
341
|
|
342
342
|
case following_char:
|
343
|
-
if (
|
343
|
+
if (DEBUG) std::cout << "following_char[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
344
344
|
if (*current == '\'') {
|
345
345
|
ch.value_callback(mark, value_len - 1, true);//value_callback, :end_value
|
346
346
|
mark = 0; value_len = 0; //mark_reset
|
@@ -364,7 +364,7 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
364
364
|
break;
|
365
365
|
|
366
366
|
case escape:
|
367
|
-
if (
|
367
|
+
if (DEBUG) std::cout << "escape[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
368
368
|
if (*current == '\"') {
|
369
369
|
ch.value_callback("\"", 1, false);//value_callback '\"'
|
370
370
|
mark = 0; value_len = 0;
|
@@ -401,12 +401,12 @@ long parse_insert_query(const char* sql, long len, ParserCallbackHandler& ch)
|
|
401
401
|
break;
|
402
402
|
|
403
403
|
case error:
|
404
|
-
if (
|
404
|
+
if (DEBUG) std::cout << "error[" << *current << std::endl << std::flush;
|
405
405
|
ret = pos - 1;
|
406
406
|
break;
|
407
407
|
|
408
408
|
default:
|
409
|
-
if (
|
409
|
+
if (DEBUG) std::cout << "default[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
|
410
410
|
break;
|
411
411
|
}//switch
|
412
412
|
if (ret != -1)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'flydata-core/errors'
|
2
|
-
require 'flydata-core/postgresql/
|
3
|
-
require 'pg'
|
2
|
+
require 'flydata-core/postgresql/pg_client'
|
4
3
|
|
5
4
|
module FlydataCore
|
6
5
|
module Postgresql
|
@@ -37,19 +36,15 @@ module FlydataCore
|
|
37
36
|
|
38
37
|
def exec_query(query)
|
39
38
|
begin
|
40
|
-
client =
|
41
|
-
client.
|
39
|
+
client = PGClient.new(@option)
|
40
|
+
client.query(query)
|
42
41
|
ensure
|
43
42
|
client.close rescue nil if client
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
47
46
|
def schema_and_table_in_query(option = @option)
|
48
|
-
schema =
|
49
|
-
"select current_schema"
|
50
|
-
else
|
51
|
-
"'#{option[:schema]}'"
|
52
|
-
end
|
47
|
+
schema = FlydataCore::Postgresql::QueryHelper.schema_as_value(option[:schema])
|
53
48
|
{
|
54
49
|
schema_name: schema,
|
55
50
|
table_names: option[:tables].collect{|tn| "'#{tn}'"}.join(',')
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module FlydataCore
|
2
|
+
module Postgresql
|
3
|
+
|
4
|
+
class QueryHelper
|
5
|
+
# Return query getting a current_schema if schema is nil or empty
|
6
|
+
def self.schema_as_value(schema)
|
7
|
+
s = schema.to_s.strip
|
8
|
+
if s.empty?
|
9
|
+
'(select current_schema)'
|
10
|
+
else
|
11
|
+
"'#{s}'"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -11,6 +11,8 @@ class Snapshot
|
|
11
11
|
|
12
12
|
def initialize(txid_snapshot)
|
13
13
|
@txid_snapshot_str = txid_snapshot.to_s
|
14
|
+
|
15
|
+
return if self.empty?
|
14
16
|
xmin_str, xmax_str, xip_list_str = @txid_snapshot_str.split(':')
|
15
17
|
|
16
18
|
raise ArgumentError, "Invalid snapshot - xmin is empty." if xmin_str.to_s.empty?
|
@@ -27,7 +29,16 @@ class Snapshot
|
|
27
29
|
@txid_snapshot_str
|
28
30
|
end
|
29
31
|
|
32
|
+
def empty?
|
33
|
+
@txid_snapshot_str == '-'
|
34
|
+
end
|
35
|
+
|
30
36
|
def <=>(other)
|
37
|
+
return 0 if empty? && other.empty?
|
38
|
+
if self.empty? || other.empty?
|
39
|
+
raise ArgumentError.new("comparison with empty source pos failed")
|
40
|
+
end
|
41
|
+
|
31
42
|
if @xmin == other.xmin
|
32
43
|
if @xmax == other.xmax
|
33
44
|
# items xip_list will disappear after the transaction is completed,
|
@@ -10,8 +10,8 @@ class PostgresqlTableDef < Base
|
|
10
10
|
TYPE_MAP_P2F = {
|
11
11
|
'bigint' => {type: 'int8'},
|
12
12
|
'int8' => {type: 'int8'},
|
13
|
-
'bigserial' => {type: 'serial8'},
|
14
|
-
'serial8' => {type: 'serial8'},
|
13
|
+
'bigserial' => {type: 'serial8'},
|
14
|
+
'serial8' => {type: 'serial8'},
|
15
15
|
'bit' => {type: 'bit',
|
16
16
|
width_attrs:["character_maximum_length"]},
|
17
17
|
'bit varying' => {type: 'varbit',
|
@@ -55,10 +55,10 @@ class PostgresqlTableDef < Base
|
|
55
55
|
'float4' => {type: 'float4'},
|
56
56
|
'smallint' => {type: 'int2'},
|
57
57
|
'int2' => {type: 'int2'},
|
58
|
-
'smallserial' => {type: 'serial2'},
|
59
|
-
'serial2' => {type: 'serial2'},
|
60
|
-
'serial' => {type: 'serial4'},
|
61
|
-
'serial4' => {type: 'serial4'},
|
58
|
+
'smallserial' => {type: 'serial2'},
|
59
|
+
'serial2' => {type: 'serial2'},
|
60
|
+
'serial' => {type: 'serial4'},
|
61
|
+
'serial4' => {type: 'serial4'},
|
62
62
|
'text' => {type: 'text'},
|
63
63
|
'time' => {type: 'time'},
|
64
64
|
'time without time zone' => {type: 'time'},
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'date'
|
2
2
|
require 'flydata-core/errors'
|
3
|
+
require 'flydata-core/table_def/value_conv'
|
3
4
|
|
4
5
|
module FlydataCore
|
5
6
|
module TableDef
|
@@ -218,7 +219,7 @@ EOS
|
|
218
219
|
|
219
220
|
rs_type = if type_info[:use_params]
|
220
221
|
if params
|
221
|
-
params = check_and_replace_max(params, Array(type_info[:max_size])) if type_info[:max_size]
|
222
|
+
params = check_and_replace_max(params, Array(type_info[:max_size]), type) if type_info[:max_size]
|
222
223
|
else
|
223
224
|
#source data type has no parameter. use default parameters.
|
224
225
|
raise "No default pramameters for type:`#{column[:type]}`" unless type_info[:default_params]
|
@@ -272,7 +273,7 @@ EOS
|
|
272
273
|
raise "default value of YEAR type must be 2 or 4-digit, value:'#{default_value}'"
|
273
274
|
end
|
274
275
|
elsif flydata_type.start_with?('money')
|
275
|
-
default_value =
|
276
|
+
default_value = ValueConv.strip_currency_format(remove_single_quote(default_value))
|
276
277
|
end
|
277
278
|
|
278
279
|
case redshift_type
|
@@ -413,11 +414,29 @@ EOS
|
|
413
414
|
text.gsub(/(['\\])/, "\\\\\\1")
|
414
415
|
end
|
415
416
|
|
416
|
-
def self.check_and_replace_max(params, max_size_a)
|
417
|
+
def self.check_and_replace_max(params, max_size_a, flydata_type)
|
417
418
|
final_params = []
|
418
|
-
|
419
|
-
|
420
|
-
|
419
|
+
case flydata_type
|
420
|
+
when 'numeric', 'numeric unsigned'
|
421
|
+
p = params.split(",")
|
422
|
+
raise "Wrong number of params for #{flydata_type} type." if p.length != max_size_a.length
|
423
|
+
above_decimal = p[0].to_i - p[1].to_i
|
424
|
+
below_decimal = p[1].to_i
|
425
|
+
|
426
|
+
# use max precision as the max number of above-dicimal digits
|
427
|
+
max_above_decimal = max_size_a[0]
|
428
|
+
final_above_decimal = above_decimal > max_above_decimal ? max_above_decimal : above_decimal
|
429
|
+
|
430
|
+
max_below_decimal = max_size_a[0] - final_above_decimal
|
431
|
+
max_below_decimal = max_size_a[1] if max_below_decimal > max_size_a[1]
|
432
|
+
final_below_decimal = below_decimal > max_below_decimal ? max_below_decimal : below_decimal
|
433
|
+
|
434
|
+
final_params << (final_above_decimal + final_below_decimal) << final_below_decimal
|
435
|
+
else
|
436
|
+
params.split(",").each_with_index do |param, i|
|
437
|
+
final_params << (/\d+/.match(param) && max_size_a[i] && param.to_i > max_size_a[i].to_i ?
|
438
|
+
max_size_a[i] : param)
|
439
|
+
end
|
421
440
|
end
|
422
441
|
final_params.join(",")
|
423
442
|
end
|
@@ -506,26 +525,6 @@ EOS
|
|
506
525
|
value # Return the value as is
|
507
526
|
end
|
508
527
|
end
|
509
|
-
|
510
|
-
def self.parse_money(value)
|
511
|
-
# It's impossible to determine a decimal point of a currency sting without
|
512
|
-
# knowing its format. Here, we're making a best effort guess to support
|
513
|
-
# as many currencty formats as we can without knowing the format.
|
514
|
-
value = value.gsub(/[^0-9\.,]/, '') # remove all chars but numbers and possible decimal point chars
|
515
|
-
|
516
|
-
whole_part = value
|
517
|
-
fractional_part = nil
|
518
|
-
if idx = value.rindex(/[\.,]/)
|
519
|
-
if idx == value.size - 3
|
520
|
-
# it's the decimal point with two digit fraction
|
521
|
-
whole_part = value[0...idx]
|
522
|
-
fractional_part = value[-2..-1]
|
523
|
-
end
|
524
|
-
end
|
525
|
-
value = whole_part.gsub(/[^0-9]/, '')
|
526
|
-
value += ".#{fractional_part}" if fractional_part
|
527
|
-
value
|
528
|
-
end
|
529
528
|
end
|
530
529
|
|
531
530
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module FlydataCore
|
2
|
+
module TableDef
|
3
|
+
|
4
|
+
# Collection of methods to covert values
|
5
|
+
module ValueConv
|
6
|
+
# currency format string -> decimal string
|
7
|
+
def self.strip_currency_format(value)
|
8
|
+
# It's impossible to determine a decimal point of a currency sting without
|
9
|
+
# knowing its format. Here, we're making a best effort guess to support
|
10
|
+
# as many currency formats as we can without knowing the format.
|
11
|
+
value = value.gsub(/[^0-9\.,]/, '') # remove all chars but numbers and possible decimal point chars
|
12
|
+
|
13
|
+
whole_part = value
|
14
|
+
fractional_part = nil
|
15
|
+
if idx = value.rindex(/[\.,]/)
|
16
|
+
if idx == value.size - 3
|
17
|
+
# it's the decimal point with two digit fraction
|
18
|
+
whole_part = value[0...idx]
|
19
|
+
fractional_part = value[-2..-1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
value = whole_part.gsub(/[^0-9]/, '')
|
23
|
+
value += ".#{fractional_part}" if fractional_part
|
24
|
+
value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -23,7 +23,7 @@ SELECT
|
|
23
23
|
FROM
|
24
24
|
information_schema.tables
|
25
25
|
WHERE
|
26
|
-
table_schema in (select current_schema)
|
26
|
+
table_schema in ((select current_schema))
|
27
27
|
AND
|
28
28
|
table_name in ('table_1','table_2','table_3');
|
29
29
|
EOT
|
@@ -86,9 +86,9 @@ EOT
|
|
86
86
|
SELECT
|
87
87
|
t.table_name
|
88
88
|
FROM
|
89
|
-
(select * from information_schema.tables where table_schema in (select current_schema) AND table_name in ('table_1','table_2','table_3')) t
|
89
|
+
(select * from information_schema.tables where table_schema in ((select current_schema)) AND table_name in ('table_1','table_2','table_3')) t
|
90
90
|
LEFT OUTER JOIN
|
91
|
-
(select * from information_schema.table_constraints where table_schema in (select current_schema) AND table_name in ('table_1','table_2','table_3')) tc
|
91
|
+
(select * from information_schema.table_constraints where table_schema in ((select current_schema)) AND table_name in ('table_1','table_2','table_3')) tc
|
92
92
|
USING (table_schema, table_name)
|
93
93
|
GROUP BY
|
94
94
|
t.table_schema, t.table_name
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata-core/postgresql/query_helper'
|
3
|
+
|
4
|
+
module FlydataCore
|
5
|
+
module Postgresql
|
6
|
+
|
7
|
+
describe QueryHelper do
|
8
|
+
describe '.schema_as_value' do
|
9
|
+
subject { described_class.schema_as_value(schema) }
|
10
|
+
|
11
|
+
context 'when schema is empty' do
|
12
|
+
let(:schema) { '' }
|
13
|
+
it { is_expected.to eq('(select current_schema)') }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when schema is nil' do
|
17
|
+
let(:schema) { nil }
|
18
|
+
it { is_expected.to eq('(select current_schema)') }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when schema is a text' do
|
22
|
+
let(:schema) { 'test-schema'}
|
23
|
+
it { is_expected.to eq("'test-schema'") }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -10,6 +10,15 @@ module FlydataCore
|
|
10
10
|
describe '#initialize' do
|
11
11
|
subject { subject_object }
|
12
12
|
|
13
|
+
context 'when text is -' do
|
14
|
+
let(:snapshot_text) { '-' }
|
15
|
+
it { expect(subject.xmin).to be_nil }
|
16
|
+
it { expect(subject.xmax).to be_nil }
|
17
|
+
it { expect(subject.xip_list).to be_nil }
|
18
|
+
it { expect(subject.to_s).to eq('-') }
|
19
|
+
it { expect(subject).to be_empty }
|
20
|
+
end
|
21
|
+
|
13
22
|
context 'when xip_list is empty' do
|
14
23
|
let(:snapshot_text) { '10:18:' }
|
15
24
|
it { expect(subject.xmin).to eq(10) }
|
@@ -27,6 +36,20 @@ module FlydataCore
|
|
27
36
|
end
|
28
37
|
end
|
29
38
|
|
39
|
+
describe '#empty?' do
|
40
|
+
subject { subject_object.empty? }
|
41
|
+
|
42
|
+
context 'when snapshot_txt is -' do
|
43
|
+
let(:snapshot_text) { '-' }
|
44
|
+
it { is_expected.to be(true) }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when snapshot_txt is normal value' do
|
48
|
+
let(:snapshot_text) { '10:10:' }
|
49
|
+
it { is_expected.to be(false) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
30
53
|
describe '#<=>' do
|
31
54
|
def new_ss(txt)
|
32
55
|
Snapshot.new(txt)
|
@@ -39,6 +62,8 @@ module FlydataCore
|
|
39
62
|
it { expect(new_ss("10:18:") == new_ss("10:18:10,11,12")).to be(false) }
|
40
63
|
it { expect(new_ss("10:18:10,11,12") == new_ss("10:18:10,11,12")).to be(true) }
|
41
64
|
it { expect(new_ss("10:18:10,11,12") == new_ss("10:18:11,12")).to be(false) }
|
65
|
+
it { expect(new_ss("-") == new_ss("-")).to be(true) }
|
66
|
+
it { expect(new_ss("-") == new_ss("10:10:")).to be(false) }
|
42
67
|
|
43
68
|
it { expect(new_ss("10:10:") > new_ss("10:10:")).to be(false) }
|
44
69
|
it { expect(new_ss("10:10:") > new_ss("11:11:")).to be(false) }
|
@@ -456,28 +456,56 @@ EOT
|
|
456
456
|
end
|
457
457
|
|
458
458
|
context 'with decimal column def' do
|
459
|
+
context 'when precision is exactly max allowed' do
|
460
|
+
let(:default_value) { "'4'" }
|
461
|
+
let(:default_value_sql) { default_value }
|
462
|
+
let(:not_null_default_sql) { " DEFAULT '0'" }
|
463
|
+
before do
|
464
|
+
column[:column] = "value"
|
465
|
+
column[:type] = "numeric(38,4)" #34+4 digits
|
466
|
+
end
|
467
|
+
let(:type_sql) { %Q|"value" numeric(38,4)| }
|
468
|
+
|
469
|
+
it_behaves_like *examples
|
470
|
+
end
|
471
|
+
|
459
472
|
context 'when precision exceeds max allowed' do
|
460
473
|
let(:default_value) { "'4'" }
|
461
474
|
let(:default_value_sql) { default_value }
|
462
475
|
let(:not_null_default_sql) { " DEFAULT '0'" }
|
463
476
|
before do
|
464
477
|
column[:column] = "value"
|
465
|
-
column[:type] = "numeric(
|
478
|
+
column[:type] = "numeric(42,37)" #5+37 (precision 42) => 5+33 digits (precision 38)
|
479
|
+
end
|
480
|
+
# Preserve digits over decimal as much as possible,
|
481
|
+
# and truncate scale.
|
482
|
+
let(:type_sql) { %Q|"value" numeric(38,33)| }
|
483
|
+
|
484
|
+
it_behaves_like *examples
|
485
|
+
end
|
486
|
+
|
487
|
+
context 'when both precision and scale exceed max allowed' do #possible in postgresql
|
488
|
+
let(:default_value) { "'4'" }
|
489
|
+
let(:default_value_sql) { default_value }
|
490
|
+
let(:not_null_default_sql) { " DEFAULT '0'" }
|
491
|
+
before do
|
492
|
+
column[:column] = "value"
|
493
|
+
column[:type] = "numeric(1000,44)" #956+44 => 38+0 digits
|
466
494
|
end
|
467
|
-
let(:type_sql) { %Q|"value" numeric(38,
|
495
|
+
let(:type_sql) { %Q|"value" numeric(38,0)| }
|
468
496
|
|
469
497
|
it_behaves_like *examples
|
470
498
|
end
|
471
|
-
|
499
|
+
|
500
|
+
context 'when only scale exceed max allowed' do
|
472
501
|
let(:default_value) { "'4'" }
|
473
502
|
let(:default_value_sql) { default_value }
|
474
503
|
let(:not_null_default_sql) { " DEFAULT '0'" }
|
475
504
|
before do
|
476
505
|
column[:column] = "value"
|
477
|
-
column[:type] = "numeric(
|
506
|
+
column[:type] = "numeric(38,38)" #0+38 => 0+37 digits
|
478
507
|
end
|
479
|
-
|
480
|
-
let(:type_sql) { %Q|"value" numeric(38,37)| }
|
508
|
+
let(:type_sql) { %Q|"value" numeric(37,37)| }
|
481
509
|
|
482
510
|
it_behaves_like *examples
|
483
511
|
end
|
@@ -503,7 +531,7 @@ EOT
|
|
503
531
|
column[:column] = "value"
|
504
532
|
column[:type] = "numeric(65,44) unsigned"
|
505
533
|
end
|
506
|
-
let(:type_sql) {%Q|"value" numeric(38,
|
534
|
+
let(:type_sql) {%Q|"value" numeric(38,17)| }
|
507
535
|
|
508
536
|
it_behaves_like *examples
|
509
537
|
end
|
@@ -1198,6 +1226,30 @@ EOS
|
|
1198
1226
|
end
|
1199
1227
|
end
|
1200
1228
|
end
|
1229
|
+
context 'with moeny type' do
|
1230
|
+
let(:flydata_type) { "money" }
|
1231
|
+
let(:redshift_type) { "money" }
|
1232
|
+
|
1233
|
+
context 'in dollar format' do
|
1234
|
+
let(:default_value) { "'$2,392.40'" }
|
1235
|
+
it { is_expected.to eq "'2392.40'" }
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
context 'in JPY format' do
|
1239
|
+
let(:default_value) { "'JPY2,382,929'" }
|
1240
|
+
it { is_expected.to eq "'2382929'" }
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
context 'in INR format' do
|
1244
|
+
let(:default_value) { "'12,23,382.29'" }
|
1245
|
+
it { is_expected.to eq "'1223382.29'" }
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
context 'in DKK format' do
|
1249
|
+
let(:default_value) { "'10.382,29'" }
|
1250
|
+
it { is_expected.to eq "'10382.29'" }
|
1251
|
+
end
|
1252
|
+
end
|
1201
1253
|
end
|
1202
1254
|
end
|
1203
1255
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module FlydataCore
|
2
|
+
module TableDef
|
3
|
+
|
4
|
+
require 'flydata-core/table_def/value_conv'
|
5
|
+
|
6
|
+
describe ValueConv do
|
7
|
+
describe '.strip_currency_format' do
|
8
|
+
subject { described_class.strip_currency_format(value) }
|
9
|
+
|
10
|
+
context 'in dollar format' do
|
11
|
+
let(:value) { "$2,392.40" }
|
12
|
+
it { is_expected.to eq "2392.40" }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'in JPY format' do
|
16
|
+
let(:value) { "JPY2,382,929" }
|
17
|
+
it { is_expected.to eq "2382929" }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'in INR format' do
|
21
|
+
let(:value) { "12,23,382.29" }
|
22
|
+
it { is_expected.to eq "1223382.29" }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'in DKK format' do
|
26
|
+
let(:value) { "10.382,29" }
|
27
|
+
it { is_expected.to eq "10382.29" }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/flydata.gemspec
CHANGED
Binary file
|
@@ -219,7 +219,7 @@ EOS
|
|
219
219
|
if @thread and @thread.alive?
|
220
220
|
$log.info "Requesting stop Kodama"
|
221
221
|
begin
|
222
|
-
@kodama_client.stop_request
|
222
|
+
@kodama_client.stop_request if @kodama_client
|
223
223
|
if wait_till_safe_to_stop
|
224
224
|
@thread.join
|
225
225
|
$log.info "Kodama has stopped successfully"
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'shellwords'
|
3
|
+
require 'flydata/command/sync'
|
4
|
+
|
5
|
+
# Command class must be in module Flydata::Command
|
6
|
+
module Flydata
|
7
|
+
module Command
|
8
|
+
|
9
|
+
class Psql < Sync
|
10
|
+
def run(*args)
|
11
|
+
de = data_entry
|
12
|
+
cmd = generate_command(de['postgresql_data_entry_preference'], args)
|
13
|
+
return if cmd.to_s.empty?
|
14
|
+
$stderr.puts "command:#{cmd}" if FLYDATA_DEBUG
|
15
|
+
if $stdin.tty?
|
16
|
+
# interactive shell
|
17
|
+
system cmd
|
18
|
+
else
|
19
|
+
# execute queries given to $stdin
|
20
|
+
Open3.popen2e(cmd) do |i, o, wt|
|
21
|
+
$stdin.each_line do |line|
|
22
|
+
i.print line
|
23
|
+
end
|
24
|
+
i.close
|
25
|
+
while line = o.gets
|
26
|
+
print line
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate_command(dbconf, args)
|
33
|
+
password = Shellwords.escape(dbconf['password'])
|
34
|
+
database = Shellwords.escape(dbconf['database'])
|
35
|
+
host = Shellwords.escape(dbconf['host'])
|
36
|
+
port = Shellwords.escape(dbconf['port'])
|
37
|
+
username = Shellwords.escape(dbconf['username'])
|
38
|
+
"PGCONNECT_TIMEOUT=5 PGPASSWORD=#{password} psql -d #{database} -h #{host} -p #{port} -U #{username}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def flush; end
|
42
|
+
def reset; end
|
43
|
+
def skip; end
|
44
|
+
def generate_table_ddl; end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -14,7 +14,7 @@ class GenerateSourceDump < Source::GenerateSourceDump
|
|
14
14
|
include PostgresqlComponent
|
15
15
|
|
16
16
|
def run_compatibility_check(dump_dir, backup_dir)
|
17
|
-
%w(host username database
|
17
|
+
%w(host username database).each do |k|
|
18
18
|
if de_prefs[k].to_s.empty?
|
19
19
|
raise "'#{k}' is required. Set the value in the conf file " +
|
20
20
|
"-> #{Flydata::Preference::DataEntryPreference.conf_path(de)}"
|
@@ -34,21 +34,24 @@ class GenerateSourceDump < Source::GenerateSourceDump
|
|
34
34
|
items
|
35
35
|
end
|
36
36
|
|
37
|
-
DUMP_SIZE_QUERY = <<EOS
|
37
|
+
DUMP_SIZE_QUERY = <<EOS
|
38
38
|
SELECT sum(pg_total_relation_size(c.oid)) AS "total_size"
|
39
39
|
FROM pg_class c
|
40
40
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
41
|
-
WHERE nspname =
|
41
|
+
WHERE nspname = %{schema} AND relname in (%{tables})
|
42
42
|
EOS
|
43
|
-
TABLE_PLACEHOLDER_START_NUM = 2 # because $1 is used by table_schema
|
44
43
|
|
45
44
|
def dump_size(tables)
|
46
45
|
cli = FlydataCore::Postgresql::PGClient.new(de_prefs)
|
47
46
|
|
48
|
-
|
47
|
+
base_query = DUMP_SIZE_QUERY % {
|
48
|
+
schema: FlydataCore::Postgresql::QueryHelper.schema_as_value(de_prefs['schema']),
|
49
|
+
tables: '%s' # Use binding parameters for tables
|
50
|
+
}
|
51
|
+
query = FlydataCore::Postgresql::PGQuery.new(base_query,
|
49
52
|
placeholder_size: tables.size,
|
50
|
-
placeholder_start_num:
|
51
|
-
res = cli.query(query,
|
53
|
+
placeholder_start_num: 1)
|
54
|
+
res = cli.query(query, tables)
|
52
55
|
|
53
56
|
res.first['total_size'].to_i
|
54
57
|
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'flydata-core/postgresql/pg_client'
|
2
|
+
require 'flydata-core/table_def/postgresql_table_def'
|
3
|
+
require 'flydata-core/table_def/value_conv'
|
2
4
|
|
3
5
|
module Flydata
|
4
6
|
module SourcePostgresql
|
@@ -107,7 +109,6 @@ EOS
|
|
107
109
|
h[k] =
|
108
110
|
case k
|
109
111
|
when "bytea"; %Q['0x' || encode("%<column>s", 'hex') AS "%<column>s"]
|
110
|
-
when "money"; %Q["%<column>s"::numeric]
|
111
112
|
else %Q["%<column>s"]
|
112
113
|
end
|
113
114
|
end
|
@@ -116,6 +117,8 @@ EOS
|
|
116
117
|
h[k] =
|
117
118
|
case k
|
118
119
|
when "bit", "varbit"; -> (v) { v ? '%d' % v.to_i(2) : nil }
|
120
|
+
when "boolean"; -> (v) { FlydataCore::TableDef::PostgresqlTableDef::to_boolean(v) }
|
121
|
+
when "money"; -> (v) { FlydataCore::TableDef::ValueConv.strip_currency_format(v) }
|
119
122
|
else
|
120
123
|
nil
|
121
124
|
end
|
@@ -126,7 +129,11 @@ EOS
|
|
126
129
|
end
|
127
130
|
|
128
131
|
def build_value_overriders(columns, types)
|
129
|
-
columns.each_with_index.inject({})
|
132
|
+
columns.each_with_index.inject({}) do |h, (c, i)|
|
133
|
+
overrider = VALUE_OVERRIDERS[types[i]]
|
134
|
+
h[c] = overrider if overrider
|
135
|
+
h
|
136
|
+
end
|
130
137
|
end
|
131
138
|
end
|
132
139
|
|
@@ -10,6 +10,8 @@ module SourcePostgresql
|
|
10
10
|
# Fetch and keep table meta information
|
11
11
|
#
|
12
12
|
# <table-name(Symbol)>:
|
13
|
+
# table_name: <String> # Table name
|
14
|
+
# table_schema: <String> or nil # Schema name
|
13
15
|
# primary_keys: <Array of String> # Set primary key names. ex: ['group_id', 'category_id']
|
14
16
|
# pk_positions: <Array of Integer> # Set the ordinal position of primary keys. ex: [1, 3]
|
15
17
|
# max_row_size: <Integer> # byte, calculated based on column size
|
@@ -45,7 +47,7 @@ SELECT
|
|
45
47
|
c.numeric_precision_radix,
|
46
48
|
CASE WHEN c.data_type='money' THEN (
|
47
49
|
SELECT CASE WHEN scale IS NULL THEN 0 ELSE scale END
|
48
|
-
FROM (SELECT length(substring(999999::money::char varying, '\.([0-9]+)$')) AS scale) s)
|
50
|
+
FROM (SELECT length(substring('999999'::money::char varying, '\.([0-9]+)$')) AS scale) s)
|
49
51
|
ELSE c.numeric_scale END AS numeric_scale,
|
50
52
|
c.datetime_precision,
|
51
53
|
i.indisprimary AS is_primary
|
@@ -72,11 +74,12 @@ EOT
|
|
72
74
|
DEFAULT_MAX_FETCH_RECORD_SIZE = 50000
|
73
75
|
#DEFAULT_MAX_FETCH_RECORD_SIZE = 8
|
74
76
|
|
75
|
-
def initialize(dbconf, tables, schema = nil)
|
77
|
+
def initialize(dbconf, tables, schema = nil)
|
76
78
|
@dbconf = dbconf
|
77
79
|
@database = dbconf[:dbname] || dbconf[:database] || dbconf['database']
|
78
80
|
@tables = tables
|
79
|
-
@schema = schema || dbconf[:schema] || dbconf['schema']
|
81
|
+
@schema = (schema || dbconf[:schema] || dbconf['schema']).to_s.strip
|
82
|
+
@schema = @schema.empty? ? nil : @schema
|
80
83
|
end
|
81
84
|
|
82
85
|
attr_reader :current_snapshot
|
@@ -145,6 +148,7 @@ EOT
|
|
145
148
|
|
146
149
|
t_meta.merge!(
|
147
150
|
table_name: table_name.to_s,
|
151
|
+
table_schema: @schema,
|
148
152
|
primary_keys: primary_keys,
|
149
153
|
pk_positions: pk_positions,
|
150
154
|
#max_row_size: max_row_size, #TODO: calculation
|
@@ -17,7 +17,7 @@ module Fluent
|
|
17
17
|
tables_append_only test_table_3
|
18
18
|
position_file #{TEST_POSITION_FILE}
|
19
19
|
host localhost
|
20
|
-
port
|
20
|
+
port 5433
|
21
21
|
username test_admin
|
22
22
|
password test_password
|
23
23
|
database test_db
|
@@ -50,6 +50,7 @@ EOT
|
|
50
50
|
'tables' => 'test_table,test_table_1,test_table_2',
|
51
51
|
'tables_append_only' => 'test_table_3',
|
52
52
|
'host' => 'localhost',
|
53
|
+
'port' => 5433,
|
53
54
|
'username' => 'test_admin',
|
54
55
|
'password' => 'test_password',
|
55
56
|
'schema' => 'test_schema',
|
@@ -71,10 +71,11 @@ EOS
|
|
71
71
|
|
72
72
|
context 'when columns includes a money type' do
|
73
73
|
let(:value_data_type) { 'money(19,2)' }
|
74
|
-
it 'returns money values as
|
74
|
+
it 'returns money values as is' do
|
75
75
|
is_expected.to eq(<<EOS)
|
76
|
-
SELECT "id", "value"
|
76
|
+
SELECT "id", "value" FROM "public"."test_table" WHERE txid_visible_in_snapshot(xmin::TEXT::BIGINT, '0002:0002:') AND NOT txid_visible_in_snapshot(xmin::TEXT::BIGINT, '0001:0001:') AND ("id" > $1) ORDER BY "id" LIMIT 100;
|
77
77
|
EOS
|
78
|
+
expect(subject.value_overriders).to eq({"value" => DiffQueryGenerator::VALUE_OVERRIDERS['money']})
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
@@ -100,6 +101,17 @@ EOS
|
|
100
101
|
end
|
101
102
|
end
|
102
103
|
|
104
|
+
context 'when columns includes a boolean type' do
|
105
|
+
let(:value_data_type) { 'boolean' }
|
106
|
+
it 'returns a query with an overrider proc for boolean' do
|
107
|
+
result = subject
|
108
|
+
expect(result).to eq(<<EOS)
|
109
|
+
SELECT "id", "value" FROM "public"."test_table" WHERE txid_visible_in_snapshot(xmin::TEXT::BIGINT, '0002:0002:') AND NOT txid_visible_in_snapshot(xmin::TEXT::BIGINT, '0001:0001:') AND ("id" > $1) ORDER BY "id" LIMIT 100;
|
110
|
+
EOS
|
111
|
+
expect(result.value_overriders).to eq({"value" => DiffQueryGenerator::VALUE_OVERRIDERS['boolean']})
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
103
115
|
context 'when from_sid is not given' do
|
104
116
|
let(:from_sid) { nil }
|
105
117
|
it { is_expected.to eq(<<EOS)
|
@@ -131,9 +143,17 @@ EOS
|
|
131
143
|
subject { DiffQueryGenerator::VALUE_OVERRIDERS[type].call(value) }
|
132
144
|
|
133
145
|
context 'when type is bit' do
|
134
|
-
|
135
|
-
|
136
|
-
|
146
|
+
context 'when value is not nil' do
|
147
|
+
let(:type) { 'bit' }
|
148
|
+
let(:value) { '0110000010' }
|
149
|
+
it { is_expected.to eq '386' }
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'when value is nil' do
|
153
|
+
let(:type) { 'bit' }
|
154
|
+
let(:value) { nil }
|
155
|
+
it { is_expected.to eq nil }
|
156
|
+
end
|
137
157
|
end
|
138
158
|
|
139
159
|
context 'when type is varbit' do
|
@@ -143,10 +163,24 @@ EOS
|
|
143
163
|
it { is_expected.to eq "18446744073709551613" }
|
144
164
|
end
|
145
165
|
|
146
|
-
context 'when
|
147
|
-
|
148
|
-
|
149
|
-
|
166
|
+
context 'when type is boolean' do
|
167
|
+
context 'when value is true' do
|
168
|
+
let(:type) { 'boolean' }
|
169
|
+
let(:value) { 't' }
|
170
|
+
it { is_expected.to eq true }
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when value is false' do
|
174
|
+
let(:type) { 'boolean' }
|
175
|
+
let(:value) { 'f' }
|
176
|
+
it { is_expected.to eq false }
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'when value is nil' do
|
180
|
+
let(:type) { 'boolean' }
|
181
|
+
let(:value) { nil }
|
182
|
+
it { is_expected.to eq nil }
|
183
|
+
end
|
150
184
|
end
|
151
185
|
end
|
152
186
|
|
@@ -201,8 +235,8 @@ EOS
|
|
201
235
|
|
202
236
|
context 'with a money column' do
|
203
237
|
let(:value_data_type) { 'money' }
|
204
|
-
it 'generates a list which outputs money values as
|
205
|
-
is_expected.to eq %Q["id", "value"
|
238
|
+
it 'generates a list which outputs money values as is' do
|
239
|
+
is_expected.to eq %Q["id", "value"]
|
206
240
|
end
|
207
241
|
end
|
208
242
|
end
|
@@ -72,6 +72,25 @@ module Flydata::SourcePostgresql
|
|
72
72
|
expect(subject[:table_a][:table_def]).to be_kind_of(FlydataCore::TableDef::Base)
|
73
73
|
end
|
74
74
|
end
|
75
|
+
|
76
|
+
context 'when schema is empty' do
|
77
|
+
let(:input_cli) { nil }
|
78
|
+
let(:schema) { nil }
|
79
|
+
|
80
|
+
before do
|
81
|
+
dbconf.delete(:schema)
|
82
|
+
dbconf.delete('schema')
|
83
|
+
allow(conn).to receive(:close)
|
84
|
+
end
|
85
|
+
it 'uses a current schema on query' do
|
86
|
+
expect(conn).to receive(:query) do |query|
|
87
|
+
expect(query).to match(/AND c.table_schema IN \(select current_schema\)/)
|
88
|
+
raw_columns
|
89
|
+
end
|
90
|
+
expect(conn).to receive(:query).with('SELECT txid_current_snapshot();').and_return(current_snapshots)
|
91
|
+
subject
|
92
|
+
end
|
93
|
+
end
|
75
94
|
end
|
76
95
|
end
|
77
96
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Custom configuration for flydata client
|
2
|
+
# This file is yaml format. http://www.yaml.org/
|
3
|
+
# Please remove '#' to enable custom option settings.
|
4
|
+
postgresql_data_entry_preference:
|
5
|
+
#host: localhost
|
6
|
+
#port: 5432
|
7
|
+
#username: root
|
8
|
+
#password: abcd
|
9
|
+
#database: dev
|
10
|
+
#schema: public
|
11
|
+
#tables: users,country,rows
|
12
|
+
#tables_append_only: employees
|
13
|
+
#dump_dir: /mnt/dump
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flydata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koichi Fujikawa
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2016-04-
|
15
|
+
date: 2016-04-05 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: rest-client
|
@@ -547,6 +547,7 @@ files:
|
|
547
547
|
- flydata-core/lib/flydata-core/postgresql/compatibility_checker.rb
|
548
548
|
- flydata-core/lib/flydata-core/postgresql/config.rb
|
549
549
|
- flydata-core/lib/flydata-core/postgresql/pg_client.rb
|
550
|
+
- flydata-core/lib/flydata-core/postgresql/query_helper.rb
|
550
551
|
- flydata-core/lib/flydata-core/postgresql/snapshot.rb
|
551
552
|
- flydata-core/lib/flydata-core/postgresql/source_pos.rb
|
552
553
|
- flydata-core/lib/flydata-core/query_job.rb
|
@@ -560,6 +561,7 @@ files:
|
|
560
561
|
- flydata-core/lib/flydata-core/table_def/postgresql_table_def.rb
|
561
562
|
- flydata-core/lib/flydata-core/table_def/redshift_table_def.rb
|
562
563
|
- flydata-core/lib/flydata-core/table_def/sync_redshift_table_def.rb
|
564
|
+
- flydata-core/lib/flydata-core/table_def/value_conv.rb
|
563
565
|
- flydata-core/lib/flydata-core/thread_context.rb
|
564
566
|
- flydata-core/lib/flydata-core/wrapper_forwardable.rb
|
565
567
|
- flydata-core/spec/config/user_maintenance_spec.rb
|
@@ -579,6 +581,7 @@ files:
|
|
579
581
|
- flydata-core/spec/postgresql/compatibility_checker_spec.rb
|
580
582
|
- flydata-core/spec/postgresql/config_spec.rb
|
581
583
|
- flydata-core/spec/postgresql/pg_client_spec.rb
|
584
|
+
- flydata-core/spec/postgresql/query_helper_spec.rb
|
582
585
|
- flydata-core/spec/postgresql/snapshot_spec.rb
|
583
586
|
- flydata-core/spec/postgresql/source_pos_spec.rb
|
584
587
|
- flydata-core/spec/redshift/string_spec.rb
|
@@ -609,6 +612,7 @@ files:
|
|
609
612
|
- flydata-core/spec/table_def/postgresql_table_def_spec.rb
|
610
613
|
- flydata-core/spec/table_def/redshift_table_def_spec.rb
|
611
614
|
- flydata-core/spec/table_def/sync_redshift_table_def_spec.rb
|
615
|
+
- flydata-core/spec/table_def/value_conv_spec.rb
|
612
616
|
- flydata-core/spec/wrapper_forwardable_spec.rb
|
613
617
|
- flydata.gemspec
|
614
618
|
- lib/fly_data_model.rb
|
@@ -735,6 +739,7 @@ files:
|
|
735
739
|
- lib/flydata/source_mysql/sync_generate_table_ddl.rb
|
736
740
|
- lib/flydata/source_mysql/table_ddl.rb
|
737
741
|
- lib/flydata/source_mysql/table_meta.rb
|
742
|
+
- lib/flydata/source_postgresql/command/psql.rb
|
738
743
|
- lib/flydata/source_postgresql/data_entry.rb
|
739
744
|
- lib/flydata/source_postgresql/generate_source_dump.rb
|
740
745
|
- lib/flydata/source_postgresql/parse_dump_and_send.rb
|
@@ -843,6 +848,7 @@ files:
|
|
843
848
|
- spec/spec_helper.rb
|
844
849
|
- test-suite.sh
|
845
850
|
- tmpl/redshift_mysql_data_entry.conf.tmpl
|
851
|
+
- tmpl/redshift_postgresql_data_entry.conf.tmpl
|
846
852
|
homepage: http://flydata.com/
|
847
853
|
licenses:
|
848
854
|
- All right reserved.
|