flydata 0.3.21 → 0.3.22

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c4aa0461989a989042cf8117321b6fb0a25290b
4
- data.tar.gz: 489443d11c79f35f1736bf52c61b438b7dfe7e54
3
+ metadata.gz: 9e5b9816ca7b0901b9716d3361a344f8842f4e67
4
+ data.tar.gz: 6c1061fd528b620f93273b5ba737da1f78d81803
5
5
  SHA512:
6
- metadata.gz: f1cf691ead10def34ef2f61fc1be6d879a1dc3761e4cda2610b47afd05e227f8b435eece8b75dfbc4b338eb774848f0bd70474ed3fa53e702da8d4fbddd7bad9
7
- data.tar.gz: 73ba527eecb4925518fcf34f432cc397ee6059ff4c166c199666c5dd9e3601fd8a5fb245d32a0fcae4f4ea62bf9ba4b9c6738a88c8f8e99be302c5bfc60bd0b6
6
+ metadata.gz: 3b8295cd89f049cde9393e02bd8ea07b37f0cc08d0584bfde3ae54b735af78172bcc6157045f58a81dfffab05361df8dc83886be48f881777f4656b3d2626825
7
+ data.tar.gz: 6f67eac41f7ba8a7794d696c1b7bfe2466628a54153be62b40ace4a8fce70b4f4201ec44807e9609dd565b5021b8682551e68072f22101cb78fb1763e76173c8
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.21
1
+ 0.3.22
@@ -2,22 +2,35 @@ module FlydataCore
2
2
  module Redshift
3
3
 
4
4
  class String
5
+ NULL_CHAR_CODE_POINTS = "\\0".codepoints
6
+
5
7
  # Redshift supports UTF-8 but it enforces stricter rule than other
6
8
  # implementations such as MySQL or Ruby. This method returns a
7
9
  # Redshift-safe string from the given string.
8
10
  def self.encode(string, options = {})
9
- result = string.encoding == Encoding::UTF_8 ?
10
- string.encode(Encoding::UTF_16, options).encode(Encoding::UTF_8) :
11
- string.encode(Encoding::UTF_8, options)
12
- rep_str = options[:replace] || "\uFFFD"
13
- result.each_codepoint.with_index(0) do |cp, i|
11
+ encoded_str = string.encoding == Encoding::UTF_8 ?
12
+ string.encode(Encoding::UTF_16, options).encode(Encoding::UTF_8) :
13
+ string.encode(Encoding::UTF_8, options)
14
+
15
+ ret_cpts = []
16
+ rep_str_cpts = (options[:replace] || "\uFFFD").codepoints
17
+ replaced = false
18
+
19
+ encoded_str.each_codepoint do |cp|
14
20
  # Per Redshift document
15
21
  # http://docs.aws.amazon.com/redshift/latest/dg/multi-byte-character-load-errors.html
16
22
  if cp >= 0xFDD0 && cp <= 0xFDEF || cp == 0xFFFE || cp == 0xFFFF
17
- result[i] = rep_str
23
+ ret_cpts.concat(rep_str_cpts)
24
+ replaced = true
25
+ elsif cp == 0x0000
26
+ ret_cpts.concat(NULL_CHAR_CODE_POINTS)
27
+ replaced = true
28
+ else
29
+ ret_cpts << cp
18
30
  end
19
31
  end
20
- result
32
+
33
+ replaced ? ret_cpts.pack("U*") : encoded_str
21
34
  end
22
35
  end
23
36
 
@@ -211,17 +211,17 @@ EOS
211
211
 
212
212
  def self.primary_key_sql(flydata_tabledef)
213
213
  pks = primary_keys(flydata_tabledef)
214
- pks.empty? ? nil : " PRIMARY KEY (#{pks.join(',')})"
214
+ pks.empty? ? nil : " PRIMARY KEY (#{pks.collect{|pk| %Q|"#{pk}"|}.join(',')})"
215
215
  end
216
216
 
217
217
  def self.primary_keys(flydata_tabledef)
218
- flydata_tabledef[:columns].select{|col| col[:primary_key]}.collect{|col| col[:column]}
218
+ flydata_tabledef[:columns].select{|col| col[:primary_key]}.collect{|col| convert_to_valid_column_name(col[:column])}
219
219
  end
220
220
 
221
221
  def self.distkey_sortkey_sql(flydata_tabledef)
222
222
  pks = primary_keys(flydata_tabledef)
