activerecord-jdbcteradata-adapter 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "activerecord-jdbcteradata-adapter"
3
- s.version = "0.2.0"
3
+ s.version = "0.3.0"
4
4
  s.authors = ["Chris Parker"]
5
5
  s.email = [ "mrcsparker@gmail.com"]
6
6
  s.homepage = "https://github.com/mrcsparker/activerecord-jdbcteradata-adapter"
data/examples/models.rb CHANGED
@@ -12,61 +12,65 @@ CONFIG = {
12
12
  ActiveRecord::Base.establish_connection(CONFIG)
13
13
 
14
14
  class Acct < ActiveRecord::Base
15
- def self.table_name
16
- 'financial.accts'
17
- end
15
+ self.table_name = 'financial.accts'
16
+
17
+ belongs_to :customer, :foreign_key => 'cust_id'
18
18
  end
19
19
 
20
20
  class CheckingAcct < ActiveRecord::Base
21
- def self.table_name
22
- 'financial.checking_acct'
23
- end
21
+ self.table_name = 'financial.checking_acct'
22
+ self.primary_key = 'cust_id'
23
+
24
+ belongs_to :customer, :foreign_key => 'cust_id'
24
25
  end
25
26
 
26
27
  class CheckingTran < ActiveRecord::Base
27
- def self.table_name
28
- 'financial.checking_tran'
29
- end
28
+ self.table_name = 'financial.checking_tran'
29
+ self.primary_key = 'Tran_Id'
30
+
31
+ belongs_to :customer, :foreign_key => 'Cust_Id'
30
32
  end
31
33
 
32
34
  class CreditAcct < ActiveRecord::Base
33
- def self.table_name
34
- 'financial.credit_acct'
35
- end
35
+ self.table_name = 'financial.credit_acct'
36
+ self.primary_key = 'cust_id'
37
+
38
+ belongs_to :customer, :foreign_key => 'cust_id'
36
39
  end
37
40
 
38
41
  class CreditTran < ActiveRecord::Base
39
- def self.table_name
40
- 'financial.credit_tran'
41
- end
42
+ self.table_name ='financial.credit_tran'
43
+ self.primary_key = 'Tran_Id'
44
+
45
+ belongs_to :customer, :foreign_key => 'Cust_Id'
42
46
  end
43
47
 
44
48
  class Customer < ActiveRecord::Base
45
- def self.table_name
46
- 'financial.customer'
47
- end
49
+ self.table_name = 'financial.customer'
50
+ self.primary_key = 'cust_id'
48
51
  end
49
52
 
50
53
  class CustomerName < ActiveRecord::Base
51
- def self.table_name
52
- 'financial.customer_name'
53
- end
54
+ self.table_name = 'financial.customer_name'
55
+ self.primary_key = 'cust_id'
56
+
57
+ belongs_to :customer, :foreign_key => 'cust_id'
54
58
  end
55
59
 
56
60
  class SavingsAcct < ActiveRecord::Base
57
- def self.table_name
58
- 'financial.savings_acct'
59
- end
61
+ self.table_name = 'financial.savings_acct'
62
+ self.primary_key = 'cust_id'
63
+
64
+ belongs_to :customer, :foreign_key => 'cust_id'
60
65
  end
61
66
 
62
67
  class SavingsTran < ActiveRecord::Base
63
- def self.table_name
64
- 'financial.savings_tran'
65
- end
68
+ self.table_name = 'financial.savings_tran'
69
+ self.primary_key = 'Tran_Id'
70
+
71
+ belongs_to :customer, :foreign_key => 'Cust_Id'
66
72
  end
67
73
 
68
74
  class Tran < ActiveRecord::Base
69
- def self.table_name
70
- 'financial.trans'
71
- end
75
+ self.table_name = 'financial.trans'
72
76
  end
@@ -2,47 +2,6 @@ module Arel
2
2
  module Visitors
3
3
  class Teradata < Arel::Visitors::ToSql
4
4
 
5
- def add_limit_offset!(sql, options)
6
- if options[:limit]
7
- order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
8
- sql.sub!(/ ORDER BY.*$/i, '')
9
- replace_limit_offset!(sql, options[:limit], options[:offset], order)
10
- end
11
- end
12
-
13
- def replace_limit_offset!(sql, limit, offset, order)
14
- if limit
15
- offset ||= 0
16
- start_row = offset + 1
17
- end_row = offset + limit.to_i
18
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
19
- whole, select, rest_of_query = find_select.match(sql).to_a
20
- rest_of_query.strip!
21
- if rest_of_query[0...1] == "1" && rest_of_query !~ /1 AS/i
22
- rest_of_query[0] = "*"
23
- end
24
- if rest_of_query[0] == "*"
25
- from_table = get_table_name(rest_of_query)
26
- rest_of_query = from_table + '.' + rest_of_query
27
- end
28
- new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query}"
29
- new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
30
- sql.replace(new_sql)
31
- end
32
- sql
33
- end
34
-
35
-
36
- def limit_for(limit_or_node)
37
- limit_or_node.respond_to?(:expr) ? limit_or_node.expr.to_i : limit_or_node
38
- end
39
-
40
- def select_count? o
41
- sel = o.cores.length == 1 && o.cores.first
42
- projections = sel && sel.projections.length == 1 && sel.projections
43
- projections && Arel::Nodes::Count === projections.first
44
- end
45
-
46
5
  def visit_Arel_Nodes_SelectStatement o
