flydata 0.7.0 → 0.7.1

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/ext/flydata/source_mysql/parser/sql_parser.cpp +25 -25
  4. data/flydata-core/lib/flydata-core/postgresql/compatibility_checker.rb +4 -9
  5. data/flydata-core/lib/flydata-core/postgresql/pg_client.rb +1 -0
  6. data/flydata-core/lib/flydata-core/postgresql/query_helper.rb +17 -0
  7. data/flydata-core/lib/flydata-core/postgresql/snapshot.rb +11 -0
  8. data/flydata-core/lib/flydata-core/postgresql/source_pos.rb +4 -0
  9. data/flydata-core/lib/flydata-core/table_def/postgresql_table_def.rb +6 -6
  10. data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +25 -26
  11. data/flydata-core/lib/flydata-core/table_def/value_conv.rb +29 -0
  12. data/flydata-core/spec/postgresql/compatibility_checker_spec.rb +3 -3
  13. data/flydata-core/spec/postgresql/query_helper_spec.rb +29 -0
  14. data/flydata-core/spec/postgresql/snapshot_spec.rb +25 -0
  15. data/flydata-core/spec/table_def/redshift_table_def_spec.rb +59 -7
  16. data/flydata-core/spec/table_def/value_conv_spec.rb +33 -0
  17. data/flydata.gemspec +0 -0
  18. data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +1 -1
  19. data/lib/flydata/source_mysql/data_entry.rb +1 -0
  20. data/lib/flydata/source_postgresql/command/psql.rb +48 -0
  21. data/lib/flydata/source_postgresql/data_entry.rb +1 -0
  22. data/lib/flydata/source_postgresql/generate_source_dump.rb +10 -7
  23. data/lib/flydata/source_postgresql/query_based_sync/diff_query_generator.rb +9 -2
  24. data/lib/flydata/source_postgresql/table_meta.rb +7 -3
  25. data/spec/flydata/fluent-plugins/in_mysql_binlog_flydata_spec.rb +1 -0
  26. data/spec/flydata/fluent-plugins/in_postgresql_query_based_flydata_spec.rb +2 -1
  27. data/spec/flydata/source_postgresql/query_based_sync/diff_query_generator_spec.rb +45 -11
  28. data/spec/flydata/source_postgresql/table_meta_spec.rb +19 -0
  29. data/tmpl/redshift_postgresql_data_entry.conf.tmpl +13 -0
  30. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b335ff2222169fc42a78d4dec2a5829aa0c94230
4
- data.tar.gz: 15042a74de15a15b3784c6917973154d1ddd02c7
3
+ metadata.gz: 4c1e7d064581a7d76441fa77bc814c1ebba986cd
4
+ data.tar.gz: bb14ff1bbf83246092ddbc44332dadc42725e1f7
5
5
  SHA512:
6
- metadata.gz: 86fb1aa9fe1449e11f0d8755773cd7a083054bb668df7dbf489973828b018f9ebe2e9a10850bcf8ca7afff7682f157cd7d3a3082907b458ad1b5cbba99cf73b5
7
- data.tar.gz: d36c033143bdd9a1b5931d891406ec6dfbaeaeba70cdae08d78c3282ccf55bebbe3125921bec021bbbb9405907a52431ac0f220722fa10f771db421134c7f348
6
+ metadata.gz: b3fee3b20cdb7ef01f45a05d3485e637fcb794c15c4bb31e2027535241db6ee31de9c7b97e8b5a4ab1892b415a1a26a336c525d8a9a19b960c2d4b5e5677006e
7
+ data.tar.gz: 477339f6ca63c80d13a9d499e062c5e42cae23a99a24d058f37fdbea01829084c9f137e7c00baa88bc56ff5389fd858119fc2c4ce1583358505e9dd7bfac4d05
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.0
1
+ 0.7.1
@@ -1,7 +1,7 @@
1
1
  #include <iostream>
2
2
  #include "sql_parser.h"
3
3
 
