athena-cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.projections.json +20 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +75 -0
  7. data/LICENSE.md +20 -0
  8. data/README.md +128 -0
  9. data/Rakefile +44 -0
  10. data/athena-cli.gemspec +26 -0
  11. data/bin/athena-cli +10 -0
  12. data/features/athena.feature +8 -0
  13. data/features/step_definitions/athena_steps.rb +6 -0
  14. data/features/support/env.rb +15 -0
  15. data/lib/amazon_athena.rb +7 -0
  16. data/lib/amazon_athena/cli.rb +406 -0
  17. data/lib/amazon_athena/client.rb +114 -0
  18. data/lib/amazon_athena/command.rb +16 -0
  19. data/lib/amazon_athena/commands.rb +20 -0
  20. data/lib/amazon_athena/commands/alter_table_add_partition.rb +29 -0
  21. data/lib/amazon_athena/commands/alter_table_drop_partition.rb +29 -0
  22. data/lib/amazon_athena/commands/create_database.rb +23 -0
  23. data/lib/amazon_athena/commands/create_table.rb +23 -0
  24. data/lib/amazon_athena/commands/describe_table.rb +21 -0
  25. data/lib/amazon_athena/commands/drop_database.rb +23 -0
  26. data/lib/amazon_athena/commands/drop_table.rb +23 -0
  27. data/lib/amazon_athena/commands/repair_table.rb +23 -0
  28. data/lib/amazon_athena/commands/show_columns.rb +22 -0
  29. data/lib/amazon_athena/commands/show_create_table.rb +22 -0
  30. data/lib/amazon_athena/commands/show_databases.rb +17 -0
  31. data/lib/amazon_athena/commands/show_partitions.rb +28 -0
  32. data/lib/amazon_athena/commands/show_table_properties.rb +36 -0
  33. data/lib/amazon_athena/commands/show_tables.rb +23 -0
  34. data/lib/amazon_athena/partition.rb +21 -0
  35. data/lib/amazon_athena/transformer.rb +19 -0
  36. data/lib/amazon_athena/version.rb +3 -0
  37. data/lib/jdbc_helper/athena.rb +38 -0
  38. data/lib/jdbc_helper/resultset.rb +14 -0
  39. data/log4j.xml +18 -0
  40. data/script/bootstrap +5 -0
  41. data/script/package +8 -0
  42. data/script/release +16 -0
  43. data/script/test +6 -0
  44. data/test/lib/amazon_athena/commands/alter_table_add_partition_test.rb +64 -0
  45. data/test/lib/amazon_athena/commands/alter_table_drop_partition_test.rb +61 -0
  46. data/test/lib/amazon_athena/commands/create_database_test.rb +27 -0
  47. data/test/lib/amazon_athena/commands/describe_table_test.rb +23 -0
  48. data/test/lib/amazon_athena/commands/drop_database_test.rb +22 -0
  49. data/test/lib/amazon_athena/commands/drop_table_test.rb +23 -0
  50. data/test/lib/amazon_athena/commands/repair_table_test.rb +20 -0
  51. data/test/lib/amazon_athena/commands/show_columns_test.rb +23 -0
  52. data/test/lib/amazon_athena/commands/show_create_table_test.rb +23 -0
  53. data/test/lib/amazon_athena/commands/show_databases_test.rb +23 -0
  54. data/test/lib/amazon_athena/commands/show_partitions_test.rb +23 -0
  55. data/test/lib/amazon_athena/commands/show_table_properties_test.rb +25 -0
  56. data/test/lib/amazon_athena/commands/show_tables_test.rb +23 -0
  57. data/test/test_helper.rb +1 -0
  58. metadata +185 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ea56f38290709fb3f5b0351f88cfa946b10d820f