223
223
  return nil if pks.empty?
224
- " DISTKEY(#{pks.first}) SORTKEY(#{pks.join(',')})"
224
+ %Q| DISTKEY("#{pks.first}") SORTKEY(#{pks.collect{|pk| %Q|"#{pk}"|}.join(',')})|
225
225
  end
226
226
 
227
227
  def self.comment_sql(flydata_tabledef, schema_name)
@@ -230,7 +230,7 @@ EOS
230
230
  next unless col[:comment]
231
231
 
232
232
  sql += <<EOS
233
- COMMENT ON COLUMN #{table_name_for_ddl(flydata_tabledef[:table_name], schema_name)}."#{col[:column]}"
233
+ COMMENT ON COLUMN #{table_name_for_ddl(flydata_tabledef[:table_name], schema_name)}."#{convert_to_valid_column_name(col[:column])}"
234
234
  IS '#{col[:comment]}';
235
235
  EOS
236
236
  end
@@ -289,7 +289,7 @@ EOS
289
289
  def self.convert_to_valid_name(key)
290
290
  name = key.downcase.gsub(/[^a-z0-9_$]/, '_')
291
291
  do_convert = yield name if block_given?
292
- name = "_#{name}" if do_convert || name =~ /^[0-9$]/
292
+ name = "_#{name}" if do_convert
293
293
  if name.length > MAX_COLUMNNAME_LENGTH
294
294
  raise NameTooLongError, "Converted name is too long. size: #{name.size} name: #{name}"
295
295
  end
@@ -358,7 +358,7 @@ EOS
358
358
  def self.parse_date(value)
359
359
  return nil if value.nil?
360
360
  value_str = value.to_s
361
- dt = Date.parse(convert_year_into_date(value_str))
361
+ dt = Date.parse(convert_date(value_str))
362
362
  dt.strftime('%Y-%m-%d')
363
363
  rescue ArgumentError => ae
364
364
  # '0000-00-00' is valid for mysql date column
@@ -366,18 +366,28 @@ EOS
366
366
  raise ae
367
367
  end
368
368
 
369
+ CONVERT_ZERO_DATE_REGEXP = /^0000|-00/
370
+ CONVERT_ZERO_DATE_HASH_TABLE = {"0000" => "0001", "-00" => "-01" }
371
+
372
+ def self.convert_date(value)
373
+ value = convert_year_into_date(value)
374
+ # Convert any 00 value to 01
375
+ value.gsub(CONVERT_ZERO_DATE_REGEXP, CONVERT_ZERO_DATE_HASH_TABLE)
376
+ end
377
+
369
378
  def self.convert_year_into_date(value)
370
- if value == '0' || value == '0000'
371
- converted_value = '0001-01-01' # '0001-01-01' is the "base" date
379
+ return value unless /^\d+$/.match(value)
380
+ # Only positive integers are allowed for year type
381
+ val_i = value.to_i
382
+
383
+ if val_i == 0 # '0' or '0000'
384
+ '0001-01-01' # '0001-01-01' is the "base" date
385
+ elsif value.length == 4
386
+ "#{value}-01-01"
387
+ elsif value.length == 2
388
+ "#{val_i >= 70 ? "19" : "20"}#{value}-01-01"
372
389
  else
373
- case value
374
- when /^\d{4}$/
375
- converted_value = "#{value}-01-01"
376
- when /^\d{2}$/
377
- converted_value = "#{value.to_i >= 70 ? "19" : "20"}#{value}-01-01"
378
- else
379
- converted_value = value # Return the value as is
380
- end
390
+ value # Return the value as is
381
391
  end
382
392
  end
383
393
  end
@@ -39,7 +39,7 @@ describe String do
39
39
  end
40
40
  context 'invalid char' do
41
41
  let(:string) { "\xeb\x13\x00" }
42
- let(:result) { "\uFFFD\u0013\u0000".encode(Encoding::UTF_8) }
42
+ let(:result) { "\uFFFD\u0013\\0".encode(Encoding::UTF_8) }
43
43
 
44
44
  it_behaves_like "converting string to a Redshift friendly string"
45
45
  end
@@ -53,6 +53,12 @@ describe String do
53
53
  let(:string) { "\xef\xbf\xbf" }
54
54
  let(:result) { "\uFFFD".encode(Encoding::UTF_8) }
55
55
 
56
+ it_behaves_like "converting string to a Redshift friendly string"
57
+ end
58
+ context 'null chars' do
59
+ let(:string) { "\x00\x00\x00aabbcc\x00\x00ddeeff\x00あい\x00う\x00\x00" }
60
+ let(:result) { "\\0\\0\\0aabbcc\\0\\0ddeeff\\0あい\\0う\\0\\0".encode(Encoding::UTF_8) }
61
+
56
62
  it_behaves_like "converting string to a Redshift friendly string"
57
63
  end
58
64
  end
@@ -76,8 +76,8 @@ CREATE TABLE "test_table_all" (
76
76
  "col_year" date DEFAULT NULL,
77
77
  "col_year_4" date DEFAULT NULL,
78
78
  "col_year_2" date DEFAULT NULL,
79
- PRIMARY KEY (id)
80
- ) DISTKEY(id) SORTKEY(id);
79
+ PRIMARY KEY ("id")
80
+ ) DISTKEY("id") SORTKEY("id");
81
81
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'test_table_all';
82
82
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
83
83
  ('test_table_all', 'id', 'int8(20)', 1),
