flydata 0.3.21 → 0.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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