47
6
  if !o.limit && o.offset
48
7
  raise ActiveRecord::ActiveRecordError, "You must specify :limit with :offset."
@@ -60,7 +19,7 @@ module Arel
60
19
  sql = o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join
61
20
  end
62
21
 
63
- order ||= "ORDER BY #{@connection.determine_order_clause(sql)}"
22
+ order ||= "ORDER BY #{determine_order_clause(sql)}"
64
23
  replace_limit_offset!(sql, limit_for(o.limit).to_i, o.offset && o.offset.value.to_i, order)
65
24
  sql = "SELECT COUNT(*) AS count_id FROM (#{sql}) AS subquery" if subquery
66
25
  else
@@ -69,6 +28,78 @@ module Arel
69
28
 
70
29
  sql
71
30
  end
31
+
32
+ # <Helpers>
33
+ # Lots of this code was pulled from the activerecord JDBC MSSQL driver
34
+
35
+ def get_table_name(sql)
36
+ if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
37
+ $1
38
+ elsif sql =~ /\bfrom\s+([^\(\s,]+)\s*/i
39
+ $1
40
+ else
41
+ nil
42
+ end
43
+ end
44
+
45
+ def determine_order_clause(sql)
46
+ return $1 if sql =~ /ORDER BY (.*)$/
47
+ table_name = get_table_name(sql)
48
+ "#{table_name}.#{determine_primary_key(table_name)}"
49
+ end
50
+
51
+ def determine_primary_key(table_name)
52
+ table_name = table_name.gsub('"', '')
53
+ primary_key = @connection.columns(table_name).detect { |column| column.primary }
54
+ return primary_key.name if primary_key
55
+ # Look for an id column. Return it, without changing case, to cover dbs with a case-sensitive collation.
56
+ columns(table_name).each { |column| return column.name if column.name =~ /^id$/i }
57
+ # Give up and provide something which is going to crash almost certainly
58
+ columns(table_name)[0].name
59
+ end
60
+
61
+ def add_limit_offset!(sql, options)
62
+ if options[:limit]
63
+ order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
64
+ sql.sub!(/ ORDER BY.*$/i, '')
65
+ replace_limit_offset!(sql, options[:limit], options[:offset], order)
66
+ end
67
+ end
68
+
69
+ def replace_limit_offset!(sql, limit, offset, order)
70
+ if limit
71
+ offset ||= 0
72
+ start_row = offset + 1
73
+ end_row = offset + limit.to_i
74
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
75
+ whole, select, rest_of_query = find_select.match(sql).to_a
76
+ rest_of_query.strip!
77
+ if rest_of_query[0...1] == "1" && rest_of_query !~ /1 AS/i
78
+ rest_of_query[0] = "*"
79
+ end
80
+ if rest_of_query[0] == "*"
81
+ from_table = get_table_name(rest_of_query)
82
+ rest_of_query = from_table + '.' + rest_of_query
83
+ end
84
+ new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query}"
85
+ new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
86
+ sql.replace(new_sql)
87
+ end
88
+ sql
89
+ end
90
+
91
+ def limit_for(limit_or_node)
92
+ limit_or_node.respond_to?(:expr) ? limit_or_node.expr.to_i : limit_or_node
93
+ end
94
+
95
+ def select_count? o
96
+ sel = o.cores.length == 1 && o.cores.first
97
+ projections = sel && sel.projections.length == 1 && sel.projections
98
+ projections && Arel::Nodes::Count === projections.first
99
+ end
100
+
101
+ # </Helpers>
102
+
72
103
  end