@@ -178,8 +178,8 @@ CREATE TABLE "bit_test_def_1" (
178
178
  "id" int4 NOT NULL,
179
179
  "bit_value" bigint DEFAULT 1,
180
180
  "int_value" int4 DEFAULT 16,
181
- PRIMARY KEY (id)
182
- ) DISTKEY(id) SORTKEY(id);
181
+ PRIMARY KEY ("id")
182
+ ) DISTKEY("id") SORTKEY("id");
183
183
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'bit_test_def_1';
184
184
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
185
185
  ('bit_test_def_1', 'id', 'int4(11)', 1),
@@ -227,8 +227,8 @@ CREATE TABLE "product_order" (
227
227
  "product_category" int4 NOT NULL,
228
228
  "product_id" int4 NOT NULL,
229
229
  "customer_id" int4 NOT NULL,
230
- PRIMARY KEY (no)
231
- ) DISTKEY(no) SORTKEY(no);
230
+ PRIMARY KEY ("no")
231
+ ) DISTKEY("no") SORTKEY("no");
232
232
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'product_order';
233
233
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
234
234
  ('product_order', 'no', 'int4(11)', 1),
@@ -280,8 +280,8 @@ DROP TABLE IF EXISTS "test_table_column_comment";
280
280
  CREATE TABLE "test_table_column_comment" (
281
281
  "id" int4 NOT NULL DEFAULT '0',
282
282
  "value" varchar(max),
283
- PRIMARY KEY (id)
284
- ) DISTKEY(id) SORTKEY(id);
283
+ PRIMARY KEY ("id")
284
+ ) DISTKEY("id") SORTKEY("id");
285
285
  COMMENT ON COLUMN "test_table_column_comment"."id"
286
286
  IS 'this is primary key';
287
287
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'test_table_column_comment';
@@ -329,8 +329,8 @@ CREATE TABLE "test_table_enum" (
329
329
  "enum_1" varchar encode bytedict DEFAULT NULL,
330
330
  "enum_2" varchar encode bytedict DEFAULT 'a',
331
331
  "enum_3" varchar encode bytedict NOT NULL,
332
- PRIMARY KEY (id)
333
- ) DISTKEY(id) SORTKEY(id);
332
+ PRIMARY KEY ("id")
333
+ ) DISTKEY("id") SORTKEY("id");
334
334
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'test_table_enum';
335
335
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
336
336
  ('test_table_enum', 'id', 'int4(11)', 1),
@@ -379,8 +379,8 @@ CREATE TABLE "test_table_multi_pk" (
379
379
  "id1" int4 NOT NULL DEFAULT '0',
380
380
  "id2" int4 NOT NULL DEFAULT '0',
381
381
  "value" varchar(max),
382
- PRIMARY KEY (id1,id2)
383
- ) DISTKEY(id1) SORTKEY(id1,id2);
382
+ PRIMARY KEY ("id1","id2")
383
+ ) DISTKEY("id1") SORTKEY("id1","id2");
384
384
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'test_table_multi_pk';
385
385
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
386
386
  ('test_table_multi_pk', 'id1', 'int4(11)', 1),
