aliyun-odps 0.1.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +31 -0
- data/Gemfile +3 -0
- data/README.md +55 -12
- data/Rakefile +15 -5
- data/aliyun-odps.gemspec +22 -11
- data/bin/console +10 -3
- data/lib/aliyun/odps.rb +69 -2
- data/lib/aliyun/odps/authorization.rb +90 -0
- data/lib/aliyun/odps/client.rb +40 -0
- data/lib/aliyun/odps/configuration.rb +16 -0
- data/lib/aliyun/odps/error.rb +97 -0
- data/lib/aliyun/odps/http.rb +138 -0
- data/lib/aliyun/odps/list.rb +40 -0
- data/lib/aliyun/odps/model/function.rb +16 -0
- data/lib/aliyun/odps/model/functions.rb +113 -0
- data/lib/aliyun/odps/model/instance.rb +130 -0
- data/lib/aliyun/odps/model/instance_task.rb +30 -0
- data/lib/aliyun/odps/model/instances.rb +119 -0
- data/lib/aliyun/odps/model/projects.rb +73 -0
- data/lib/aliyun/odps/model/resource.rb +26 -0
- data/lib/aliyun/odps/model/resources.rb +144 -0
- data/lib/aliyun/odps/model/table.rb +37 -0
- data/lib/aliyun/odps/model/table_column.rb +13 -0
- data/lib/aliyun/odps/model/table_partition.rb +9 -0
- data/lib/aliyun/odps/model/table_partitions.rb +90 -0
- data/lib/aliyun/odps/model/table_schema.rb +13 -0
- data/lib/aliyun/odps/model/tables.rb +125 -0
- data/lib/aliyun/odps/model/task_result.rb +9 -0
- data/lib/aliyun/odps/modelable.rb +16 -0
- data/lib/aliyun/odps/project.rb +47 -0
- data/lib/aliyun/odps/service_object.rb +27 -0
- data/lib/aliyun/odps/struct.rb +126 -0
- data/lib/aliyun/odps/tunnel/download_session.rb +98 -0
- data/lib/aliyun/odps/tunnel/router.rb +15 -0
- data/lib/aliyun/odps/tunnel/snappy_reader.rb +19 -0
- data/lib/aliyun/odps/tunnel/snappy_writer.rb +45 -0
- data/lib/aliyun/odps/tunnel/table_tunnels.rb +81 -0
- data/lib/aliyun/odps/tunnel/upload_block.rb +9 -0
- data/lib/aliyun/odps/tunnel/upload_session.rb +132 -0
- data/lib/aliyun/odps/utils.rb +102 -0
- data/lib/aliyun/odps/version.rb +1 -1
- data/requirements.png +0 -0
- data/wiki/error.md +188 -0
- data/wiki/functions.md +39 -0
- data/wiki/get_start.md +34 -0
- data/wiki/installation.md +15 -0
- data/wiki/instances.md +32 -0
- data/wiki/projects.md +51 -0
- data/wiki/resources.md +62 -0
- data/wiki/ssl.md +7 -0
- data/wiki/tables.md +75 -0
- data/wiki/tunnels.md +80 -0
- metadata +195 -13
- data/requirements.mindnode/QuickLook/Preview.jpg +0 -0
- data/requirements.mindnode/contents.xml +0 -10711
- data/requirements.mindnode/viewState.plist +0 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
class TablePartitions < ServiceObject
|
4
|
+
# List partitions for table
|
5
|
+
#
|
6
|
+
# @see http://repo.aliyun.com/api-doc/Table/get_table_partition/index.html Get table partitions
|
7
|
+
#
|
8
|
+
# @param options [Hash] options
|
9
|
+
# @option options [String] :marker specify marker for paginate
|
10
|
+
# @option options [String] :maxitems (1000) specify maxitems in this request
|
11
|
+
#
|
12
|
+
# @return [List]
|
13
|
+
def list(options = {})
|
14
|
+
Utils.stringify_keys!(options)
|
15
|
+
path = "/projects/#{project.name}/tables/#{master.name}"
|
16
|
+
query = {
|
17
|
+
partitions: true,
|
18
|
+
expectmarker: true
|
19
|
+
}.merge(Utils.hash_slice(options, 'marker', 'maxitems'))
|
20
|
+
|
21
|
+
result = client.get(path, query: query).parsed_response
|
22
|
+
Aliyun::Odps::List.build(result, %w(Partitions Partition)) do |hash|
|
23
|
+
build_table_partition(hash)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create Partition
|
28
|
+
#
|
29
|
+
# @param partition [Hash<name, value>] specify the partition you want to create
|
30
|
+
#
|
31
|
+
# @raise InstanceTaskNotSuccessError if instance task failed
|
32
|
+
#
|
33
|
+
# @return [Hash] the partition you create
|
34
|
+
def create(partition)
|
35
|
+
sql = generate_create_sql(partition)
|
36
|
+
|
37
|
+
task = InstanceTask.new(
|
38
|
+
name: 'SQLCreatePartitionTask',
|
39
|
+
type: 'SQL',
|
40
|
+
query: sql
|
41
|
+
)
|
42
|
+
|
43
|
+
instance = project.instances.create([task])
|
44
|
+
|
45
|
+
instance.wait_for_success
|
46
|
+
|
47
|
+
partition
|
48
|
+
end
|
49
|
+
|
50
|
+
# Delete Partition
|
51
|
+
#
|
52
|
+
# @params partitions [Hash<name, value>] specify the partition you want to delete
|
53
|
+
#
|
54
|
+
# @raise InstanceTaskNotSuccessError if instance task failed
|
55
|
+
#
|
56
|
+
# @return [true]
|
57
|
+
def delete(partition)
|
58
|
+
sql = generate_drop_sql(partition)
|
59
|
+
|
60
|
+
task = InstanceTask.new(
|
61
|
+
name: 'SQLDropPartitionTask',
|
62
|
+
type: 'SQL',
|
63
|
+
query: sql
|
64
|
+
)
|
65
|
+
|
66
|
+
instance = project.instances.create([task])
|
67
|
+
|
68
|
+
instance.wait_for_success
|
69
|
+
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def generate_create_sql(partition)
|
76
|
+
spec = partition.map { |k, v| "#{k} = '#{v}'" }.join(',')
|
77
|
+
"ALTER TABLE #{project.name}.`#{master.name}` ADD PARTITION (#{spec});"
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate_drop_sql(partition)
|
81
|
+
spec = partition.map { |k, v| "#{k} = '#{v}'" }.join(',')
|
82
|
+
"ALTER TABLE #{project.name}.`#{master.name}`DROP IF EXISTS PARTITION (#{spec});"
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_table_partition(hash)
|
86
|
+
Hash[Utils.wrap(hash['Column']).map(&:values)]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
class TableSchema < Struct::Base
|
4
|
+
property :columns, Array, init_with: ->(value) do
|
5
|
+
value.map { |v| TableColumn.new(v) }
|
6
|
+
end
|
7
|
+
|
8
|
+
property :partitions, Array, init_with: ->(value) do
|
9
|
+
value.map { |v| TablePartition.new(v) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
# Methods for Tables
|
4
|
+
class Tables < ServiceObject
|
5
|
+
# List tables in this project
|
6
|
+
#
|
7
|
+
# @see http://repo.aliyun.com/api-doc/Table/get_tables/index.html Get tables
|
8
|
+
#
|
9
|
+
# @params options [Hash] options
|
10
|
+
# @option options [String] :name specify the table name
|
11
|
+
# @option options [String] :owner specify the table owner
|
12
|
+
# @option options [String] :marker
|
13
|
+
# @option options [String] :maxitems (1000)
|
14
|
+
def list(options = {})
|
15
|
+
Utils.stringify_keys!(options)
|
16
|
+
path = "/projects/#{project.name}/tables"
|
17
|
+
query = Utils.hash_slice(options, 'name', 'owner', 'marker', 'maxitems')
|
18
|
+
query.merge!(tables: true, expectmarker: true)
|
19
|
+
result = client.get(path, query: query).parsed_response
|
20
|
+
|
21
|
+
Aliyun::Odps::List.build(result, %w(Tables Table)) do |hash|
|
22
|
+
build_table(hash)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get Table
|
27
|
+
#
|
28
|
+
# @see http://repo.aliyun.com/api-doc/Table/get_table/index.html Get Table
|
29
|
+
#
|
30
|
+
# @params name [String] specify the table name
|
31
|
+
def get(name)
|
32
|
+
path = "/projects/#{project.name}/tables/#{name}"
|
33
|
+
resp = client.get(path)
|
34
|
+
|
35
|
+
build_table(
|
36
|
+
Utils.dig_value(resp.parsed_response, 'Table')
|
37
|
+
.merge(
|
38
|
+
'creation_time' => resp.headers['x-odps-creation-time'],
|
39
|
+
'last_modified' => resp.headers['Last-Modified'],
|
40
|
+
'owner' => resp.headers['x-odps-owner']
|
41
|
+
)
|
42
|
+
)
|
43
|
+
end
|
44
|
+
alias_method :table, :get
|
45
|
+
|
46
|
+
# Create Table
|
47
|
+
#
|
48
|
+
# @params name [String] specify table name
|
49
|
+
# @params schema [Struct::TableSchema] specify table schema
|
50
|
+
# @params options [Hash] options
|
51
|
+
# @option options [String] :comment specify table comment
|
52
|
+
#
|
53
|
+
# @raise InstanceTaskNotSuccessError if instance task failed
|
54
|
+
#
|
55
|
+
# @return Table
|
56
|
+
def create(name, schema, options = {})
|
57
|
+
Utils.stringify_keys!(options)
|
58
|
+
table = Table.new(name: name, schema: schema, project: project)
|
59
|
+
table.comment = options['comment'] if options.key?('comment')
|
60
|
+
|
61
|
+
task = InstanceTask.new(name: 'SQLCreateTableTask', type: 'SQL', query: generate_create_sql(table))
|
62
|
+
|
63
|
+
instance = project.instances.create([task])
|
64
|
+
instance.wait_for_success
|
65
|
+
table
|
66
|
+
end
|
67
|
+
|
68
|
+
# Delete Table
|
69
|
+
#
|
70
|
+
# @params name [String]
|
71
|
+
#
|
72
|
+
# @raise InstanceTaskNotSuccessError if instance task failed
|
73
|
+
#
|
74
|
+
# @return true
|
75
|
+
def delete(name)
|
76
|
+
table = Table.new(name: name, project: project)
|
77
|
+
|
78
|
+
task = InstanceTask.new(
|
79
|
+
name: 'SQLDropTableTask',
|
80
|
+
type: 'SQL',
|
81
|
+
query: generate_drop_sql(table)
|
82
|
+
)
|
83
|
+
|
84
|
+
instance = project.instances.create([task])
|
85
|
+
instance.wait_for_success
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def generate_create_sql(table)
|
92
|
+
sql = "CREATE TABLE #{project.name}.`#{table.name}`"
|
93
|
+
sql += generate_table_coumns_sql(table.schema.columns) if table.schema && table.schema.columns
|
94
|
+
sql += " COMMENT '#{table.comment}'" if table.comment
|
95
|
+
sql += generate_table_partitions_sql(table.schema.partitions) if table.schema && table.schema.partitions
|
96
|
+
sql += ';'
|
97
|
+
sql
|
98
|
+
end
|
99
|
+
|
100
|
+
def generate_table_coumns_sql(columns)
|
101
|
+
' (' + columns.map do |column|
|
102
|
+
generate_table_column_sql(column)
|
103
|
+
end.join(', ') + ')'
|
104
|
+
end
|
105
|
+
|
106
|
+
def generate_table_partitions_sql(partitions)
|
107
|
+
' PARTITIONED BY (' + partitions.map do |column|
|
108
|
+
generate_table_column_sql(column)
|
109
|
+
end.join(', ') + ')'
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_table_column_sql(column)
|
113
|
+
"`#{column.name}` #{column.type}" + (column.comment ? " COMMENT '#{column.comment}'" : '')
|
114
|
+
end
|
115
|
+
|
116
|
+
def generate_drop_sql(table)
|
117
|
+
"DROP TABLE IF EXISTS #{project.name}.`#{table.name}`;"
|
118
|
+
end
|
119
|
+
|
120
|
+
def build_table(result)
|
121
|
+
Table.new(result.merge(project: project))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'aliyun/odps/service_object'
|
3
|
+
|
4
|
+
module Aliyun
|
5
|
+
module Odps
|
6
|
+
module Modelable
|
7
|
+
def has_many(models, _opts = {})
|
8
|
+
mod = models.to_s.singularize
|
9
|
+
klass = "Aliyun::Odps::#{mod.camelize}s".constantize
|
10
|
+
define_method(models) do
|
11
|
+
klass.build(self)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'aliyun/odps/model/instances'
|
2
|
+
require 'aliyun/odps/model/tables'
|
3
|
+
require 'aliyun/odps/model/functions'
|
4
|
+
require 'aliyun/odps/model/resources'
|
5
|
+
require 'aliyun/odps/tunnel/table_tunnels'
|
6
|
+
require 'aliyun/odps/client'
|
7
|
+
|
8
|
+
module Aliyun
|
9
|
+
module Odps
|
10
|
+
class Project < Struct::Base
|
11
|
+
extend Aliyun::Odps::Modelable
|
12
|
+
|
13
|
+
# @!method instances
|
14
|
+
# @return [Instances]
|
15
|
+
has_many :instances
|
16
|
+
|
17
|
+
# @!method functions
|
18
|
+
# @return [Functions]
|
19
|
+
has_many :functions
|
20
|
+
|
21
|
+
# @!method tables
|
22
|
+
# @return [Tables]
|
23
|
+
has_many :tables
|
24
|
+
|
25
|
+
# @!method resources
|
26
|
+
# @return [Resources]
|
27
|
+
has_many :resources
|
28
|
+
|
29
|
+
# @!method table_tunnels
|
30
|
+
# @return [TableTunnels]
|
31
|
+
has_many :table_tunnels
|
32
|
+
|
33
|
+
property :client, Client, required: true
|
34
|
+
|
35
|
+
property :name, String, required: true
|
36
|
+
property :comment, String
|
37
|
+
property :project_group_name, String
|
38
|
+
property :state, String
|
39
|
+
property :clusters, Hash
|
40
|
+
property :property, String
|
41
|
+
property :properties, Hash
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Dir[File.join(File.dirname(__FILE__), 'model/*.rb')].each { |f| require f }
|
47
|
+
Dir[File.join(File.dirname(__FILE__), 'tunnel/*.rb')].each { |f| require f }
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
class ServiceObject
|
4
|
+
def client
|
5
|
+
master.is_a?(Client) ? master : project.client
|
6
|
+
end
|
7
|
+
|
8
|
+
def project
|
9
|
+
@master.is_a?(Project) ? @master : @master.try(:project)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :master
|
13
|
+
|
14
|
+
def initialize(master, _options = {})
|
15
|
+
@master = master
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.service_pool
|
19
|
+
@service_pool ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.build(master, options = {})
|
23
|
+
service_pool[master] ||= new(master, options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
module Struct
|
4
|
+
class Base
|
5
|
+
def initialize(attributes = {})
|
6
|
+
attributes.each do |key, value|
|
7
|
+
m = "#{Utils.underscore(key)}=".to_sym
|
8
|
+
send(m, value) if self.respond_to?(m)
|
9
|
+
end
|
10
|
+
|
11
|
+
validate_required
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate_required
|
15
|
+
missing_attrs = []
|
16
|
+
(self.class.required_attrs || []).each do |attr|
|
17
|
+
missing_attrs.push(attr) if send(attr).nil?
|
18
|
+
end
|
19
|
+
fail "Missing attribute: #{missing_attrs.join(',')}" unless missing_attrs.empty?
|
20
|
+
end
|
21
|
+
private :validate_required
|
22
|
+
|
23
|
+
def update_attrs(attrs)
|
24
|
+
attrs.each do |k, v|
|
25
|
+
send("#{Utils.underscore(k)}=", v)
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def client
|
31
|
+
project.client
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
attr_reader :required_attrs
|
36
|
+
|
37
|
+
# @!macro [attach] property
|
38
|
+
# @!attribute [rw] $1
|
39
|
+
# @return [$2]
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
#
|
43
|
+
# property :name, String, required: true, init_with: proc {|value| value.upcase }, within: %w{value1 value2}
|
44
|
+
#
|
45
|
+
# @params options [Hash] options
|
46
|
+
# @option options [Boolean] :required required or optional
|
47
|
+
# @option options [block] :init_with block used to init attr
|
48
|
+
def property(attr, type, options = {})
|
49
|
+
@required_attrs ||= []
|
50
|
+
@required_attrs << attr.to_s if options[:required]
|
51
|
+
|
52
|
+
define_reader_method(attr)
|
53
|
+
define_writer_method(attr, type, options)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def define_reader_method(attr)
|
59
|
+
attr_reader attr
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_writer_method(attr, type, options)
|
63
|
+
init_with_block =
|
64
|
+
if options.key?(:init_with) && options[:init_with].respond_to?(:call)
|
65
|
+
options[:init_with]
|
66
|
+
else
|
67
|
+
build_block_with_type(attr, type, options)
|
68
|
+
end
|
69
|
+
define_writer_method_with_block(attr, init_with_block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_block_with_type(attr, type, options)
|
73
|
+
case type.to_s
|
74
|
+
when 'Integer'
|
75
|
+
build_integer_block(attr, options)
|
76
|
+
when 'DateTime'
|
77
|
+
build_datetime_block(attr, options)
|
78
|
+
when 'String'
|
79
|
+
build_string_block(attr, options)
|
80
|
+
else
|
81
|
+
build_default_block(attr, options)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_string_block(attr, options)
|
86
|
+
if options.key?(:within) && options[:within].is_a?(Array)
|
87
|
+
build_block_with_within(attr, options)
|
88
|
+
else
|
89
|
+
build_default_block(attr, options)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_block_with_within(attr, options)
|
94
|
+
proc do |value|
|
95
|
+
if options[:within].include?(value.to_s)
|
96
|
+
value
|
97
|
+
else
|
98
|
+
fail ValueNotSupportedError.new(attr, options[:within])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_integer_block(attr, options)
|
104
|
+
proc { |value| value.to_i }
|
105
|
+
end
|
106
|
+
|
107
|
+
def build_datetime_block(attr, options)
|
108
|
+
proc { |value| DateTime.parse(value) }
|
109
|
+
end
|
110
|
+
|
111
|
+
def build_default_block(attr, options)
|
112
|
+
proc { |value| value }
|
113
|
+
end
|
114
|
+
|
115
|
+
def define_writer_method_with_block(attr, block)
|
116
|
+
define_method("#{attr}=") do |value|
|
117
|
+
instance_eval do
|
118
|
+
instance_variable_set("@#{attr}", block.call(value))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|