73
104
  end
74
105
  end
@@ -41,8 +41,9 @@ module ::ArJdbc
41
41
 
42
42
  #+ native_database_types
43
43
  def native_database_types
44
+
44
45
  super.merge({
45
- :primary_key => 'INTEGER PRIMARY KEY NOT NULL GENERATED ALWAYS AS IDENTITY',
46
+ :primary_key => 'INTEGER PRIMARY KEY NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1 MINVALUE -2147483647 MAXVALUE 1000000000 NO CYCLE)',
46
47
  :string => { :name => 'VARCHAR', :limit => 255 },
47
48
  :integer => { :name => "INTEGER" },
48
49
  :float => { :name => "FLOAT" },
@@ -80,6 +81,25 @@ module ::ArJdbc
80
81
  #+ do_exec
81
82
 
82
83
  #- execute
84
+ def _execute(sql, name = nil)
85
+ result = super
86
+ self.class.insert?(sql) ? last_insert_id(_table_name_from_insert(sql)) : result
87
+ end
88
+ private :_execute
89
+
90
+ def _table_name_from_insert(sql)
91
+ sql.split(" ", 4)[2].gsub('"', '').gsub("'", "")
92
+ end
93
+ private :_table_name_from_insert
94
+
95
+ def last_insert_id(table)
96
+ output = nil
97
+ pk = primary_key(table)
98
+ if pk
99
+ output = execute("SELECT TOP 1 #{pk} FROM #{table} ORDER BY #{pk} DESC").first[pk]
100
+ end
101
+ output
102
+ end
83
103
 
84
104
  #- select
85
105
 
@@ -154,65 +174,5 @@ module ::ArJdbc
154
174
  name.to_s
155
175
  end
156
176
 
157
- # <Helpers>
158
-
159
- def get_table_name(sql)
160
- if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
161
- $1
162
- elsif sql =~ /\bfrom\s+([^\(\s,]+)\s*/i
163
- $1
164
- else
165
- nil
166
- end
167
- end
168
-
169
- def determine_order_clause(sql)
170
- return $1 if sql =~ /ORDER BY (.*)$/
171
- table_name = get_table_name(sql)
172
- "#{table_name}.#{determine_primary_key(table_name)}"
173
- end
174
-
175
- def determine_primary_key(table_name)
176
- table_name = table_name.gsub('"', '')
177
- primary_key = columns(table_name).detect { |column| column.primary }
178
- return primary_key.name if primary_key
179
- # Look for an id column. Return it, without changing case, to cover dbs with a case-sensitive collation.
180
- columns(table_name).each { |column| return column.name if column.name =~ /^id$/i }
181
- # Give up and provide something which is going to crash almost certainly
182
- columns(table_name)[0].name
183
- end
184
-
185
- def add_limit_offset!(sql, options)
186
- if options[:limit]
187
- order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
188
- sql.sub!(/ ORDER BY.*$/i, '')
189
- replace_limit_offset!(sql, options[:limit], options[:offset], order)
190
- end
191
- end
192
-
193
- def replace_limit_offset!(sql, limit, offset, order)
194
- if limit
195
- offset ||= 0
196
- start_row = offset + 1
197
- end_row = offset + limit.to_i
198
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
199
- whole, select, rest_of_query = find_select.match(sql).to_a
200
- rest_of_query.strip!
201
- if rest_of_query[0...1] == "1" && rest_of_query !~ /1 AS/i
202
- rest_of_query[0] = "*"
203
- end
204
- if rest_of_query[0] == "*"
205
- from_table = get_table_name(rest_of_query)
206
- rest_of_query = from_table + '.' + rest_of_query
207
- end
208
- new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query}"
209
- new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
210
- sql.replace(new_sql)
211
- end
212
- sql
213
- end
214
-
215
- # </Helpers>
216
-
217
177
  end
218
178
  end
data/spec/adapter_spec.rb CHANGED
@@ -24,6 +24,26 @@ describe 'Adapter' do
24
24
  @adapter.active?.should be_true
25
25
  end
26
26
 
27
+ it '#exec_query' do
28
+ article_1 = Article.create(:title => 'exec_query_1', :body => 'exec_query_1')
29
+ article_2 = Article.create(:title => 'exec_query_2', :body => 'exec_query_2')
30
+ articles = @adapter.exec_query('select * from articles')
31
+
32
+ articles.select { |i| i['title'] == article_1.title }.first.should_not be_nil
33
+ articles.select { |i| i['title'] == article_2.title }.first.should_not be_nil
34
+ end
35
+
36
+ it '#last_insert_id(table)' do
37
+ article_1 = Article.create(:title => 'exec_query_1', :body => 'exec_query_1')
38
+ article_1.id.should eq(@adapter.last_insert_id('articles'))
39
+
40
+ article_2 = Article.create(:title => 'exec_query_2', :body => 'exec_query_2')
41
+
42
+ article_1.id.should_not eq(article_2.id)
43
+
44
+ article_2.id.should eq(@adapter.last_insert_id('articles'))
45
+ end
46
+
27
47
  it '#tables' do
28
48
  @adapter.tables.should include('articles')
29
49
  end
data/spec/simple_spec.rb CHANGED
@@ -37,13 +37,32 @@ describe 'SimpleSpec' do
37
37
  end
38
38
 
39
39
  it 'should be able to find(:where) an item' do
40
+ article = Article.create(:title => 'where', :body => 'where')
41
+ article = Article.where(:title => 'where').first
42
+ article.title.should eq('where')
43
+ end
44
+
45
+ it 'should be able to #reload and item' do
40
46
  article = Article.create(:title => 'reload', :body => 'reload')
41
- article = Article.where(:title => 'reload').first
47
+ article.reload
42
48
  article.title.should eq('reload')
43
49
  end
44
50
 
45
- it 'should be able to #reload and item' do
51
+ it 'should populate distinct ids for the primary key field after reload' do
52
+ article_1 = Article.create(:title => 'pk1_item_reload', :body => 'pk1_item_reload')
53
+ article_2 = Article.create(:title => 'pk2_item_reload', :body => 'pk2_item_reload')
54
+
55
+ article_1.reload
56
+ article_2.reload
57
+
58
+ article_1.id.should_not eq(article_2.id)
59
+ end
60
+
61
+ it 'should populate distinct ids for the primary key field' do
62
+ article_1 = Article.create(:title => 'pk1_item', :body => 'pk1_item')
63
+ article_2 = Article.create(:title => 'pk2_item', :body => 'pk2_item')
46
64
 
65
+ article_1.id.should_not eq(article_2.id)
47
66
  end
48
67
 
49
68
  after(:all) do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-jdbcteradata-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-28 00:00:00.000000000 Z
12
+ date: 2013-03-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -108,6 +108,7 @@ executables: []
108
108
  extensions: []
109
109
  extra_rdoc_files: []
110
110
  files:
111
+ - ".gitignore"
111
112
  - ".rspec"
112
113
  - Gemfile
113
114
  - Gemfile.lock