@@ -436,8 +436,8 @@ CREATE TABLE "sample1" (
436
436
  "title" varchar(768) DEFAULT NULL,
437
437
  "name" varchar(max),
438
438
  "num" int4 DEFAULT NULL,
439
- PRIMARY KEY (id)
440
- ) DISTKEY(id) SORTKEY(id);
439
+ PRIMARY KEY ("id")
440
+ ) DISTKEY("id") SORTKEY("id");
441
441
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'sample1';
442
442
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
443
443
  ('sample1', 'id', 'int4(11)', 1),
@@ -489,8 +489,8 @@ CREATE TABLE "sample1" (
489
489
  "title" varchar(768) DEFAULT NULL,
490
490
  "name" varchar(max),
491
491
  "num" int4 DEFAULT NULL,
492
- PRIMARY KEY (id)
493
- ) DISTKEY(id) SORTKEY(id);
492
+ PRIMARY KEY ("id")
493
+ ) DISTKEY("id") SORTKEY("id");
494
494
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'sample1';
495
495
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
496
496
  ('sample1', 'id', 'int4(11)', 1),
@@ -556,8 +556,8 @@ CREATE TABLE "invoice_items" (
556
556
  "bill_ahead_resolved_at" timestamp DEFAULT NULL,
557
557
  "stripe_invoice_id" varchar(765) DEFAULT NULL,
558
558
  "is_vat" int2 NOT NULL DEFAULT '0',
559
- PRIMARY KEY (id)
560
- ) DISTKEY(id) SORTKEY(id);
559
+ PRIMARY KEY ("id")
560
+ ) DISTKEY("id") SORTKEY("id");
561
561
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'invoice_items';
562
562
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
563
563
  ('invoice_items', 'id', 'int4(11)', 1),
@@ -639,8 +639,8 @@ CREATE TABLE "zerofill_table" (
639
639
  "value_double" float8 DEFAULT NULL,
640
640
  "name" varchar(768) DEFAULT NULL,
641
641
  "value_small_int" int4 DEFAULT NULL,
642
- PRIMARY KEY (id)
643
- ) DISTKEY(id) SORTKEY(id);
642
+ PRIMARY KEY ("id")
643
+ ) DISTKEY("id") SORTKEY("id");
644
644
  DELETE FROM "flydata_ctl_columns" WHERE table_name = 'zerofill_table';
645
645
  INSERT INTO "flydata_ctl_columns" (table_name, column_name, src_data_type, ordinal_position) VALUES
646
646
  ('zerofill_table', 'id', 'int4(11)', 1),
@@ -5,10 +5,11 @@ module FlydataCore
5
5
  module TableDef
6
6
 
7
7
  describe RedshiftTableDef do
8
+ let(:subject_object) { described_class }
8
9
  let(:table_name) { 'test_table' }
9
10
 
10
11
  describe '.from_flydata_tabledef' do
11
- subject { described_class.from_flydata_tabledef(flydata_tabledef, option) }
12
+ subject { subject_object.from_flydata_tabledef(flydata_tabledef, option) }
12
13
 
13
14
  let(:value_column_hash) { { column: "value", type: "text" } }
14
15
  let(:value_column) { value_column_hash }
@@ -33,8 +34,8 @@ CREATE TABLE #{schema_prefix}"test_table" (
33
34
  "id" int4 NOT NULL,
34
35
  "age" int8,
35
36
  "value" varchar(max),
36
- PRIMARY KEY (id)
37
- ) DISTKEY(id) SORTKEY(id);
37
+ PRIMARY KEY ("id")
38
+ ) DISTKEY("id") SORTKEY("id");
38
39
  EOT
39
40
  let(:flydata_ctl_update) { <<EOT.strip }
40
41
  DELETE FROM #{schema_prefix}"flydata_ctl_columns" WHERE table_name = 'test_table';
@@ -126,7 +127,7 @@ EOT
126
127
  end
127
128
 
128
129
  describe '.column_def_sql' do
129
- subject { described_class.column_def_sql(column, opt) }
130
+ subject { subject_object.column_def_sql(column, opt) }
130
131
  let(:column) { {} }
131
132
  let(:opt) { {} }
132
133
 
@@ -524,7 +525,7 @@ EOT
524
525
  end
525
526
 
526
527
  describe '.flydata_ctl_columns_sql' do