4
- static int $DEBUG = 0;
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 ($DEBUG) std::cout << "value[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "first_zero[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "leading_zero[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "number[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "null_n[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "null_u[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "null_l1[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "null_l2[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "hex_blob[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "hex_blob_number[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "first_char[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_2_firstbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_3_firstbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_3_secondbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_3_err_secondbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_4_firstbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_4_secondbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_4_err_secondbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_4_thirdbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "utf8_4_err_thirdbyte[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "following_char[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "escape[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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 ($DEBUG) std::cout << "error[" << *current << std::endl << std::flush;
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 ($DEBUG) std::cout << "default[" << *current << "(0x" << std::hex << (*current & 0xff) << ")]: " << std::endl << std::flush;
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/config'
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 = PGconn.connect(FlydataCore::Postgresql::Config.opts_for_pg(@option))
41
- client.exec(query)
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 = if option[:schema].to_s.strip.empty?
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(',')
@@ -2,6 +2,7 @@ require 'pg'
2
2
  require 'socket'
3
3
  require 'delegate'
4
4
  require 'flydata-core/postgresql/config'
5
+ require 'flydata-core/postgresql/query_helper'
5
6
 
6
7
  module FlydataCore
7
8
  module Postgresql
@@ -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,
@@ -59,6 +59,10 @@ class SourcePos
59
59
  attr_reader :to_snapshot
60
60
  attr_reader :pk_values
61
61
 
62
+ def empty?
63
+ @snapshot.empty?
64
+ end
65
+
62
66
  def to_s
63
67
  pk_values = @pk_values ? @pk_values.to_json : ''
64
68
  "#{@snapshot}\t#{@to_snapshot}\t#{pk_values}"
@@ -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'}, # TODO support
14
- 'serial8' => {type: 'serial8'}, # TODO support
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'}, # TODO support
59
- 'serial2' => {type: 'serial2'}, # TODO support
60
- 'serial' => {type: 'serial4'}, # TODO support
61
- 'serial4' => {type: 'serial4'}, # TODO support
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 = self.parse_money(remove_single_quote(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
- params.split(",").each_with_index do |param, i|
419
- final_params << (/\d+/.match(param) && max_size_a[i] && param.to_i > max_size_a[i].to_i ?
420
- max_size_a[i] : param)
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(65,30)"
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,30)| }
495
+ let(:type_sql) { %Q|"value" numeric(38,0)| }
468
496
 
469
497
  it_behaves_like *examples
470
498
  end
471
- context 'when scale exceeds max allowed' do #possible in postgresql
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(100,44)"
506
+ column[:type] = "numeric(38,38)" #0+38 => 0+37 digits
478
507
  end
479
- #this column accepts only 1 digit above decimal point. Other values such as 10 cause DDE.
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,37)| }
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
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"
@@ -10,6 +10,7 @@ class DataEntry < Source::DataEntry
10
10
  tables: {},
11
11
  tables_append_only: {},
12
12
  host: {},
13
+ port: {},
13
14
  username: {},
14
15
  password: {encrypted: true},
15
16
  ssl_ca_content: {},
@@ -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
@@ -10,6 +10,7 @@ class DataEntry < Source::DataEntry
10
10
  tables: {},
11
11
  tables_append_only: {},
12
12
  host: {},
13
+ port: {},
13
14
  username: {},
14
15
  password: {encrypted: true},
15
16
  schema: {},
@@ -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 schema).each do |k|
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 = $1 AND relname in (%s)
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
- query = FlydataCore::Postgresql::PGQuery.new(DUMP_SIZE_QUERY,
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: TABLE_PLACEHOLDER_START_NUM)
51
- res = cli.query(query, [de_prefs['schema']] + tables)
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({}) {|h, (c, i)| overrider = VALUE_OVERRIDERS[types[i]]; h[c] = overrider if overrider; h}
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) # 64mb
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
@@ -250,6 +250,7 @@ EOT
250
250
  'tables' => 'test_table,test_table_1,test_table_2',
251
251
  'tables_append_only' => 'test_table_3',
252
252
  'host' => 'localhost',
253
+ 'port' => 3306,
253
254
  'username' => 'test_admin',
254
255
  'password' => 'test_password',
255
256
  'ssl_ca_content' => '',
@@ -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 3306
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 numeric' do
74
+ it 'returns money values as is' do
75
75
  is_expected.to eq(<<EOS)
76
- SELECT "id", "value"::numeric 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;
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
- let(:type) { 'bit' }
135
- let(:value) { '0110000010' }
136
- it { is_expected.to eq '386' }
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 value is nil' do
147
- let(:type) { 'bit' }
148
- let(:value) { nil }
149
- it { is_expected.to eq nil }
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 numeric' do
205
- is_expected.to eq %Q["id", "value"::numeric]
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.0
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-01 00:00:00.000000000 Z
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.