jrubysql 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in jrubysql.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Junegunn Choi
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.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ jrubysql
2
+ ========
3
+ An SQL client for any JDBC-compliant database. Written in JRuby.
4
+
5
+ Installation
6
+ ------------
7
+
8
+ ```
9
+ gem install jrubysql
10
+ ```
11
+
12
+ Usage
13
+ -----
14
+
15
+ ```
16
+ usage: jrubysql [options]
17
+ jrubysql -t DBMS_TYPE -h HOSTNAME [-u USERNAME -p [PASSWORD] [-d DATABASE]] [-f FILENAME]
18
+ jrubysql -c CLASSNAME -j JDBC_URL [-u USERNAME -p [PASSWORD] [-d DATABASE]] [-f FILENAME]
19
+
20
+ -t, --type DBMS_TYPE Database type: mysql/oracle/postgres/sqlserver
21
+ -h, --host HOST DBMS host address
22
+
23
+ -c, --class-name CLASSNAME Class name of the JDBC driver
24
+ -j, --jdbc-url JDBC_URL JDBC URL for the connection
25
+
26
+ -u, --user USERNAME Username
27
+ -p, --password [PASSWORD] Password
28
+ -d, --database DATABASE Name of the database (optional)
29
+
30
+ -f, --filename FILENAME SQL script file
31
+ -o, --output OUTPUT_TYPE Output type: cterm|term|csv (default: cterm)
32
+
33
+ --help Show this message
34
+ --version Show version
35
+ ```
36
+
37
+ Connecting to the database
38
+ --------------------------
39
+
40
+ ### Setting up CLASSPATH
41
+ Add the appropriate JDBC drivers to the CLASSPATH.
42
+
43
+ ```
44
+ export CLASSPATH=$CLASSPATH:~/lib/mysql-connector-java-5.1.17-bin.jar:~/lib/ojdbc6.jar
45
+ ```
46
+
47
+ ### With type (-t) and hostname (-h)
48
+
49
+ ```
50
+ # Supports MySQL/Oracle/PostgreSQL/MSSQL
51
+
52
+ jrubysql -t mysql -h localhost -d test -u user -p
53
+ jrubysql -t oracle -h localhost:1521/orcl -u user -p password
54
+ jrubysql -t postgres -h localhost -u root
55
+ jrubysql -t sqlserver -h 192.168.62.26 -u user -p password
56
+ ```
57
+
58
+ ### Connect with class name of JDBC driver (-c) and JDBC URL (-j)
59
+
60
+ ```
61
+ # You can connect to any database with its JDBC driver
62
+
63
+ bin/jrubysql -corg.postgresql.Driver -jjdbc:postgresql://localhost/test
64
+ bin/jrubysql -ccom.mysql.jdbc.Driver -jjdbc:mysql://localhost/test -uuser -p
65
+ ```
66
+
67
+ Screenshot
68
+ ----------
69
+ ![](https://github.com/junegunn/jrubysql/raw/master/screenshots/simpsons.png)
70
+
71
+ TODO
72
+ ----
73
+ TESTS!!!
74
+
75
+ Copyright
76
+ ---------
77
+ Copyright (c) 2012 Junegunn Choi. See LICENSE.txt for
78
+ further details.
79
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
data/bin/jrubysql ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # Junegunn Choi (junegunn.c@gmail.com)
4
+ # 2012/03/07-
5
+
6
+ require 'rubygems'
7
+ require 'jrubysql'
8
+
9
+ JRubySQL.launch ARGV
data/jrubysql.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "jrubysql/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "jrubysql"
7
+ s.version = JRubySQL::VERSION
8
+ s.authors = ["Junegunn Choi"]
9
+ s.email = ["junegunn.c@gmail.com"]
10
+ s.homepage = "https://github.com/junegunn/jrubysql"
11
+ s.summary = %q{An SQL client for any JDBC-compliant database.}
12
+ s.description = %q{An SQL client for any JDBC-compliant database. Written in JRuby.}
13
+
14
+ s.rubyforge_project = "jrubysql"
15
+
16
+ s.files = `git ls-files`.split("\n").reject { |f| f =~ /^screenshots/ }
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "test-unit"
23
+
24
+ s.add_runtime_dependency "jdbc-helper", '~> 0.7.2'
25
+ s.add_runtime_dependency "insensitive_hash", '~> 0.2.3'
26
+ s.add_runtime_dependency "tabularize", '~> 0.1.1'
27
+ s.add_runtime_dependency "each_sql", '~> 0.3.1'
28
+ s.add_runtime_dependency "highline", '~> 1.6.11'
29
+ s.add_runtime_dependency "ansi", '~> 1.4.2'
30
+ s.add_runtime_dependency "erubis", '~> 2.7.0'
31
+ end
@@ -0,0 +1,35 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+
4
+ module JRubySQL
5
+ # A simple key-value config in YAML
6
+ class Config
7
+ DEFAULT_PATH = File.join(ENV['HOME'], '.jrubysqlrc')
8
+
9
+ def initialize path = DEFAULT_PATH
10
+ @path = path
11
+ if @path && File.exists?(@path)
12
+ @yaml = YAML.load(File.read(@path))
13
+ end
14
+ @yaml = @yaml || {}
15
+ end
16
+
17
+ def [] key
18
+ @yaml[key]
19
+ end
20
+
21
+ def []= key, value
22
+ (@yaml[key] = value).tap { dump }
23
+ end
24
+
25
+ private
26
+ def dump
27
+ # Try to write atomically
28
+ File.open(@path + '.tmp', 'w') do |f|
29
+ f << YAML.dump(@yaml)
30
+ end
31
+ FileUtils.mv(@path + '.tmp', @path)
32
+ end
33
+ end#Config
34
+ end#JRubySQL
35
+
@@ -0,0 +1,15 @@
1
+ module JRubySQL
2
+ module Constants
3
+ SUPPORTED_DBMS_TYPES = [ :mysql, :oracle, :postgres, :sqlserver ]
4
+
5
+ # .jrubysqlrc
6
+ MAX_COMMAND_HISTORY = 100
7
+ MAX_CONNECTION_HISTORY = 10
8
+
9
+ # Terminal (TBD)
10
+ MAX_COLUMN_WIDTH = 80
11
+ MIN_SCREEN_ROWS = 10
12
+ MAX_SCREEN_ROWS = 50
13
+
14
+ end#Constants
15
+ end#JRubySQL
@@ -0,0 +1,141 @@
1
+ require 'quote_unquote'
2
+
3
+ module JRubySQL
4
+ class Controller
5
+ include JRubySQL::Messages
6
+
7
+ attr_reader :db_type
8
+
9
+ def initialize options, argv_str
10
+ @config = JRubySQL::Config.new
11
+ histories = @config['connections']
12
+
13
+ if options.nil?
14
+ if histories.nil? || histories.empty?
15
+ JRubySQL::OptionParser.parse []
16
+ else
17
+ # FIXME: Output
18
+ puts m(:choose_parameter)
19
+ histories.each_with_index do |history, idx|
20
+ puts "[#{idx + 1}] #{history.first}"
21
+ end
22
+ print '> '
23
+ select = $stdin.gets
24
+ select = select && select.chomp
25
+ if (1..(histories.length)).include?(select.to_i)
26
+ history = histories[select.to_i - 1]
27
+ @options = history.last
28
+ @argv_str = history.first
29
+ else
30
+ puts
31
+ JRubySQL::OptionParser.parse []
32
+ end
33
+ end
34
+ else
35
+ @options = options
36
+ @argv_str = argv_str
37
+ end
38
+
39
+ @db_type = JRubySQL::RDBMS.get_type(@options[:type] || @options[:driver])
40
+
41
+ # Setting up input: file or console (and more?)
42
+ if @options[:filename]
43
+ @input = JRubySQL::Input::File.new(self, @options[:filename])
44
+ else
45
+ @input = JRubySQL::Input::Console.new(self)
46
+ end
47
+
48
+ # Setting up output: Colored terminal
49
+ case @options[:output]
50
+ when 'cterm'
51
+ @output = JRubySQL::Output::CTerm.new
52
+ when 'term'
53
+ @output = JRubySQL::Output::Term.new
54
+ when 'csv'
55
+ @output = JRubySQL::Output::CSV.new
56
+ end
57
+ end
58
+
59
+ def start
60
+ @output.welcome!
61
+ @output.info m(:connecting)
62
+ begin
63
+ @rdbms = JRubySQL::RDBMS.new @options
64
+ rescue Exception => e
65
+ @output.error e.to_s
66
+ exit 1
67
+ end
68
+ @output.info m(:connected)
69
+
70
+ history = @config['connections'] || []
71
+ history.unshift [@argv_str, @options]
72
+ history.uniq!
73
+ if history.length > JRubySQL::Constants::MAX_CONNECTION_HISTORY
74
+ history = history[0, JRubySQL::Constants::MAX_CONNECTION_HISTORY]
75
+ end
76
+ @config['connections'] = history
77
+
78
+ loop do
79
+ ret = @input.get
80
+
81
+ ret[:sqls].each do |sql|
82
+ begin
83
+ output @rdbms.execute(sql)
84
+ rescue Exception => e
85
+ @output.error e.to_s
86
+ end
87
+ end if ret.has_key?(:sqls)
88
+
89
+ ret[:commands].each do |command|
90
+ process_command command.keys.first, command.values.first
91
+ end if ret.has_key?(:commands)
92
+ end
93
+ end
94
+
95
+ def cursor empty = true
96
+ @output.cursor empty
97
+ end
98
+
99
+ def print_cursor empty = true
100
+ @output.print_cursor empty
101
+ end
102
+
103
+ private
104
+ def output result
105
+ @output.print_result result
106
+ end
107
+
108
+ def process_command cmd, params
109
+ case cmd
110
+ when :help
111
+ @output.print_help
112
+ when :quit
113
+ @output.info m(:goodbye)
114
+ quit!
115
+ when :delimiter
116
+ @output.info m(:set_delimiter, params)
117
+ @input.delimiter = params
118
+ when :now
119
+ @output.info Time.now.strftime('%Y/%m/%d %H:%M:%S.%L')
120
+ when :autocommit
121
+ if params.nil?
122
+ @output.info m(:current_autocommit, @rdbms.autocommit ? 'on' : 'off')
123
+ elsif %[on off].include?(params.downcase)
124
+ @rdbms.autocommit = params.downcase == 'on'
125
+ @output.info m(:turn_autocommit, params.downcase)
126
+ else
127
+ @output.error m(:invalid_autocommit, params)
128
+ end
129
+ else
130
+ # TODO
131
+ @output.error m(:unknown_command)
132
+ end
133
+ end
134
+
135
+ def quit!
136
+ @rdbms.close rescue nil
137
+ exit 0
138
+ end
139
+
140
+ end#Controller
141
+ end#JRubySQL
@@ -0,0 +1,10 @@
1
+ <% title = JRubySQL.name %>
2
+ <%= title %>
3
+ <%= '=' * title.length %>
4
+
5
+ help Display this message.
6
+ <SQL> Execute the SQL.
7
+ autocommit [<on|off>] Show/enable/disable autocommit. (default: on)
8
+ delimiter <DELIMITER> Change SQL delimiter.
9
+ now Display current timestamp.
10
+ exit Exit JRubySQL.
@@ -0,0 +1,84 @@
1
+ require 'readline'
2
+
3
+ module JRubySQL
4
+ module Input
5
+ class Console
6
+ def initialize controller
7
+ @controller = controller
8
+ @esql = JRubySQL::Input.get_parser @controller.db_type
9
+ end
10
+
11
+ def get
12
+ empty_response = { :sqls => [], :commands => [] }
13
+ line = Readline::readline(@controller.cursor(@esql.empty?), false)
14
+
15
+ return(
16
+ # CTRL-D
17
+ if line.nil?
18
+ puts
19
+ @esql.clear
20
+ empty_response
21
+ # Empty input (not inside a block)
22
+ elsif @esql.empty? && line.gsub(/\s/m, '').empty?
23
+ empty_response
24
+ # Console commands
25
+ elsif @esql.empty? && cmd = process_command(line)
26
+ { :commands => [cmd].compact }
27
+ # Line with delimiters
28
+ elsif line.include?(@esql.delimiter)
29
+ @esql << line + ' '
30
+ result = @esql.shift
31
+ result[:sqls].each do |sql|
32
+ Readline::HISTORY << sql + @esql.delimiter
33
+ end
34
+ { :sqls => result[:sqls] }
35
+ # SQL without delimiter
36
+ else
37
+ @esql << line + ' '
38
+ empty_response
39
+ end
40
+ )
41
+ end
42
+
43
+ def delimiter
44
+ @esql.delimiter
45
+ end
46
+
47
+ def delimiter= delim
48
+ @esql.delimiter = delim
49
+ end
50
+
51
+ private
52
+
53
+ def process
54
+ result = @esql.shift
55
+ result[:sqls].each do |sql|
56
+ Readline::HISTORY << sql + ';'
57
+ @controller.execute sql
58
+ end
59
+ end
60
+
61
+ def process_command line
62
+ Readline::HISTORY << line
63
+ case line.chomp.downcase.strip
64
+ when /^help(#{Regexp.escape delimiter})?$/
65
+ { :help => nil }
66
+ when /^delimiter (\S+$)/
67
+ { :delimiter => $1 }
68
+ when 'autocommit'
69
+ { :autocommit => nil }
70
+ when /^autocommit (\S+?)(#{Regexp.escape delimiter})?$/
71
+ { :autocommit => $1 }
72
+ when 'now'
73
+ { :now => nil }
74
+ when /^(exit|quit)(#{Regexp.escape delimiter})?$/
75
+ { :quit => nil }
76
+ else
77
+ Readline::HISTORY.pop
78
+ nil
79
+ end
80
+ end
81
+ end#Console
82
+ end#Input
83
+ end#JRubySQL
84
+
@@ -0,0 +1,19 @@
1
+ require 'each_sql'
2
+
3
+ module JRubySQL
4
+ module Input
5
+ class File
6
+ def initialize controller, file_path
7
+ @controller = controller
8
+ script = ::File.read(file_path)
9
+ sqls = EachSQL(script, JRubySQL::Input.get_each_sql_type(@controller.db_type))
10
+ @ret = { :sqls => sqls }
11
+ end
12
+
13
+ def get
14
+ @ret.tap { @ret = { :commands => [{ :quit => nil }] } }
15
+ end
16
+ end#Console
17
+ end#Input
18
+ end#JRubySQL
19
+
@@ -0,0 +1,17 @@
1
+ require 'each_sql'
2
+
3
+ module JRubySQL
4
+ module Input
5
+ def self.get_each_sql_type db_type
6
+ {
7
+ :mysql => :mysql,
8
+ :oracle => :oracle,
9
+ :postgres => :postgres
10
+ }[db_type] || :default
11
+ end
12
+
13
+ def self.get_parser db_type, delimiter = ';'
14
+ EachSQL.new(get_each_sql_type(db_type), delimiter)
15
+ end
16
+ end#Input
17
+ end#JRubySQL
@@ -0,0 +1,23 @@
1
+ require 'yaml'
2
+ require 'insensitive_hash/minimal'
3
+
4
+ module JRubySQL
5
+ module Messages
6
+ def m *args
7
+ args = args.dup
8
+ type = args.shift
9
+ msg = MESSAGES[type]
10
+ args.each do |arg|
11
+ msg = msg.sub('$$', arg.to_s)
12
+ end
13
+ msg
14
+ end
15
+
16
+ private
17
+ MESSAGES =
18
+ InsensitiveHash[
19
+ YAML.load(File.read File.join( File.dirname(__FILE__), 'messages.yml' ))
20
+ ].tap { |ih| ih.underscore = true }
21
+ end#Messages
22
+ end#JRubySQL
23
+
@@ -0,0 +1,43 @@
1
+ ---
2
+ choose parameter:
3
+ "Parameter not given. Choose one:"
4
+ ask password:
5
+ "Password: "
6
+
7
+ invalid connection:
8
+ Invalid connection specification
9
+ unsupported database:
10
+ $$ is not supported yet. Try with -c and -j options instead.
11
+ oracle service name required:
12
+ Oracle service name must be included in the hostname: e.g. localhost:1521/orcl
13
+ invalid output:
14
+ Invalid output type.
15
+ file not found:
16
+ "File not found: $$"
17
+
18
+ connecting:
19
+ Connecting to the database ...
20
+ connected:
21
+ Connected.
22
+
23
+ set delimiter:
24
+ Setting delimiter to $$
25
+ current autocommit:
26
+ "Current autocommit: $$"
27
+ turn autocommit:
28
+ "Turning autocommit $$"
29
+ invalid autocommit:
30
+ "Invalid option: '$$'. Required: [on|off]"
31
+ unknown command:
32
+ Unknown command. Possibly a bug.
33
+ goodbye:
34
+ Goodbye!
35
+
36
+ interrupted:
37
+ Interrupted.
38
+
39
+ rows returned:
40
+ $$ row$$. $$
41
+ rows affected:
42
+ $$ row$$ affected. $$
43
+
@@ -0,0 +1,124 @@
1
+ require 'optparse'
2
+ require 'highline'
3
+
4
+ module JRubySQL
5
+ module OptionParser
6
+ class << self
7
+ include JRubySQL::Messages
8
+ end
9
+
10
+ def self.parse argv
11
+ {:output => 'cterm'}.tap do |options|
12
+ opts = ::OptionParser.new { |opts|
13
+ opts.banner =
14
+ [
15
+ "usage: jrubysql [options]",
16
+ " jrubysql -t DBMS_TYPE -h HOSTNAME [-u USERNAME -p [PASSWORD] [-d DATABASE]] [-f FILENAME]",
17
+ " jrubysql -c CLASSNAME -j JDBC_URL [-u USERNAME -p [PASSWORD] [-d DATABASE]] [-f FILENAME]"
18
+ ].join($/)
19
+ opts.separator ''
20
+
21
+ opts.on('-t', '--type DBMS_TYPE', 'Database type: mysql/oracle/postgres/sqlserver') do |v|
22
+ options[:type] = v.downcase.to_sym
23
+ end
24
+
25
+ opts.on('-h', '--host HOST', 'DBMS host address') do |v|
26
+ options[:host] = v
27
+ end
28
+
29
+ opts.separator ""
30
+
31
+ opts.on('-c', '--class-name CLASSNAME', 'Class name of the JDBC driver') do |v|
32
+ options[:driver] = v
33
+ end
34
+
35
+ opts.on('-j', '--jdbc-url JDBC_URL', 'JDBC URL for the connection') do |v|
36
+ options[:url] = v
37
+ end
38
+
39
+ opts.separator ""
40
+
41
+ opts.on('-u', '--user USERNAME', 'Username') do |v|
42
+ options[:user] = v
43
+ end
44
+
45
+ opts.on('-p', '--password [PASSWORD]', 'Password') do |v|
46
+ options[:password] = v
47
+ end
48
+
49
+ opts.on('-d', '--database DATABASE', 'Name of the database (optional)') do |v|
50
+ options[:database] = v
51
+ end
52
+
53
+ opts.separator ""
54
+
55
+ opts.on('-f', '--filename FILENAME', 'SQL script file') do |v|
56
+ options[:filename] = v
57
+ end
58
+
59
+ opts.on('-o', '--output OUTPUT_TYPE', 'Output type: cterm|term|csv (default: cterm)') do |v|
60
+ options[:output] = v
61
+ end
62
+
63
+ opts.separator ""
64
+
65
+ opts.on_tail('--help', "Show this message") do
66
+ puts opts
67
+ exit
68
+ end
69
+
70
+ opts.on_tail('--version', "Show version") do
71
+ puts JRubySQL.name
72
+ exit
73
+ end
74
+ }
75
+ begin
76
+ opts.parse! argv
77
+ if options.has_key?(:password) && options[:password].nil?
78
+ options[:password] = ask_password
79
+ end
80
+
81
+ validate options
82
+ rescue SystemExit
83
+ exit 0
84
+ rescue Exception => e
85
+ puts e.to_s
86
+ puts '=' * e.to_s.length
87
+ puts opts
88
+ exit 1
89
+ end
90
+ end#tap
91
+ end
92
+
93
+ private
94
+ def self.validate opts
95
+ unless %w[cterm term csv].include?(opts[:output])
96
+ raise ArgumentError.new m(:invalid_output)
97
+ end
98
+
99
+ if (!opts[:type] && !opts[:driver]) || (opts[:type] && opts[:driver])
100
+ raise ArgumentError.new m(:invalid_connection)
101
+ end
102
+
103
+ unless (opts[:type] && opts[:host]) || (opts[:driver] && opts[:url])
104
+ raise ArgumentError.new m(:invalid_connection)
105
+ end
106
+
107
+ if opts[:type] && !JRubySQL::Constants::SUPPORTED_DBMS_TYPES.include?(opts[:type])
108
+ raise ArgumentError.new m(:unsupported_database, opts[:type])
109
+ end
110
+
111
+ if opts[:filename] && !File.exists?(opts[:filename])
112
+ raise ArgumentError.new m(:file_not_found, opts[:filename])
113
+ end
114
+
115
+ end
116
+
117
+ def self.ask_password
118
+ HighLine.new.ask(m(:ask_password)) { |q| q.echo = "*" }
119
+ end
120
+
121
+ end#OptionParser
122
+ end#JRubySQL
123
+
124
+ # p JRubySQL::OptionParser.parse %w[-h aa gg -t asdfa -p]
@@ -0,0 +1,49 @@
1
+ require 'csv'
2
+
3
+ module JRubySQL
4
+ module Output
5
+ class CSV
6
+ def welcome!; end
7
+ def info msg
8
+ $stderr.puts "[I] #{msg}"
9
+ end
10
+ def result msg
11
+ $stderr.puts "[R] #{msg}"
12
+ end
13
+ def warn msg
14
+ $stderr.puts "[W] #{msg}"
15
+ end
16
+ def error msg
17
+ $stderr.puts "[E] #{msg}"
18
+ end
19
+ def print_help; end
20
+
21
+ def cursor empty
22
+ ''
23
+ end
24
+
25
+ def print_cursor empty; end
26
+
27
+ def print_result ret
28
+ # Footer
29
+ elapsed = "(#{'%.2f' % ret[:elapsed]} sec)"
30
+
31
+ if ret[:set?]
32
+ ret[:result].each_with_index do |row, idx|
33
+ puts ::CSV.generate_line(row.labels) if idx == 0
34
+ puts ::CSV.generate_line row.map { |col|
35
+ case col
36
+ when BigDecimal
37
+ col.to_s('F')
38
+ else
39
+ col
40
+ end
41
+ }
42
+ end
43
+ end
44
+ end
45
+
46
+ end#CSV
47
+ end#Output
48
+ end#JRubySQL
49
+
@@ -0,0 +1,95 @@
1
+ require 'ansi'
2
+ require 'erubis'
3
+ require 'bigdecimal'
4
+
5
+ module JRubySQL
6
+ module Output
7
+ class CTerm < Term
8
+ include ANSI::Code
9
+ HELP = Erubis::Eruby.new(File.read File.join(File.dirname(__FILE__), '../doc/help.txt.erb')).result(binding)
10
+
11
+ def welcome!
12
+ puts bold + JRubySQL.name + reset
13
+ end
14
+
15
+ def cursor empty
16
+ if empty
17
+ wrap('jrubysql', bold) +
18
+ wrap('> ', bold + green)
19
+ else
20
+ wrap(' -', bold + yellow) +
21
+ wrap('> ', bold + red)
22
+ end
23
+ end
24
+
25
+ def print_cursor empty
26
+ print cursor(empty)
27
+ end
28
+
29
+ def print_help
30
+ puts
31
+ puts wrap(HELP, blue + bold)
32
+ puts
33
+ end
34
+
35
+ def info message
36
+ col = blue + bold
37
+ puts wrap(message, col)
38
+ end
39
+
40
+ def result message
41
+ col = green
42
+ puts wrap(message, green)
43
+ end
44
+
45
+ def warn message
46
+ col = yellow + bold
47
+ puts wrap(message, col)
48
+ end
49
+
50
+ def error message
51
+ col = red + bold
52
+ puts wrap(message, col)
53
+ end
54
+
55
+ private
56
+ def separator_for row
57
+ # 13-bytes for ANSI codes
58
+ # - bold/reset: 4
59
+ # - colors: 5
60
+ '+-' + row.map { |e| '-' * (e.length - 13) }.join('-+-') + '-+'
61
+ end
62
+
63
+ def decorate_label label
64
+ white + bold + label + reset
65
+ end
66
+
67
+ # This looks stupid though.
68
+ def decorate value
69
+ case value
70
+ when BigDecimal
71
+ cyan + value.to_s('F') + reset + reset
72
+ when Numeric
73
+ cyan + value.to_s + reset + reset
74
+ when String
75
+ yellow + value + reset + reset
76
+ when Time, Java::JavaSql::Timestamp
77
+ magenta + value.to_s + reset + reset
78
+ when NilClass
79
+ bold + red + '(null)' + reset
80
+ else
81
+ white + reset + value.to_s + reset
82
+ end
83
+ end
84
+
85
+ def cnow
86
+ green + now + reset
87
+ end
88
+
89
+ def wrap text, color
90
+ color + text + reset
91
+ end
92
+
93
+ end#CTerm
94
+ end#Output
95
+ end#JRubySQL
@@ -0,0 +1,122 @@
1
+ require 'erubis'
2
+ require 'tabularize'
3
+ require 'java'
4
+
5
+ module JRubySQL
6
+ module Output
7
+ class Term
8
+ include JRubySQL::Messages
9
+
10
+ HELP = Erubis::Eruby.new(File.read File.join(File.dirname(__FILE__), '../doc/help.txt.erb')).result(binding)
11
+
12
+ def initialize
13
+ # Make use of JLine included in JRuby
14
+ java_import 'jline.Terminal'
15
+ @terminal = Terminal.getTerminal
16
+ trap 'INT' do
17
+ Thread.main.raise Interrupt
18
+ end
19
+ end
20
+
21
+ def welcome!
22
+ puts JRubySQL.name
23
+ end
24
+
25
+ def cursor empty
26
+ if empty
27
+ 'jrubysql> '
28
+ else
29
+ ' -> '
30
+ end
31
+ end
32
+
33
+ def print_cursor empty
34
+ print cursor(empty)
35
+ end
36
+
37
+ def print_help
38
+ puts
39
+ puts HELP
40
+ puts
41
+ end
42
+
43
+ def info message
44
+ puts "[I] #{message}"
45
+ end
46
+
47
+ def result message
48
+ puts "[R] #{message}"
49
+ end
50
+
51
+ def warn message
52
+ puts "[W] #{message}"
53
+ end
54
+
55
+ def error message
56
+ puts "[E] #{message}"
57
+ end
58
+
59
+ def print_result ret
60
+ # Footer
61
+ elapsed = "(#{'%.2f' % ret[:elapsed]} sec)"
62
+
63
+ if ret[:set?]
64
+ begin
65
+ cnt = print_table ret[:result]
66
+ result m(:rows_returned, cnt, cnt > 1 ? 's' : '', elapsed)
67
+ rescue Interrupt
68
+ warn m(:interrupted)
69
+ end
70
+ elsif ret[:result]
71
+ cnt = [0, ret[:result]].max
72
+ result m(:rows_affected, cnt, cnt > 1 ? 's' : '', elapsed)
73
+ else
74
+ result elapsed
75
+ end
76
+ puts
77
+ end
78
+
79
+ private
80
+ def print_table ret
81
+ cnt = 0
82
+ lines = [(@terminal.getTerminalHeight rescue JRubySQL::Constants::MAX_SCREEN_ROWS) - 5,
83
+ JRubySQL::Constants::MIN_SCREEN_ROWS].max
84
+ ret.each_slice(lines) do |slice|
85
+ cnt += slice.length
86
+
87
+ table = [slice.first.labels.map { |l| decorate_label l }] +
88
+ slice.map { |row| row.to_a.map { |v| decorate v } }
89
+
90
+ output = Tabularize.it(table, :unicode_display => true)
91
+ separator = separator_for(output.first)
92
+ output_strs = output.map { |r| '| ' + r.join(' | ') + ' |' }
93
+ [0, 2, -1].each { |l| output_strs.insert l, separator }
94
+ puts output_strs
95
+ end
96
+ cnt
97
+ end
98
+
99
+ def separator_for row
100
+ '+-' + row.map { |e| '-' * e.length }.join('-+-') + '-+'
101
+ end
102
+
103
+ def decorate_label label
104
+ label
105
+ end
106
+
107
+ def decorate value
108
+ case value
109
+ when BigDecimal
110
+ value.to_s('F')
111
+ else
112
+ value.to_s
113
+ end
114
+ end
115
+
116
+ def now
117
+ Time.now.strftime('%Y/%m/%d %H:%M:%S')
118
+ end
119
+ end#Term
120
+ end#Output
121
+ end#JRubySQL
122
+
@@ -0,0 +1,82 @@
1
+ require 'jdbc-helper'
2
+
3
+ module JRubySQL
4
+ class RDBMS
5
+ include JRubySQL::Messages
6
+
7
+ def self.get_type driver_or_type
8
+ case driver_or_type.to_s.downcase
9
+ when /oracle/
10
+ :oracle
11
+ when /mysql/
12
+ :mysql
13
+ when /postgres/
14
+ :postgres
15
+ when /sqlserver/
16
+ :sqlserver
17
+ else
18
+ :unknown
19
+ end
20
+ end
21
+
22
+ def initialize options
23
+ @conn =
24
+ if options[:type]
25
+ case options[:type]
26
+ when :mysql
27
+ JDBCHelper::MySQLConnector.connect(
28
+ options[:host], options[:user], options[:password], options[:database])
29
+ when :oracle
30
+ host, svc = options[:host].split('/')
31
+ if svc.nil?
32
+ # FIXME
33
+ raise ArgumentError.new m(:oracle_service_name_required)
34
+ end
35
+ JDBCHelper::OracleConnector.connect(
36
+ host, options[:user], options[:password], svc)
37
+ when :postgres
38
+ JDBCHelper::PostgresConnector.connect(
39
+ options[:host], options[:user], options[:password], options[:database])
40
+ when :sqlserver
41
+ JDBCHelper::SqlServerConnector.connect(
42
+ options[:host], options[:user], options[:password], options[:database])
43
+ end
44
+ elsif options[:driver]
45
+ JDBCHelper::Connection.new(
46
+ {
47
+ :driver => options[:driver],
48
+ :url => options[:url],
49
+ :user => options[:user],
50
+ :password => options[:password]
51
+ }.reject { |k, v| v.nil? }
52
+ )
53
+ else
54
+ raise ArgumentError.new m(:invalid_connection)
55
+ end
56
+ end
57
+
58
+ def autocommit
59
+ @conn.java_obj.get_auto_commit
60
+ end
61
+
62
+ def autocommit= ac
63
+ @conn.java_obj.set_auto_commit ac
64
+ end
65
+
66
+ def close
67
+ @conn.close
68
+ end
69
+
70
+ def execute sql
71
+ st = Time.now
72
+ result = @conn.execute sql
73
+ elapsed = Time.now - st
74
+
75
+ {
76
+ :set? => result.respond_to?(:each),
77
+ :result => result,
78
+ :elapsed => elapsed
79
+ }
80
+ end
81
+ end#RDBMS
82
+ end#JRubySQL
@@ -0,0 +1,3 @@
1
+ module JRubySQL
2
+ VERSION = "0.1.1"
3
+ end
data/lib/jrubysql.rb ADDED
@@ -0,0 +1,36 @@
1
+ require "jrubysql/version"
2
+
3
+ if RUBY_PLATFORM.match(/java/).nil?
4
+ puts 'Sorry. jrubysql only runs on JRuby.'
5
+ exit 1
6
+ end
7
+
8
+ require 'jrubysql/messages'
9
+ require 'jrubysql/config'
10
+ require 'jrubysql/constants'
11
+ require 'jrubysql/rdbms'
12
+ require 'jrubysql/option_parser'
13
+ require 'jrubysql/input/input'
14
+ require 'jrubysql/input/console'
15
+ require 'jrubysql/input/file'
16
+ require 'jrubysql/output/csv'
17
+ require 'jrubysql/output/term'
18
+ require 'jrubysql/output/cterm'
19
+ require 'jrubysql/controller'
20
+
21
+ module JRubySQL
22
+ def self.name
23
+ "JRubySQL #{JRubySQL::VERSION}"
24
+ end
25
+
26
+ def self.launch argv
27
+ if argv.empty?
28
+ JRubySQL::Controller.new(nil, nil).start
29
+ else
30
+ argv_str = argv.join(' ')
31
+ options = JRubySQL::OptionParser.parse argv
32
+ JRubySQL::Controller.new(options, argv_str).start
33
+ end
34
+ end
35
+ end
36
+
File without changes
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ ENV['BUNDLE_GEMFILE'] = File.join(File.dirname(__FILE__), '..', 'Gemfile')
4
+ Bundler.setup(:default, :development)
5
+ require 'test/unit'
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+
8
+ class TestJRubySQL < Test::Unit::TestCase
9
+ end
File without changes
File without changes
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jrubysql
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.1
6
+ platform: ruby
7
+ authors:
8
+ - Junegunn Choi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-15 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: test-unit
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ none: false
22
+ requirement: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ none: false
28
+ prerelease: false
29
+ type: :development
30
+ - !ruby/object:Gem::Dependency
31
+ name: jdbc-helper
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ~>
35
+ - !ruby/object:Gem::Version
36
+ version: 0.7.2
37
+ none: false
38
+ requirement: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ version: 0.7.2
43
+ none: false
44
+ prerelease: false
45
+ type: :runtime
46
+ - !ruby/object:Gem::Dependency
47
+ name: insensitive_hash
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: 0.2.3
53
+ none: false
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ version: 0.2.3
59
+ none: false
60
+ prerelease: false
61
+ type: :runtime
62
+ - !ruby/object:Gem::Dependency
63
+ name: tabularize
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.1
69
+ none: false
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ~>
73
+ - !ruby/object:Gem::Version
74
+ version: 0.1.1
75
+ none: false
76
+ prerelease: false
77
+ type: :runtime
78
+ - !ruby/object:Gem::Dependency
79
+ name: each_sql
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ~>
83
+ - !ruby/object:Gem::Version
84
+ version: 0.3.1
85
+ none: false
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: 0.3.1
91
+ none: false
92
+ prerelease: false
93
+ type: :runtime
94
+ - !ruby/object:Gem::Dependency
95
+ name: highline
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ version: 1.6.11
101
+ none: false
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ~>
105
+ - !ruby/object:Gem::Version
106
+ version: 1.6.11
107
+ none: false
108
+ prerelease: false
109
+ type: :runtime
110
+ - !ruby/object:Gem::Dependency
111
+ name: ansi
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ~>
115
+ - !ruby/object:Gem::Version
116
+ version: 1.4.2
117
+ none: false
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ~>
121
+ - !ruby/object:Gem::Version
122
+ version: 1.4.2
123
+ none: false
124
+ prerelease: false
125
+ type: :runtime
126
+ - !ruby/object:Gem::Dependency
127
+ name: erubis
128
+ version_requirements: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ~>
131
+ - !ruby/object:Gem::Version
132
+ version: 2.7.0
133
+ none: false
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: 2.7.0
139
+ none: false
140
+ prerelease: false
141
+ type: :runtime
142
+ description: An SQL client for any JDBC-compliant database. Written in JRuby.
143
+ email:
144
+ - junegunn.c@gmail.com
145
+ executables:
146
+ - jrubysql
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - .gitignore
151
+ - Gemfile
152
+ - LICENSE.txt
153
+ - README.md
154
+ - Rakefile
155
+ - bin/jrubysql
156
+ - jrubysql.gemspec
157
+ - lib/jrubysql.rb
158
+ - lib/jrubysql/config.rb
159
+ - lib/jrubysql/constants.rb
160
+ - lib/jrubysql/controller.rb
161
+ - lib/jrubysql/doc/help.txt.erb
162
+ - lib/jrubysql/input/console.rb
163
+ - lib/jrubysql/input/file.rb
164
+ - lib/jrubysql/input/input.rb
165
+ - lib/jrubysql/messages.rb
166
+ - lib/jrubysql/messages.yml
167
+ - lib/jrubysql/option_parser.rb
168
+ - lib/jrubysql/output/csv.rb
169
+ - lib/jrubysql/output/cterm.rb
170
+ - lib/jrubysql/output/term.rb
171
+ - lib/jrubysql/rdbms.rb
172
+ - lib/jrubysql/version.rb
173
+ - test/test_cterm.rb
174
+ - test/test_jrubysql.rb
175
+ - test/test_option_parser.rb
176
+ - test/test_term_output.rb
177
+ homepage: https://github.com/junegunn/jrubysql
178
+ licenses: []
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ! '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ none: false
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ! '>='
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ none: false
195
+ requirements: []
196
+ rubyforge_project: jrubysql
197
+ rubygems_version: 1.8.18
198
+ signing_key:
199
+ specification_version: 3
200
+ summary: An SQL client for any JDBC-compliant database.
201
+ test_files:
202
+ - test/test_cterm.rb
203
+ - test/test_jrubysql.rb
204
+ - test/test_option_parser.rb
205
+ - test/test_term_output.rb
206
+ ...