527
- subject { described_class.flydata_ctl_columns_sql(flydata_tabledef, schema_name) }
528
+ subject { subject_object.flydata_ctl_columns_sql(flydata_tabledef, schema_name) }
528
529
  let(:flydata_tabledef) { {
529
530
  table_name: table_name,
530
531
  columns: [
@@ -572,7 +573,7 @@ EOS
572
573
 
573
574
  describe '.parse_timestamp' do
574
575
  let(:value) { nil }
575
- subject { described_class.parse_timestamp(value) }
576
+ subject { subject_object.parse_timestamp(value) }
576
577
 
577
578
  context 'with w3c format value' do
578
579
  context 'with sec' do
@@ -721,7 +722,7 @@ EOS
721
722
 
722
723
  describe '.parse_date' do
723
724
  let(:value) { nil }
724
- subject { described_class.parse_date(value) }
725
+ subject { subject_object.parse_date(value) }
725
726
 
726
727
  context 'with year values' do
727
728
  context 'with value string 0' do
@@ -758,6 +759,26 @@ EOS
758
759
  let(:value){ '69' }
759
760
  it { is_expected.to eq('2069-01-01') }
760
761
  end
762
+
763
+ context 'with value 259392-00-00' do
764
+ let(:value) { '259392-00-00' }
765
+ it { is_expected.to eq('259392-01-01') }
766
+ end
767
+
768
+ context 'with value 259392-00-04' do
769
+ let(:value) { '259392-00-04' }
770
+ it { is_expected.to eq('259392-01-04') }
771
+ end
772
+
773
+ context 'with value 0000-01-04' do
774
+ let(:value) { '0000-01-04' }
775
+ it { is_expected.to eq('0001-01-04') }
776
+ end
777
+
778
+ context 'with value 259392-40-04' do
779
+ let(:value) { '259392-40-04' }
780
+ it { expect{subject}.to raise_error(ArgumentError) }
781
+ end
761
782
  end
762
783
 
763
784
  context 'with date values' do
@@ -779,6 +800,45 @@ EOS
779
800
  it { is_expected.to eq(nil) }
780
801
  end
781
802
  end
803
+
804
+ shared_examples "expecting no underscore in front of numbers" do
805
+ # Redshift accepts a table/column name starting with numbers (e.g. 20grams) or even a number-only name (e.g. 12345)
806
+ context "with a key including numbers" do
807
+ let(:key) { "year2000" }
808
+ let(:expected_value) { key }
809
+ it { is_expected.to eq expected_value }
810
+ end
811
+
812
+ context "with a key only including numbers" do
813
+ let(:key) { "2000" }
814
+ let(:expected_value) { key }
815
+ it { is_expected.to eq expected_value }
816
+ end
817
+
818
+ context "with a key starting with numbers" do
819
+ let(:key) { "2000stars" }
820
+ let(:expected_value) { key }
821
+ it { is_expected.to eq expected_value }
822
+ end
823
+ end
824
+
825
+ describe ".convert_to_valid_name" do
826
+ subject { subject_object.convert_to_valid_name(key) }
827
+
828
+ it_behaves_like "expecting no underscore in front of numbers"
829
+ end
830
+
831
+ describe ".convert_to_valid_column_name" do
832
+ subject { subject_object.convert_to_valid_name(key) }
833
+
834
+ it_behaves_like "expecting no underscore in front of numbers"
835
+ end
836
+
837
+ describe ".convert_to_valid_table_name" do
838
+ subject { subject_object.convert_to_valid_name(key) }
839
+
840
+ it_behaves_like "expecting no underscore in front of numbers"
841
+ end
782
842
  end
783
843
 
784
844
  end
@@ -2,11 +2,11 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: flydata 0.3.21 ruby lib
5
+ # stub: flydata 0.3.22 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "flydata"
9
- s.version = "0.3.21"
9
+ s.version = "0.3.22"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
@@ -181,7 +181,7 @@ Gem::Specification.new do |s|
181
181
  ]
182
182
  s.homepage = "http://flydata.com/"
183
183
  s.licenses = ["All right reserved."]
184
- s.rubygems_version = "2.4.3"
184
+ s.rubygems_version = "2.2.2"
185
185
  s.summary = "FlyData Agent"
186
186
 
187
187
  if s.respond_to? :specification_version then
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.3.21
4
+ version: 0.3.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Fujikawa
@@ -621,7 +621,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
621
621
  version: '0'
622
622
  requirements: []
623
623
  rubyforge_project:
624
- rubygems_version: 2.4.3
624
+ rubygems_version: 2.2.2
625
625
  signing_key:
626
626
  specification_version: 4
627
627
  summary: FlyData Agent