flydata 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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.