4
+ data.tar.gz: ab1d16e5143dde991ef413f014c60456a8bacb01
5
+ SHA512:
6
+ metadata.gz: e8b87e4a1b879ac686194e0a91a8ff4cee7b292056acaf2bf9da2cacc9a78abd44c2ebd9825f2bf51492c7495d9c20efe1ef9aff2356d9b83f22c5969cad0a88
7
+ data.tar.gz: e1109e094eb8c5c33acb55254784bce9f43db3c012e5ae36d8379db12c00f0d1cf321404a4de07a61f2e97d69e0ca4690d274c724f86a2fa593d94914e6679e1
@@ -0,0 +1,5 @@
1
+ jdbc
2
+ pkg
3
+ queries
4
+ results.html
5
+ schemas
@@ -0,0 +1,20 @@
1
+ {
2
+ "test/*_test.rb": {
3
+ "alternate": [
4
+ "{}.rb"
5
+ ],
6
+ "type": "test"
7
+ },
8
+ "lib/amazon_athena/commands/*.rb": {
9
+ "alternate": [
10
+ "test/lib/amazon_athena/commands/{}_test.rb"
11
+ ],
12
+ "type": "command"
13
+ },
14
+ "lib/*.rb": {
15
+ "alternate": [
16
+ "test/lib/{}_test.rb"
17
+ ],
18
+ "type": "lib"
19
+ }
20
+ }
@@ -0,0 +1 @@
1
+ jruby-9.1.6.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ group :development do
5
+ gem "pry"
6
+ end
7
+
8
+ gemspec
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ athena-cli (0.0.1)
5
+ gli (= 2.5.2)
6
+ jdbc-helper (~> 0.8.2)
7
+ table_print (~> 1.5)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ aruba (0.14.2)
13
+ childprocess (~> 0.5.6)
14
+ contracts (~> 0.9)
15
+ cucumber (>= 1.3.19)
16
+ ffi (~> 1.9.10)
17
+ rspec-expectations (>= 2.99)
18
+ thor (~> 0.19)
19
+ builder (3.2.3)
20
+ childprocess (0.5.9)
21
+ ffi (~> 1.0, >= 1.0.11)
22
+ coderay (1.1.1)
23
+ contracts (0.15.0)
24
+ cucumber (2.4.0)
25
+ builder (>= 2.1.2)
26
+ cucumber-core (~> 1.5.0)
27
+ cucumber-wire (~> 0.0.1)
28
+ diff-lcs (>= 1.1.3)
29
+ gherkin (~> 4.0)
30
+ multi_json (>= 1.7.5, < 2.0)
31
+ multi_test (>= 0.1.2)
32
+ cucumber-core (1.5.0)
33
+ gherkin (~> 4.0)
34
+ cucumber-wire (0.0.1)
35
+ diff-lcs (1.3)
36
+ ffi (1.9.18-java)
37
+ gherkin (4.0.0)
38
+ gli (2.5.2)
39
+ insensitive_hash (0.3.3)
40
+ jdbc-helper (0.8.2)
41
+ insensitive_hash (>= 0.2.4)
42
+ sql_helper (~> 0.1.1)
43
+ method_source (0.8.2)
44
+ multi_json (1.12.1)
45
+ multi_test (0.1.2)
46
+ pry (0.10.4-java)
47
+ coderay (~> 1.1.0)
48
+ method_source (~> 0.8.1)
49
+ slop (~> 3.4)
50
+ spoon (~> 0.0)
51
+ rake (10.4.2)
52
+ rdoc (5.1.0)
53
+ rspec-expectations (3.5.0)
54
+ diff-lcs (>= 1.2.0, < 2.0)
55
+ rspec-support (~> 3.5.0)
56
+ rspec-support (3.5.0)
57
+ slop (3.6.0)
58
+ spoon (0.0.6)
59
+ ffi
60
+ sql_helper (0.1.1)
61
+ table_print (1.5.6)
62
+ thor (0.19.4)
63
+
64
+ PLATFORMS
65
+ java
66
+
67
+ DEPENDENCIES
68
+ aruba (~> 0.14)
69
+ athena-cli!
70
+ pry
71
+ rake (~> 10)
72
+ rdoc (~> 5.1)
73
+
74
+ BUNDLED WITH
75
+ 1.14.6
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 Wynn Netherland
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,128 @@
1
+ # athena-cli
2
+
3
+ A CLI for Amazon Athena, powered by JRuby.
4
+
5
+ [Amazon Athena](https://aws.amazon.com/athena/) allows you to define a schema and query structured data in S3 without the usual Extract-Transform-Load process. This project uses the [JDBC driver](http://docs.aws.amazon.com/athena/latest/ug/connect-with-jdbc.html) and JRuby to bring Athena access to your terminal.
6
+
7
+ ##### _athena-cli is a rapidly evolving work in progress. Some parts of this README are [aspirational](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html)._
8
+
9
+ #### Installation
10
+
11
+ Make sure you have JRuby >= 9.1.6.0.
12
+
13
+ jruby -v
14
+
15
+ Clone the repository:
16
+
17
+ git clone https://github.com/pengwynn/athena-cli
18
+
19
+ Install the dependencies:
20
+
21
+ bundle install
22
+
23
+ Build the gem:
24
+
25
+ rake gem
26
+
27
+ Install it locally
28
+
29
+ gem install pkg/athena-cli-<version>.gem
30
+
31
+ ##### TODO: Install via Rubygems:
32
+
33
+ ##### Setting up AWS credentials
34
+
35
+ The app will look for your AWS key and secret in the `AWS_ACCESS_KEY` and `AWS_SECRET_KEY` environment variables. If you use multiple AWS accounts, [awsam](https://github.com/mheffner/awsam) makes that a snap. Otherwise, you'll need to supply your AWS key and secret on every command via `--key` and `--secret` flags.
36
+
37
+ ##### Configuring the JDBC driver
38
+
39
+ The underlying Java code needs to know where to find your [JDBC
40
+ driver](http://docs.aws.amazon.com/athena/latest/ug/connect-with-jdbc.html#downloading-the-driver). Add the path to your `CLASSPATH` environment variable. Something like:
41
+
42
+
43
+ Example:
44
+
45
+ export CLASSPATH="$CLASSPATH:~/src/AthenaJDB41-1.0.0.jar"
46
+
47
+ ##### Setting the scratch folder
48
+
49
+ Athena requires an S3 scratch folder for storing results. You'll need to set the `ATHENA_S3_STAGING_DIR` environment variable or pass via the `--staging-dir` command line flag.
50
+
51
+ ##### Check your settings
52
+
53
+ athena-cli doctor
54
+
55
+ If you're calling it outside the repository folder, you'll want to drop a
56
+ [log4j.xml](https://github.com/pengwynn/athena-cli/blob/master/log4j.xml) file
57
+ into that folder to silence the logger.
58
+
59
+ ### Hacking on athena-cli
60
+
61
+ If you're running the project form source, be sure and use
62
+
63
+ bundle exec bin/athena-cli
64
+
65
+ so that the dependencies are properly loaded.
66
+
67
+ ### Usage
68
+
69
+ ```
70
+ ❯ athena-cli
71
+ NAME
72
+ athena-cli - CLI for Amazon Athena
73
+
74
+ SYNOPSIS
75
+ athena-cli [global options] command [command options] [arguments...]
76
+
77
+ VERSION
78
+ 0.0.1
79
+
80
+ GLOBAL OPTIONS
81
+ --help - Show this message
82
+ --key=arg - AWS Access Key (default: ********)
83
+ -p, --preview - Output the SQL statement instead of running
84
+ --secret=arg - AWS Secret Key (default: ********)
85
+ --staging-dir=arg - S3 bucket for staging results (default: none)
86
+ --version -
87
+
88
+ COMMANDS
89
+ column - Manage columns in Athena tables
90
+ database - Manage Athena databases
91
+ doctor - Check for required AWS setup
92
+ help - Shows a list of commands or help for one command
93
+ partition - Manage partitions in Athena tables
94
+ query - Run a query from a file or STDIN
95
+ schema - Manage Athena schemas
96
+ table - Manage tables in Athena databases
97
+
98
+ ```
99
+
100
+ ### TODO
101
+
102
+ - [ ] Explore more discoverable commands. `show-tables` instead of `table
103
+ list`
104
+ - [ ] Tests for unhappy paths
105
+ - [ ] Publish to Rubygems
106
+
107
+ ## LICENSE
108
+
109
+ Copyright (c) 2017 Wynn Netherland
110
+
111
+ Permission is hereby granted, free of charge, to any person obtaining
112
+ a copy of this software and associated documentation files (the
113
+ "Software"), to deal in the Software without restriction, including
114
+ without limitation the rights to use, copy, modify, merge, publish,
115
+ distribute, sublicense, and/or sell copies of the Software, and to
116
+ permit persons to whom the Software is furnished to do so, subject to
117
+ the following conditions:
118
+
119
+ The above copyright notice and this permission notice shall be
120
+ included in all copies or substantial portions of the Software.
121
+
122
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
123
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
124
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
125
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
126
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
127
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
128
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rubygems/package_task'
4
+ require 'rdoc/task'
5
+ require 'cucumber'
6
+ require 'cucumber/rake/task'
7
+ Rake::RDocTask.new do |rd|
8
+ rd.main = "README.rdoc"
9
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
10
+ rd.title = 'Your application title'
11
+ end
12
+
13
+ spec = eval(File.read('athena-cli.gemspec'))
14
+
15
+ Gem::PackageTask.new(spec) do |pkg|
16
+ end
17
+ CUKE_RESULTS = 'results.html'
18
+ CLEAN << CUKE_RESULTS
19
+ desc 'Run features'
20
+ Cucumber::Rake::Task.new(:features) do |t|
21
+ opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
22
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
23
+ t.cucumber_opts = opts
24
+ t.fork = false
25
+ end
26
+
27
+ desc 'Run features tagged as work-in-progress (@wip)'
28
+ Cucumber::Rake::Task.new('features:wip') do |t|
29
+ tag_opts = ' --tags ~@pending'
30
+ tag_opts = ' --tags @wip'
31
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
32
+ t.fork = false
33
+ end
34
+
35
+ task :cucumber => :features
36
+ task 'cucumber:wip' => 'features:wip'
37
+ task :wip => 'features:wip'
38
+ require 'rake/testtask'
39
+ Rake::TestTask.new do |t|
40
+ t.libs << "test"
41
+ t.test_files = FileList['test/**/*_test.rb']
42
+ end
43
+
44
+ task :default => [:test,:features]
@@ -0,0 +1,26 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require File.join([File.dirname(__FILE__),'lib','amazon_athena','version.rb'])
6
+ spec = Gem::Specification.new do |s|
7
+ s.name = 'athena-cli'
8
+ s.version = AmazonAthena::VERSION
9
+ s.author = 'Wynn Netherland'
10
+ s.email = 'wynn.netherland@gmail.com'
11
+ s.homepage = 'https://wynnnetherland.com'
12
+ s.platform = Gem::Platform::RUBY
13
+ s.summary = 'A JRuby-powered CLI for Amazon Athena'
14
+ s.licenses = ["MIT"]
15
+ s.files = `git ls-files`.split("
16
+ ")
17
+ s.require_paths << 'lib'
18
+ s.bindir = 'bin'
19
+ s.executables << 'athena-cli'
20
+ s.add_development_dependency('rake', '~> 10')
21
+ s.add_development_dependency('rdoc', '~> 5.1')
22
+ s.add_development_dependency('aruba', '~> 0.14')
23
+ s.add_runtime_dependency('gli','2.5.2')
24
+ s.add_runtime_dependency('jdbc-helper', '~> 0.8.2')
25
+ s.add_runtime_dependency('table_print', '~> 1.5 ')
26
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ if %x(ruby -v) !~ /jruby/
4
+ puts "athena-cli requires jruby"
5
+ exit
6
+ end
7
+
8
+ require 'amazon_athena/cli'
9
+
10
+ AmazonAthena::CLI.run(ARGV)
@@ -0,0 +1,8 @@
1
+ Feature: My bootstrapped app kinda works
2
+ In order to get going on coding my awesome app
3
+ I want to have aruba and cucumber setup
4
+ So I don't have to do it myself
5
+
6
+ Scenario: App just runs
7
+ When I get help for "athena-cli"
8
+ Then the exit status should be 0
@@ -0,0 +1,6 @@
1
+ When /^I get help for "([^"]*)"$/ do |app_name|
2
+ @app_name = app_name
3
+ step %(I run `#{app_name} help`)
4
+ end
5
+
6
+ # Add more step definitions here
@@ -0,0 +1,15 @@
1
+ require 'aruba/cucumber'
2
+
3
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
4
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
5
+
6
+ Before do
7
+ # Using "announce" causes massive warnings on 1.9.2
8
+ @puts = true
9
+ @original_rubylib = ENV['RUBYLIB']
10
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
11
+ end
12
+
13
+ After do
14
+ ENV['RUBYLIB'] = @original_rubylib
15
+ end
@@ -0,0 +1,7 @@
1
+ require 'amazon_athena/client'
2
+ require 'amazon_athena/transformer'
3
+ require 'amazon_athena/version.rb'
4
+
5
+ module AmazonAthena
6
+
7
+ end
@@ -0,0 +1,406 @@
1
+ require 'gli'
2
+ require 'amazon_athena'
3
+ require 'fileutils'
4
+ require 'table_print'
5
+
6
+ module AmazonAthena
7
+ module CLI
8
+
9
+ def self.athena
10
+ AmazonAthena::Client.new \
11
+ key: self.access_key,
12
+ secret: self.access_secret,
13
+ s3_staging_dir: self.staging_folder
14
+ end
15
+
16
+ extend GLI::App
17
+
18
+ program_desc 'CLI for Amazon Athena'
19
+ version AmazonAthena::VERSION
20
+
21
+ switch [:p, :preview],
22
+ :default_value => false,
23
+ :desc => "Output the SQL statement instead of running",
24
+ :negatable => false
25
+
26
+ flag ["key"],
27
+ :default_value => ENV["AWS_ACCESS_KEY"],
28
+ :mask => true,
29
+ :desc => "AWS Access Key"
30
+
31
+ flag ["secret"],
32
+ :default_value => ENV["AWS_SECRET_KEY"],
33
+ :mask => true,
34
+ :desc => "AWS Secret Key"
35
+
36
+ flag ["staging-dir"],
37
+ :default_value => ENV["ATHENA_S3_STAGING_DIR"],
38
+ :desc => "S3 bucket for staging results"
39
+
40
+ desc 'Check for required AWS setup'
41
+ command :doctor do |c|
42
+ c.action do |global_options,options,args|
43
+ puts "Good to go!"
44
+ end
45
+ end
46
+
47
+ desc 'Manage Athena schemas'
48
+ command :schema do |c|
49
+ c.desc "Dump schemas to a local folder"
50
+ c.command :dump do |add|
51
+ add.flag :path,
52
+ :required => true,
53
+ :desc => "Local path for saving schemas"
54
+ add.action do |global_options,options,args|
55
+ path = options[:path]
56
+
57
+ unless File.exists?(path)
58
+ exit_now! "Path #{path} not found"
59
+ end
60
+
61
+ databases = athena.run(AmazonAthena::Commands::ShowDatabases.new)
62
+ preview = !!global_options[:p]
63
+
64
+ databases.each do |db|
65
+ folder = File.join(path, db)
66
+ prefix = File.exists?(folder) ? "Exists" : "Creating"
67
+ render "#{prefix} #{folder}"
68
+
69
+ unless preview
70
+ FileUtils.mkdir_p(folder)
71
+ end
72
+
73
+ tables = athena.run(AmazonAthena::Commands::ShowTables.new(db))
74
+
75
+ tables.each do |table|
76
+ database_table = [db, table].join(".")
77
+ file = File.join(folder, table) + ".sql"
78
+ render "Writing #{file}"
79
+
80
+ if !preview
81
+ sql = athena.run(AmazonAthena::Commands::ShowCreateTable.new(database_table))
82
+
83
+ File.open(file, 'w') { |f| f.write(sql) }
84
+ end
85
+ end
86
+ end
87
+
88
+ # puts global_options, options, args
89
+ end
90
+ end
91
+ end
92
+
93
+ desc 'Manage Athena databases'
94
+ command :database do |c|
95
+ c.desc "List databases"
96
+ c.command :list do |add|
97
+ add.action do |global_options,options,args|
98
+ cmd = AmazonAthena::Commands::ShowDatabases.new
99
+
100
+ render athena.run(cmd, global_options[:p])
101
+ end
102
+ end
103
+
104
+ c.desc "Create a new database"
105
+ c.arg_name "database"
106
+ c.command :create do |add|
107
+ add.action do |global_options,options,args|
108
+ cmd = AmazonAthena::Commands::CreateDatabase.new(args.first)
109
+
110
+ render athena.run(cmd, global_options[:p])
111
+ end
112
+ end
113
+
114
+ c.desc "Drop an existing database"
115
+ c.arg_name "database"
116
+ c.command :drop do |add|
117
+ add.action do |global_options,options,args|
118
+ cmd = AmazonAthena::Commands::DropDatabase.new(args.first)
119
+
120
+ render athena.run(cmd, global_options[:p])
121
+ end
122
+ end
123
+
124
+ c.default_command :list
125
+ end
126
+
127
+ desc 'Manage tables in Athena databases'
128
+ command :table do |c|
129
+ c.desc "Create table"
130
+ c.arg_name "[file]"
131
+ c.command :create do |add|
132
+ add.flag [:l, :location], :desc => "S3 location - s3://bucket/path/"
133
+ add.flag [:n, :name], :desc => "Fully qualifed name as: database.table"
134
+ add.action do |global_options,options,args|
135
+ ddl = if filename = args.first
136
+ exit_now! "File not found" unless File.exists?(filename)
137
+ File.read(filename)
138
+ elsif !STDIN.tty?
139
+ $stdin.read
140
+ end
141
+
142
+ exit_now! "Must supply path to file or pass via STDIN" if ddl.to_s.empty?
143
+
144
+
145
+ opts = {
146
+ :name => options[:name],
147
+ :location => options[:location]
148
+ }
149
+
150
+ ddl = AmazonAthena::Transformer.transform_table(ddl, opts)
151
+
152
+ cmd = AmazonAthena::Commands::CreateTable.new(ddl)
153
+
154
+ render athena.run(cmd, global_options[:p])
155
+ end
156
+ end
157
+
158
+ c.desc "Describe a table"
159
+ c.arg_name "database.table"
160
+ c.command :describe do |add|
161
+ add.action do |global_options,options,args|
162
+ cmd = AmazonAthena::Commands::DescribeTable.new(args.first)
163
+
164
+ render athena.run(cmd, global_options[:p])
165
+ end
166
+ end
167
+
168
+ c.desc "List tables in a database"
169
+ c.arg_name "database"
170
+ c.command :list do |add|
171
+ add.action do |global_options,options,args|
172
+ cmd = AmazonAthena::Commands::ShowTables.new(args.first)
173
+
174
+ render athena.run(cmd, global_options[:p])
175
+ end
176
+ end
177
+
178
+ c.desc "Show table properties"
179
+ c.arg_name "database.table"
180
+ c.command :properties do |add|
181
+ add.action do |global_options,options,args|
182
+ cmd = AmazonAthena::Commands::ShowTableProperties.new(args.first)
183
+
184
+ render athena.run(cmd, global_options[:p])
185
+ end
186
+ end
187
+
188
+ c.desc "Show table create DDL"
189
+ c.arg_name "database.table"
190
+ c.command :show do |add|
191
+ add.action do |global_options,options,args|
192
+ cmd = AmazonAthena::Commands::ShowCreateTable.new(args.first)
193
+
194
+ render athena.run(cmd, global_options[:p])
195
+ end
196
+ end
197
+
198
+ c.desc "Refresh table partitions"
199
+ c.arg_name "database.table"
200
+ c.command :repair do |add|
201
+ add.action do |global_options,options,args|
202
+ commands = [
203
+ AmazonAthena::Commands::RepairTable.new(args.first),
204
+ AmazonAthena::Commands::ShowPartitions.new(args.first),
205
+ ]
206
+
207
+ commands.each do |cmd|
208
+ render athena.run(cmd, global_options[:p])
209
+ end
210
+ end
211
+ end
212
+
213
+ c.desc "Drop table"
214
+ c.arg_name "database.table"
215
+ c.command :drop do |add|
216
+ add.action do |global_options,options,args|
217
+ cmd = AmazonAthena::Commands::DropTable.new(args.first)
218
+
219
+ render athena.run(cmd, global_options[:p])
220
+ end
221
+ end
222
+ end
223
+
224
+ desc 'Manage columns in Athena tables'
225
+ command :column do |c|
226
+ c.desc "List columns in a table"
227
+ c.arg_name "database.table"
228
+ c.command :list do |add|
229
+ add.action do |global_options,options,args|
230
+ cmd = AmazonAthena::Commands::ShowColumns.new(args.first)
231
+
232
+ render athena.run(cmd, global_options[:p])
233
+ end
234
+ end
235
+
236
+ c.default_command :list
237
+ end
238
+
239
+ desc 'Manage partitions in Athena tables'
240
+ command :partition do |c|
241
+ c.desc "List partitions in a table"
242
+ c.arg_name "database.table"
243
+ c.command :list do |add|
244
+ add.action do |global_options,options,args|
245
+ cmd = AmazonAthena::Commands::ShowPartitions.new(args.first)
246
+
247
+ render athena.run(cmd, global_options[:p])
248
+ end
249
+ end
250
+
251
+ c.desc "TODO: Add partition(s) to a table"
252
+ c.arg_name "database.table"
253
+ c.arg_name "key=value,key=value:key=value,key=value"
254
+ c.command :add do |add|
255
+ add.action do |global_options,options,args|
256
+ render "NOT IMPLEMENTED"
257
+ end
258
+ end
259
+
260
+ c.desc "TODO: Drop partition(s) from a table"
261
+ c.arg_name "database.table"
262
+ c.arg_name "key=value,key=value:key=value,key=value"
263
+ c.command :drop do |add|
264
+ add.action do |global_options,options,args|
265
+ render "NOT IMPLEMENTED"
266
+ end
267
+ end
268
+
269
+ c.default_command :list
270
+ end
271
+
272
+ desc 'Run a query from a file or STDIN'
273
+ arg_name '[file]'
274
+ command :query do |c|
275
+ c.action do |global_options,options,args|
276
+ input = args.first
277
+ sql = case input
278
+ when "", NilClass
279
+ $stdin.read if !STDIN.tty?
280
+ when String
281
+ if File.exists?(input)
282
+ File.read(input)
283
+ else
284
+ input
285
+ end
286
+ else
287
+ exit_now! "SQL string or file path required"
288
+ end
289
+
290
+ if global_options[:p]
291
+ render sql
292
+ else
293
+ begin
294
+ render athena.query(sql).map(&:to_h)
295
+ rescue Exception => e
296
+ render e.message
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ pre do |global,command,options,args|
303
+ @access_key = global[:key]
304
+ @access_secret = global[:secret]
305
+ @staging_folder = global[:"staging-dir"]
306
+
307
+
308
+ self.check_aws_settings
309
+ self.check_class_path
310
+
311
+ true
312
+ end
313
+
314
+ post do |global,command,options,args|
315
+ # Post logic here
316
+ # Use skips_post before a command to skip this
317
+ # block on that command only
318
+ end
319
+
320
+ on_error do |exception|
321
+ # Error logic here
322
+ # return false to skip default error handling
323
+ true
324
+ end
325
+
326
+ def self.access_key
327
+ @access_key
328
+ end
329
+
330
+ def self.access_secret
331
+ @access_secret
332
+ end
333
+
334
+ def self.staging_folder
335
+ @staging_folder
336
+ end
337
+
338
+ def self.class_path
339
+ ENV['CLASSPATH']
340
+ end
341
+
342
+ def self.check_aws_settings
343
+ if self.access_key.nil? || self.access_secret.nil?
344
+ msg = <<~MSG
345
+ athena-cli needs your AWS credentials. You can pass them via the
346
+ --key and --secret flags or set AWS_ACCESS_KEY and AWS_SECRET_KEY
347
+ environment variables.
348
+ MSG
349
+ exit_now! msg
350
+ end
351
+
352
+ if self.staging_folder.nil?
353
+ msg = <<~MSG
354
+ athena-cli requires an S3 location to use as a scratch folder.
355
+ Please provide one via the --staging-dir flag or set the
356
+ ATHENA_S3_STAGING_DIR environment variable.
357
+ MSG
358
+ exit_now! msg, 1
359
+ end
360
+ end
361
+
362
+ def self.check_class_path
363
+ unless self.class_path =~ /AthenaJDBC/
364
+ jar_path = File.expand_path(File.join([File.dirname(__FILE__),'..', 'jdbc','AthenaJDBC41-1.0.0.jar']))
365
+ msg = <<~MSG
366
+ JRuby requires JDBC driver to be in your Java class path.
367
+ For download instructions, see:
368
+
369
+ http://docs.aws.amazon.com/athena/latest/ug/connect-with-jdbc.html
370
+
371
+ After downloading, add /path/to/driver.jar to your CLASSPATH environment variable.
372
+
373
+ Example:
374
+
375
+ export CLASSPATH="$CLASSPATH:~/src/AthenaJDB41-1.0.0.jar"
376
+ MSG
377
+ exit_now! msg, 1
378
+ end
379
+ end
380
+
381
+ def self.details(data)
382
+ longest_key = data.keys.max_by(&:length)
383
+ data.each do |key, value|
384
+ printf "%##{longest_key.length}s %s\n", key, value
385
+ end
386
+ end
387
+
388
+ def self.render(output)
389
+ case output
390
+ when Hash
391
+ details(output)
392
+ when String
393
+ puts output
394
+ when Array
395
+ case output.first
396
+ when Hash
397
+ tp output
398
+ else
399
+ puts output
400
+ end
401
+ else
402
+ return
403
+ end
404
+ end
405
+ end
406
+ end