teradata-cli 0.0.1

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.
@@ -0,0 +1,15 @@
1
+ #
2
+ # $Id: exception.rb 7 2010-03-04 16:54:09Z tdaoki $
3
+ #
4
+ # Copyright (C) 2009,2010 Teradata Japan, LTD.
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU LGPL2, Lesser General Public License version 2.
9
+ #
10
+
11
+ module Teradata
12
+
13
+ class Error < StandardError; end
14
+
15
+ end
@@ -0,0 +1,184 @@
1
+ #
2
+ # $Id: utils.rb 7 2010-03-04 16:54:09Z tdaoki $
3
+ #
4
+ # Copyright (C) 2009,2010 Teradata Japan, LTD.
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU LGPL2, Lesser General Public License version 2.
9
+ #
10
+
11
+ require 'teradata/exception'
12
+
13
+ module Teradata
14
+
15
+ class BadLogonString < Error; end
16
+
17
+ class LogonString
18
+ def LogonString.intern(arg)
19
+ arg.kind_of?(LogonString) ? arg : LogonString.parse(arg.to_s)
20
+ end
21
+
22
+ def LogonString.parse(str)
23
+ m = %r<\A(?:([^/\s]+)/)?(\w+),(\w+)(?:,('.*'))?\z>.match(str) or
24
+ raise BadLogonString, "bad logon string: #{str.inspect}"
25
+ new(* m.captures)
26
+ end
27
+
28
+ def initialize(tdpid, user, password, account = nil)
29
+ @tdpid = tdpid
30
+ @user = user
31
+ @password = password
32
+ @account = account
33
+ end
34
+
35
+ attr_reader :tdpid
36
+ attr_reader :user
37
+ attr_reader :password
38
+ attr_reader :account
39
+
40
+ def to_s
41
+ "#{@tdpid ? @tdpid + '/' : ''}#{@user},#{@password}#{@account ? ',' + @account : ''}"
42
+ end
43
+
44
+ def safe_string
45
+ "#{@tdpid ? @tdpid + '/' : ''}#{@user},****#{@account ? ',' + @account : ''}"
46
+ end
47
+
48
+ def inspect
49
+ "\#<#{self.class} #{to_s}>"
50
+ end
51
+ end
52
+
53
+ class SessionCharset
54
+ def SessionCharset.intern(arg)
55
+ arg.kind_of?(SessionCharset) ? arg : SessionCharset.new(arg.to_s)
56
+ end
57
+
58
+ def initialize(name)
59
+ @name = name
60
+ end
61
+
62
+ attr_reader :name
63
+ alias to_s name
64
+
65
+ if defined?(::Encoding) # M17N
66
+ def encoding
67
+ case @name
68
+ when /UTF8/i then Encoding::UTF_8
69
+ when /KANJISJIS_0S/i then Encoding::Windows_31J
70
+ when /KANJIEUC_0U/i then Encoding::EUC_JP
71
+ when /ASCII/i then Encoding::US_ASCII
72
+ else
73
+ raise ArgumentError, "could not convert session charset to encoding name: #{sc.inspect}"
74
+ end
75
+ end
76
+ else
77
+ def encoding
78
+ nil
79
+ end
80
+ end
81
+ end
82
+
83
+ module MetadataUtils
84
+ def adjust_list_size(list, size)
85
+ if list.size > size
86
+ list[0...size]
87
+ else
88
+ list.push nil while list.size < size
89
+ list
90
+ end
91
+ end
92
+ end
93
+
94
+ SESSION_ATTRIBUTES = [
95
+ :user_name,
96
+ :account_name,
97
+ :logon_date,
98
+ :logon_time,
99
+ :current_database,
100
+ :collation,
101
+ :character_set,
102
+ :transaction_semantics,
103
+ :current_dateform,
104
+ :timezone,
105
+ :default_character_type,
106
+ :export_latin,
107
+ :export_unicode,
108
+ :export_unicode_adjust,
109
+ :export_kanjisjis,
110
+ :export_graphic,
111
+ :default_date_format,
112
+ :radix_separator,
113
+ :group_separator,
114
+ :grouping_rule,
115
+ :currency_radix_separator,
116
+ :currency_graphic_rule,
117
+ :currency_grouping_rule,
118
+ :currency_name,
119
+ :currency,
120
+ :iso_currency,
121
+ :dual_currency_name,
122
+ :dual_currency,
123
+ :dual_iso_currency,
124
+ :default_byteint_format,
125
+ :default_integer_format,
126
+ :default_smallint_format,
127
+ :default_numeric_format,
128
+ :default_real_format,
129
+ :default_time_format,
130
+ :default_timestamp_format,
131
+ :current_role,
132
+ :logon_account,
133
+ :profile,
134
+ :ldap,
135
+ :audit_trail_id,
136
+ :current_isolation_level,
137
+ :default_bigint_format,
138
+ :query_band
139
+ ]
140
+
141
+ SessionInfo = Struct.new(*SESSION_ATTRIBUTES)
142
+
143
+ class SessionInfo # reopen
144
+ extend MetadataUtils
145
+
146
+ def SessionInfo.for_record(rec)
147
+ new(* adjust_list_size(rec.to_a, SESSION_ATTRIBUTES.size))
148
+ end
149
+ end
150
+
151
+ module SQLUtils
152
+ private
153
+
154
+ def sql_int(n)
155
+ return 'NULL' unless n
156
+ n
157
+ end
158
+
159
+ alias int sql_int
160
+
161
+ def sql_string(str)
162
+ return 'NULL' unless str
163
+ "'" + str.gsub(/'/, "''") + "'"
164
+ end
165
+
166
+ alias string sql_string
167
+
168
+ def sql_date(d)
169
+ return 'NULL' unless d
170
+ "DATE '#{d.strftime('%Y-%m-%d')}'"
171
+ end
172
+
173
+ alias date sql_date
174
+
175
+ def sql_timestamp(t)
176
+ return 'NULL' unless t
177
+ "TIMESTAMP '#{t.strftime('%Y-%m-%d %H:%M:%S')}'"
178
+ end
179
+
180
+ alias timestamp sql_timestamp
181
+ end
182
+
183
+
184
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'teradata/cli/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "teradata-cli"
8
+ spec.version = Teradata::Cli::VERSION
9
+ spec.authors = ["Giuseppe Privitera"]
10
+ spec.email = ["priviterag@gmail.com"]
11
+ spec.description = %q{ruby extension for Teradata Cliv2}
12
+ spec.summary = %q{ruby extension for Teradata Cliv2}
13
+ spec.homepage = "https://github.com/priviterag/teradata-cli"
14
+ spec.license = "LGPL2"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.extensions = ["ext/teradata/cli/extconf.rb"]
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ end
data/test/all ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ basedir = File.dirname($0)
4
+ $LOAD_PATH.unshift basedir
5
+ Dir.glob("#{basedir}/test_*.rb") do |test_case|
6
+ load test_case
7
+ end
@@ -0,0 +1,99 @@
1
+ module RubyCLITestUtils
2
+
3
+ def logon_string
4
+ s = ENV['TEST_LOGON_STRING'] or raise ArgumentError, "environ TEST_LOGON_STRING not given"
5
+ Teradata::LogonString.parse(s)
6
+ end
7
+
8
+ def playpen_string
9
+ s = ENV['TEST_PLAYPEN_STRING']
10
+ s == '' ? nil : "#{s}"
11
+ end
12
+
13
+ def env_string
14
+ s = ENV['TEST_ENV_STRING']
15
+ s == '' ? nil : "#{s}"
16
+ end
17
+
18
+ def get_table_name(name)
19
+ "#{playpen_string ? playpen_string + '.' : ''}#{env_string ? env_string + '_' : ''}#{name}"
20
+ end
21
+
22
+ def connect(*args)
23
+ options = {}
24
+ unless args.empty?
25
+ charset, internal = args
26
+ options[:session_charset] = charset if charset
27
+ options[:internal_encoding] = internal if internal
28
+ end
29
+ Teradata::Connection.open(logon_string, options) {|conn|
30
+ begin
31
+ @conn = conn
32
+ yield conn
33
+ ensure
34
+ @conn = nil
35
+ end
36
+ }
37
+ end
38
+
39
+ def using_test_table(name = "#{get_table_name('t')}", conn = @conn, &block)
40
+ unless conn
41
+ connect {|_conn| using_test_table(name, _conn, &block) }
42
+ return
43
+ end
44
+ using_table(name, 'x INTEGER, y INTEGER', conn) do |n|
45
+ %w(1,2 3,4 5,6).each do |values|
46
+ insert n, values, conn
47
+ end
48
+ yield n
49
+ end
50
+ end
51
+
52
+ def using_table(name, fields, conn = @conn, &block)
53
+ unless conn
54
+ connect {|_conn| using_table(name, fields, _conn, &block) }
55
+ return
56
+ end
57
+ drop_table_force name, conn
58
+ conn.execute_update "CREATE TABLE #{name} (#{fields});"
59
+ begin
60
+ yield name
61
+ ensure
62
+ drop_table_force name, conn
63
+ end
64
+ end
65
+
66
+ def create_table(name, fields, conn = @conn)
67
+ conn.execute_update "CREATE TABLE #{name} (#{fields});"
68
+ end
69
+
70
+ ERR_OBJECT_NOT_EXIST = 3807
71
+ ERR_INDEX_NOT_EXIST = 3526
72
+
73
+ def drop_table_force(name, conn = @conn)
74
+ drop_table name, conn
75
+ rescue Teradata::SQLError => err
76
+ raise err unless err.code == ERR_OBJECT_NOT_EXIST
77
+ end
78
+
79
+ def drop_table(name, conn = @conn)
80
+ drop 'TABLE', name, conn
81
+ end
82
+
83
+ def drop(type, name, conn = @conn)
84
+ conn.execute_update "DROP #{type} #{name};"
85
+ end
86
+
87
+ def delete(table, conn = @conn)
88
+ conn.execute_update "DELETE FROM #{table};"
89
+ end
90
+
91
+ def insert(table, values, conn = @conn)
92
+ conn.execute_update "INSERT INTO #{table} (#{values});"
93
+ end
94
+
95
+ def select(table, conn = @conn)
96
+ conn.entries "SELECT * FROM #{table} ORDER BY 1;"
97
+ end
98
+
99
+ end
@@ -0,0 +1,298 @@
1
+ require 'teradata'
2
+ require 'test/unit'
3
+ libdir = File.dirname(__FILE__)
4
+ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
5
+ require 'rubyclitestutils'
6
+
7
+ class Test_Connection < Test::Unit::TestCase
8
+
9
+ include RubyCLITestUtils
10
+
11
+ def test_s_open
12
+ begin
13
+ conn = Teradata::Connection.open(logon_string)
14
+ assert_instance_of Teradata::Connection, conn
15
+ assert_equal false, conn.closed?
16
+ ensure
17
+ begin
18
+ conn.close
19
+ rescue
20
+ end
21
+ assert_equal true, conn.closed?
22
+ end
23
+
24
+ Teradata::Connection.open(logon_string) {|c| assert_instance_of(Teradata::Connection, c); assert_equal(false, c.closed?)}
25
+ assert_equal true, conn.closed?
26
+ end
27
+
28
+ def test_execute_update
29
+ connect {
30
+ drop_table_force "#{get_table_name('t')}"
31
+ x = @conn.execute_update("CREATE TABLE #{get_table_name('t')} (x INTEGER);")
32
+ assert_instance_of Teradata::ResultSet, x
33
+ assert_equal true, x.closed?
34
+ }
35
+ end
36
+
37
+ def test_txn
38
+ using_table("#{get_table_name('t')}", "x INTEGER, y INTEGER") {|name|
39
+ @conn.execute_update "BEGIN TRANSACTION;"
40
+ @conn.execute_update "INSERT INTO #{name} (x,y) VALUES (1,2);"
41
+ @conn.execute_update "INSERT INTO #{name} (x,y) VALUES (3,4);"
42
+ @conn.execute_update "END TRANSACTION;"
43
+ recs = @conn.entries("SELECT * FROM #{name} ORDER BY 1;")
44
+ assert_equal 2, recs.size
45
+
46
+ begin
47
+ @conn.execute_update "BEGIN TRANSACTION;"
48
+ @conn.execute_update "DELETE FROM #{name};"
49
+ @conn.execute_update "ABORT;"
50
+ rescue Teradata::UserAbort
51
+ end
52
+ recs = @conn.entries("SELECT * FROM #{name} ORDER BY 1;")
53
+ assert_equal 2, recs.size
54
+ assert_equal 1, recs[0][:x]
55
+ }
56
+ end
57
+
58
+ def test_execute_query
59
+ using_test_table {|name|
60
+ _test_single_rs name, @conn
61
+ _test_single_rs2 name, @conn
62
+ _test_multiple_rs name, @conn
63
+ }
64
+ end
65
+
66
+ def _test_single_rs(name, conn)
67
+ buf = []
68
+ conn.execute_query("SELECT * FROM #{name} ORDER BY 1") {|rs|
69
+ assert_instance_of Teradata::ResultSet, rs
70
+ rs.each do |rec|
71
+ buf.push rec
72
+ end
73
+ }
74
+ assert_equal 3, buf.size
75
+ assert_instance_of Teradata::Record, buf[0]
76
+ assert_equal 1, buf[0][:x]
77
+ assert_equal 2, buf[0][:y]
78
+ assert_instance_of Teradata::Record, buf[1]
79
+ assert_equal 3, buf[1][:x]
80
+ assert_equal 4, buf[1][:y]
81
+ assert_instance_of Teradata::Record, buf[2]
82
+ assert_equal 5, buf[2][:x]
83
+ assert_equal 6, buf[2][:y]
84
+ end
85
+
86
+ def _test_single_rs2(name, conn)
87
+ buf = []
88
+ num_rs = 0
89
+ conn.execute_query("SELECT * FROM #{name} ORDER BY 1") {|sets|
90
+ assert_instance_of Teradata::ResultSet, sets
91
+ sets.each_result_set do |rs|
92
+ num_rs += 1
93
+ assert_instance_of Teradata::ResultSet, rs
94
+ rs.each do |rec|
95
+ buf.push rec
96
+ end
97
+ end
98
+ }
99
+ assert_equal 1, num_rs
100
+ assert_equal 3, buf.size
101
+ buf.each do |r|
102
+ assert_instance_of Teradata::Record, r
103
+ end
104
+ assert_equal [1,2], [buf[0][:x], buf[0][:y]]
105
+ assert_equal [3,4], [buf[1][:x], buf[1][:y]]
106
+ assert_equal [5,6], [buf[2][:x], buf[2][:y]]
107
+ end
108
+
109
+ def _test_multiple_rs(name, conn)
110
+ buf = []
111
+ num_rs = 0
112
+ conn.execute_query(
113
+ "SELECT * FROM #{name} ORDER BY 1;
114
+ SELECT * FROM #{name} ORDER BY 1 DESC;") {|sets|
115
+ assert_instance_of Teradata::ResultSet, sets
116
+ sets.each_result_set do |rs|
117
+ num_rs += 1
118
+ assert_instance_of Teradata::ResultSet, rs
119
+ rs.each do |rec|
120
+ buf.push rec
121
+ end
122
+ end
123
+ }
124
+ assert_equal 2, num_rs
125
+ assert_equal 6, buf.size
126
+ buf.each do |r|
127
+ assert_instance_of Teradata::Record, r
128
+ end
129
+ assert_equal [1,2], [buf[0][:x], buf[0][:y]]
130
+ assert_equal [3,4], [buf[1][:x], buf[1][:y]]
131
+ assert_equal [5,6], [buf[2][:x], buf[2][:y]]
132
+ assert_equal [5,6], [buf[3][:x], buf[3][:y]]
133
+ assert_equal [3,4], [buf[4][:x], buf[4][:y]]
134
+ assert_equal [1,2], [buf[5][:x], buf[5][:y]]
135
+ end
136
+
137
+ def test_execute_query_without_block
138
+ using_test_table {|name|
139
+ rs = @conn.execute_query("SELECT * FROM #{get_table_name('t')} ORDER BY 1;")
140
+
141
+ recs = []
142
+ rs.each do |rec|
143
+ recs.push rec
144
+ end
145
+ assert_equal 3, recs.size
146
+ assert_equal 1, recs[0][:x]
147
+ assert_equal 6, recs[2][:y]
148
+
149
+ recs = rs.entries
150
+ assert_equal 3, recs.size
151
+ assert_equal 1, recs[0][:x]
152
+ assert_equal 6, recs[2][:y]
153
+ }
154
+ end
155
+
156
+ def test_entries
157
+ using_test_table {|name|
158
+ recs = @conn.entries("SELECT * FROM #{name} ORDER BY 1;")
159
+ assert_equal 3, recs.size
160
+ assert_equal 1, recs[0][:x]
161
+ assert_equal 6, recs[2][:y]
162
+ }
163
+ end
164
+
165
+ # Teradata hates "\n", check it.
166
+ def test_line_terms
167
+ using_test_table {|name|
168
+ recs = @conn.entries("SELECT *\nFROM #{name} \n ORDER BY 1;")
169
+ assert_equal 3, recs.size
170
+
171
+ assert_nothing_thrown {
172
+ @conn.execute_update "INSERT INTO #{name}\n(x,y)\nVALUES \n (7,8);"
173
+ }
174
+ recs = @conn.entries("SELECT *\nFROM #{name} \n ORDER BY 1;")
175
+ assert_equal 4, recs.size
176
+ }
177
+ end
178
+
179
+ # connection/request intersection test
180
+ def test_duplicated_connections
181
+ assert_nothing_thrown {
182
+ connect {|c1|
183
+ connect {|c2|
184
+ drop_table_force "#{get_table_name('t')}", c1
185
+ c2.execute_update "CREATE TABLE #{get_table_name('t')} (x INTEGER);"
186
+ drop_table_force "#{get_table_name('t')}", c1
187
+ c2.execute_update "CREATE TABLE #{get_table_name('t')} (x INTEGER);"
188
+ drop_table_force "#{get_table_name('t')}", c1
189
+ }
190
+ }
191
+ }
192
+ end
193
+
194
+ def test_tables
195
+ db = playpen_string
196
+ connect {|conn|
197
+ # assert_equal [], @conn.tables(db)
198
+ using_test_table(get_table_name('t1')) {
199
+ using_test_table(get_table_name('t2')) {
200
+ list = @conn.tables(db)
201
+ assert(list.include? Teradata::Table.new(db, 't1'))
202
+ assert(list.include? Teradata::Table.new(db, 't2'))
203
+ }}
204
+ }
205
+ end
206
+
207
+ def test_views
208
+ db = playpen_string
209
+ using_test_table do
210
+ using_view("#{get_table_name('v')}", 'select 1 as i') do
211
+ assert(@conn.views(db).include? Teradata::View.new(db, 'v'))
212
+ end
213
+ end
214
+ end
215
+
216
+ def test_objects
217
+ db = playpen_string
218
+ connect do
219
+ # assert_equal [], @conn.objects(db)
220
+ using_test_table(get_table_name('t')) do
221
+ using_view("#{get_table_name('v')}", 'select 1 as i') do
222
+ objects = @conn.objects(db)
223
+ assert(objects.include? Teradata::Table.new(db, 't'))
224
+ assert(objects.include? Teradata::View.new(db, 'v'))
225
+ end
226
+ end
227
+ end
228
+ end
229
+
230
+ def using_view(name, query, conn = @conn)
231
+ drop_view_force name, conn
232
+ begin
233
+ conn.execute_update "CREATE VIEW #{name} AS #{query}"
234
+ yield name
235
+ ensure
236
+ drop_view_force name, conn
237
+ end
238
+ end
239
+
240
+ def drop_view_force(name, conn = @conn)
241
+ conn.execute_update "DROP VIEW #{name}"
242
+ rescue Teradata::SQLError
243
+ end
244
+
245
+ def test_info
246
+ connect {
247
+ info = @conn.info
248
+ assert_instance_of Teradata::SessionInfo, info
249
+ assert_equal logon_string.user.downcase, info.user_name.downcase
250
+ }
251
+ end
252
+
253
+ def test_column
254
+ db = playpen_string
255
+ using_table("#{get_table_name('t')}", "x INTEGER, y INTEGER") do
256
+ col = @conn.column(Teradata::Table.new(db, 't'), 'x')
257
+ assert_instance_of Teradata::Column, col
258
+ assert_equal 'x', col.column_name.strip.downcase
259
+ end
260
+ end
261
+
262
+ def test_transaction
263
+ connect {|conn|
264
+ using_test_table("#{get_table_name('t')}") {|table|
265
+ n_records = count(table, conn)
266
+
267
+ # transaction fails #1
268
+ assert_raise(RuntimeError) {
269
+ conn.transaction {
270
+ conn.query "DELETE FROM #{table}"
271
+ raise RuntimeError, "USER ABORT"
272
+ }
273
+ }
274
+ assert_equal n_records, count(table, conn)
275
+
276
+ # transaction fails #2
277
+ assert_raise(Teradata::UserAbort) {
278
+ conn.transaction {
279
+ conn.query "DELETE FROM #{table}"
280
+ conn.abort
281
+ }
282
+ }
283
+ assert_equal n_records, count(table, conn)
284
+
285
+ # transaction success
286
+ conn.transaction {
287
+ conn.query "DELETE FROM #{table}"
288
+ }
289
+ assert_equal 0, count(table, conn)
290
+ }
291
+ }
292
+ end
293
+
294
+ def count(table, conn)
295
+ conn.entries("SELECT count(*) FROM #{table}").first[0]
296
+ end
297
+
298
+ end