ar-sybase-jdbc-adapter 0.2.0 → 0.2.2

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.
@@ -1,15 +1,16 @@
1
1
  = ar-sybase-jdbc-adapter
2
2
 
3
- ar-sybase-jdbc-adapter enhances activerecord-jdbc-adapter (Rails 3) to support "limit" and "offset" for Sybase ASE DB.
3
+ ar-sybase-jdbc-adapter enhances activerecord-jdbc-adapter (Rails 3) to support <tt>.limit</tt> and <tt>offset</tt> for Sybase ASE DB.
4
4
 
5
- **This project is a proof of concept that Sybase ASE can work nicely with Rails**
6
- Once the project reaches "close to production" functionality I will try to merge it with activerecord-jdbc-adapter
5
+ At the moment Sybase ASE version 15 or grater is required. If you need it work for ASE version 12, please open an Issue[https://github.com/arkadiyk/ar-sybase-jdbc-adapter/issues]
7
6
 
7
+ <em>This project is a proof of concept that Sybase ASE can work nicely with Rails</em>. Once the project reaches "close to production" functionality I will try to merge it with activerecord-jdbc-adapter
8
+
9
+ If you have any issues with the adapter please add an Issue[https://github.com/arkadiyk/ar-sybase-jdbc-adapter/issues] or fork the project and send a pull request.
8
10
 
9
11
  == Usage
10
12
  1. Install
11
-
12
- gem install ar-sybase-jdbc-adapter
13
+ gem install ar-sybase-jdbc-adapter
13
14
 
14
15
  2. Configuration
15
16
  To use this gem, set the "dialect" configuration parameter to "sybase_jtds".
@@ -24,22 +25,19 @@ Example:
24
25
  driver: net.sourceforge.jtds.jdbc.Driver
25
26
  url: jdbc:jtds:sybase://host:port/db_name
26
27
 
28
+ == Implementation notes
29
+ If <tt>.limit</tt> with no <tt>.offset</tt> or <tt>.count</tt> methods is used, the adapter simply adds "TOP" keyword to SQL and sends it to the Sybase server:
30
+ User.limit(10)
31
+ produces:
32
+ SELECT TOP 10 users.* FROM users
27
33
 
28
- == Offset in Sybase ASE
29
-
30
- Sybase is an awful DB in term of support for "offset". I found 2 ways to do it for generic queries:
31
-
32
- === 1. Use TempTables:
33
-
34
- select top <limit + offset> * into #tt from (<original query with NO order by>) t ORDER BY <original order> ASC
35
- select top <limit> * from #tt into #tt_sorted ORDER BY <original order> DESC
36
- select * from #tt_sorted ORDER BY <original order>
37
-
38
- There are 2 major drawbacks here.
39
- 1. If the offset is a large number, the space taken by the temptable can fill up the tempdb.
40
- 2. The original query should be parsed to strip out "ORDER BY"
34
+ The adapter has to rely on Java code to implement <tt>.offset</tt> or when <tt>.count</tt> is used together with <tt>.offset</tt> or <tt>.limit</tt>. In this case adapter will generate SQL like it was MySQL query:
35
+ User.limit(10).offset(20)
36
+ produces
37
+ SELECT users.* FROM users LIMIT 10 OFFSET 21
38
+ This can be confusing if you are looking at the log file.
41
39
 
42
- === 2. Use a scrollable cursor:
40
+ Java layer parses the SQL and executes it as multistep scrollable cursor query:
43
41
 
44
42
  declare crsr insensitive scroll cursor for
45
43
  select * from <original query>
@@ -52,40 +50,15 @@ There are 2 major drawbacks here.
52
50
  close crsr
53
51
  deallocate crsr
54
52
 
55
- The problems here are:
56
- 1. Scrollable cursor works for Sybase ASE starting from version 15.
57
- 2. Cursors are not very efficient in Sybase ASE, and very inefficient in Sybase IQ.
58
-
59
- I am not a Sybase expert, so *Please let me know if you are aware of more efficient ways to do limit and offset.*
60
-
61
- == Limit and Count in Sybase ASE
62
-
63
- There is another interesting issue with Sybase DB I have just discovered. To implement "limit" I add "TOP <limit>" to the
64
- query and it works fine. To check how many records this query returns the obvios thing is to run something like
65
-
66
- select count(*) from (select top 10 * from table_name) t -- DOES NOT WORK!
67
-
68
- But it does not work! The result of this query will be total number of rows in the table. So the only solution will be to
69
- fall back to `cursor` and get `@@rowcount` to get the number of rows.
70
53
 
71
- declare crsr insensitive scroll cursor for
72
- select * from <original query>
73
- go
74
- open crsr
75
-
76
- set cursor rows <limit> for crsr
77
- fetch absolute <offset> from crsr
78
-
79
- select @@rowcount
80
-
81
- close crsr
82
- deallocate crsr
54
+ Unfortunately this approach is not very efficient for very large OFFSET values. Also scrollable cursor works for Sybase ASE starting from version 15.
83
55
 
56
+ I am not a Sybase expert, so <em>Please let me know if you are aware of more efficient ways to do limit and offset.</em>
84
57
 
85
58
 
86
59
  == Known issues
87
60
 
88
- I am aware of a very strange issue where the driver does not work when the very first query uses "limit()".
61
+ I am aware of a very strange issue where the adapter does not work when the very first query uses "limit()".
89
62
 
90
63
  e.g.
91
64
  $ rails c
@@ -93,7 +66,7 @@ e.g.
93
66
  irb(main):001:0> Client.limit(10).to_sql
94
67
  => "SELECT clients.* FROM clients LIMIT 10"
95
68
 
96
- Otherwise, the driver works fine by adding the "TOP" keyword to your SQL query:
69
+ Otherwise, the adapter works fine by adding the "TOP" keyword to your SQL query:
97
70
 
98
71
  e.g.
99
72
  $ rails c
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar-sybase-jdbc-adapter
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 2
8
- - 0
9
- version: 0.2.0
4
+ prerelease:
5
+ version: 0.2.2
10
6
  platform: ruby
11
7
  authors:
12
8
  - arkadiy kraportov
@@ -14,45 +10,38 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2011-01-17 00:00:00 +09:00
13
+ date: 2011-06-15 00:00:00 +09:00
18
14
  default_executable:
19
15
  dependencies:
20
16
  - !ruby/object:Gem::Dependency
21
17
  name: bundler
22
18
  version_requirements: &id001 !ruby/object:Gem::Requirement
19
+ none: false
23
20
  requirements:
24
21
  - - ~>
25
22
  - !ruby/object:Gem::Version
26
- segments:
27
- - 1
28
- - 0
29
- - 0
30
- version: 1.0.0
23
+ version: 1.0.15
31
24
  requirement: *id001
32
25
  prerelease: false
33
26
  type: :development
34
27
  - !ruby/object:Gem::Dependency
35
28
  name: jeweler
36
29
  version_requirements: &id002 !ruby/object:Gem::Requirement
30
+ none: false
37
31
  requirements:
38
32
  - - ~>
39
33
  - !ruby/object:Gem::Version
40
- segments:
41
- - 1
42
- - 5
43
- - 1
44
- version: 1.5.1
34
+ version: 1.6.2
45
35
  requirement: *id002
46
36
  prerelease: false
47
37
  type: :development
48
38
  - !ruby/object:Gem::Dependency
49
39
  name: rcov
50
40
  version_requirements: &id003 !ruby/object:Gem::Requirement
41
+ none: false
51
42
  requirements:
52
43
  - - ">="
53
44
  - !ruby/object:Gem::Version
54
- segments:
55
- - 0
56
45
  version: "0"
57
46
  requirement: *id003
58
47
  prerelease: false
@@ -60,11 +49,10 @@ dependencies:
60
49
  - !ruby/object:Gem::Dependency
61
50
  name: minitest
62
51
  version_requirements: &id004 !ruby/object:Gem::Requirement
52
+ none: false
63
53
  requirements:
64
54
  - - ">="
65
55
  - !ruby/object:Gem::Version
66
- segments:
67
- - 0
68
56
  version: "0"
69
57
  requirement: *id004
70
58
  prerelease: false
@@ -72,27 +60,21 @@ dependencies:
72
60
  - !ruby/object:Gem::Dependency
73
61
  name: arel
74
62
  version_requirements: &id005 !ruby/object:Gem::Requirement
63
+ none: false
75
64
  requirements:
76
- - - "="
65
+ - - ~>
77
66
  - !ruby/object:Gem::Version
78
- segments:
79
- - 2
80
- - 0
81
- - 7
82
- version: 2.0.7
67
+ version: 2.0.10
83
68
  requirement: *id005
84
69
  prerelease: false
85
70
  type: :development
86
71
  - !ruby/object:Gem::Dependency
87
72
  name: activerecord-jdbc-adapter
88
73
  version_requirements: &id006 !ruby/object:Gem::Requirement
74
+ none: false
89
75
  requirements:
90
76
  - - "="
91
77
  - !ruby/object:Gem::Version
92
- segments:
93
- - 1
94
- - 1
95
- - 1
96
78
  version: 1.1.1
97
79
  requirement: *id006
98
80
  prerelease: false
@@ -100,27 +82,21 @@ dependencies:
100
82
  - !ruby/object:Gem::Dependency
101
83
  name: activerecord
102
84
  version_requirements: &id007 !ruby/object:Gem::Requirement
85
+ none: false
103
86
  requirements:
104
- - - ">="
87
+ - - ~>
105
88
  - !ruby/object:Gem::Version
106
- segments:
107
- - 3
108
- - 0
109
- - 3
110
- version: 3.0.3
89
+ version: 3.0.7
111
90
  requirement: *id007
112
91
  prerelease: false
113
92
  type: :development
114
93
  - !ruby/object:Gem::Dependency
115
94
  name: activerecord-jdbc-adapter
116
95
  version_requirements: &id008 !ruby/object:Gem::Requirement
96
+ none: false
117
97
  requirements:
118
- - - ">="
98
+ - - ~>
119
99
  - !ruby/object:Gem::Version
120
- segments:
121
- - 1
122
- - 1
123
- - 1
124
100
  version: 1.1.1
125
101
  requirement: *id008
126
102
  prerelease: false
@@ -128,13 +104,10 @@ dependencies:
128
104
  - !ruby/object:Gem::Dependency
129
105
  name: arel
130
106
  version_requirements: &id009 !ruby/object:Gem::Requirement
107
+ none: false
131
108
  requirements:
132
- - - ">="
109
+ - - ~>
133
110
  - !ruby/object:Gem::Version
134
- segments:
135
- - 2
136
- - 0
137
- - 7
138
111
  version: 2.0.7
139
112
  requirement: *id009
140
113
  prerelease: false
@@ -142,11 +115,10 @@ dependencies:
142
115
  - !ruby/object:Gem::Dependency
143
116
  name: jdbc-jtds
144
117
  version_requirements: &id010 !ruby/object:Gem::Requirement
118
+ none: false
145
119
  requirements:
146
120
  - - ">="
147
121
  - !ruby/object:Gem::Version
148
- segments:
149
- - 0
150
122
  version: "0"
151
123
  requirement: *id010
152
124
  prerelease: false
@@ -154,14 +126,11 @@ dependencies:
154
126
  - !ruby/object:Gem::Dependency
155
127
  name: minitest
156
128
  version_requirements: &id011 !ruby/object:Gem::Requirement
129
+ none: false
157
130
  requirements:
158
- - - ">="
131
+ - - ~>
159
132
  - !ruby/object:Gem::Version
160
- segments:
161
- - 2
162
- - 0
163
- - 0
164
- version: 2.0.0
133
+ version: 2.2.2
165
134
  requirement: *id011
166
135
  prerelease: false
167
136
  type: :development
@@ -192,28 +161,26 @@ rdoc_options: []
192
161
  require_paths:
193
162
  - lib
194
163
  required_ruby_version: !ruby/object:Gem::Requirement
164
+ none: false
195
165
  requirements:
196
166
  - - ">="
197
167
  - !ruby/object:Gem::Version
168
+ hash: 2
198
169
  segments:
199
170
  - 0
200
171
  version: "0"
201
172
  required_rubygems_version: !ruby/object:Gem::Requirement
173
+ none: false
202
174
  requirements:
203
175
  - - ">="
204
176
  - !ruby/object:Gem::Version
205
- segments:
206
- - 0
207
177
  version: "0"
208
178
  requirements: []
209
179
 
210
180
  rubyforge_project:
211
- rubygems_version: 1.3.6
181
+ rubygems_version: 1.5.1
212
182
  signing_key:
213
183
  specification_version: 3
214
184
  summary: Adds support for limit and offset for Rails 3 and Sybase JDBC driver
215
- test_files:
216
- - test/helper.rb
217
- - test/support/fake_record.rb
218
- - test/test_connection.rb
219
- - test/test_visitor.rb
185
+ test_files: []
186
+
@@ -1,31 +0,0 @@
1
- require 'rubygems'
2
- require 'bundler'
3
-
4
- begin
5
- Bundler.setup(:default, :development)
6
- rescue Bundler::BundlerError => e
7
- $stderr.puts e.message
8
- $stderr.puts "Run `bundle install` to install missing gems"
9
- exit e.status_code
10
- end
11
-
12
-
13
- require 'minitest/autorun'
14
- require 'fileutils'
15
- require 'arel'
16
-
17
-
18
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
19
- $LOAD_PATH.unshift(File.dirname(__FILE__))
20
-
21
- require 'ar-sybase-jdbc-adapter'
22
- require 'support/fake_record'
23
-
24
-
25
- Arel::Table.engine = Arel::Sql::Engine.new(FakeRecord::Base.new)
26
-
27
- class Object
28
- def must_be_like other
29
- gsub(/\s+/, ' ').strip.must_equal other.gsub(/\s+/, ' ').strip
30
- end
31
- end
@@ -1,91 +0,0 @@
1
- module FakeRecord
2
- class Column < Struct.new(:name, :type)
3
- end
4
-
5
- class Connection
6
- attr_reader :tables
7
-
8
- def initialize
9
- @tables = %w{ users photos developers }
10
- @columns = {
11
- 'users' => [
12
- Column.new('id', :integer),
13
- Column.new('name', :string),
14
- Column.new('bool', :boolean),
15
- Column.new('created_at', :date),
16
- ]
17
- }
18
- @primary_keys = {
19
- 'users' => 'id'
20
- }
21
- end
22
-
23
- def primary_key name
24
- @primary_keys[name.to_s]
25
- end
26
-
27
- def table_exists? name
28
- @tables.include? name.to_s
29
- end
30
-
31
- def columns name, message = nil
32
- @columns[name.to_s]
33
- end
34
-
35
- def quote_table_name name
36
- "\"#{name.to_s}\""
37
- end
38
-
39
- def quote_column_name name
40
- "\"#{name.to_s}\""
41
- end
42
-
43
- def quote thing, column = nil
44
- if column && column.type == :integer
45
- return 'NULL' if thing.nil?
46
- return thing.to_i
47
- end
48
-
49
- case thing
50
- when true
51
- "'t'"
52
- when false
53
- "'f'"
54
- when nil
55
- 'NULL'
56
- when Numeric
57
- thing
58
- else
59
- "'#{thing}'"
60
- end
61
- end
62
- end
63
-
64
- class ConnectionPool
65
- class Spec < Struct.new(:config)
66
- end
67
-
68
- attr_reader :spec, :connection
69
-
70
- def initialize
71
- @spec = Spec.new(:adapter => 'america')
72
- @connection = Connection.new
73
- end
74
-
75
- def with_connection
76
- yield connection
77
- end
78
- end
79
-
80
- class Base
81
- attr_accessor :connection_pool
82
-
83
- def initialize
84
- @connection_pool = ConnectionPool.new
85
- end
86
-
87
- def connection
88
- connection_pool.connection
89
- end
90
- end
91
- end
@@ -1,42 +0,0 @@
1
- require 'arjdbc'
2
- require 'helper'
3
-
4
- #module ActiveRecord
5
- # module ConnectionAdapters
6
- # class JdbcAdapter < AbstractAdapter
7
- # def initialize
8
- # end
9
- # end
10
- # end
11
- #end
12
-
13
- module ConnectionTests
14
- class MockConnection
15
- def adapter=(adapt)
16
- end
17
- def jndi_connection?
18
- false
19
- end
20
- end
21
-
22
- describe 'the sybase jtds connection' do
23
- before do
24
- @config = {
25
- :driver => 'net.sourceforge.jtds.Driver',
26
- :url => "jdbc:jtds:sybase://test:1234/database",
27
- :dialect => 'sybase_jtds'
28
- }
29
- @adapter = ActiveRecord::ConnectionAdapters::JdbcAdapter.new MockConnection.new, nil, @config
30
- end
31
-
32
- it "instantiate correct adapter when using 'sybase_jtds' dialect" do
33
- @adapter.must_be_kind_of(::ArJdbc::SybaseJtds)
34
- end
35
-
36
- it "should configure arel2 visitors for SybaseJtds" do
37
- ::Arel::Visitors::VISITORS.must_include('sybase_jtds')
38
- visitor = ::Arel::Visitors::VISITORS['sybase_jtds']
39
- visitor.must_equal(::Arel::Visitors::SybaseJtds)
40
- end
41
- end
42
- end
@@ -1,60 +0,0 @@
1
- require 'helper'
2
- require 'arel/visitors/sybase_jtds'
3
-
4
- module Arel
5
- module Visitors
6
- describe 'the sybase jtds visitor' do
7
- before do
8
- @visitor = SybaseJtds.new Table.engine
9
- end
10
-
11
-
12
- describe Nodes::SelectStatement do
13
- it "should not have 'LIMIT' keyword" do
14
- stmt = Nodes::SelectStatement.new
15
- stmt.cores.first.projections << 'first_field'
16
- stmt.limit = 10
17
- sql = @visitor.accept stmt
18
- sql.wont_match /LIMIT 10/
19
- end
20
-
21
- describe 'limit with no offset and no "DISTINCT"' do
22
- it 'adds a TOP keyword after "SELECT"' do
23
- stmt = Nodes::SelectStatement.new
24
- stmt.cores.first.projections << 'first_field'
25
- stmt.limit = 10
26
- sql = @visitor.accept stmt
27
- sql.must_be_like %{ SELECT TOP 10 'first_field' }
28
- end
29
- end
30
-
31
- describe 'limit with no offset and "DISTINCT"' do
32
- it 'adds a TOP keyword after "DISTINCT"' do
33
- stmt = Nodes::SelectStatement.new
34
- stmt.cores.first.projections << Nodes::SqlLiteral.new('DISTINCT id')
35
- stmt.limit = 10
36
- sql = @visitor.accept stmt
37
- sql.must_be_like %{ SELECT DISTINCT TOP 10 id }
38
- end
39
- end
40
-
41
- #
42
- # describe 'only offset' do
43
- # it 'creates a select from subquery with rownum condition' do
44
- # stmt = Nodes::SelectStatement.new
45
- # stmt.offset = Nodes::Offset.new(10)
46
- # sql = @visitor.accept stmt
47
- # sql.must_be_like %{
48
- # SELECT * FROM (
49
- # SELECT raw_sql_.*, rownum raw_rnum_
50
- # FROM (SELECT ) raw_sql_
51
- # )
52
- # WHERE raw_rnum_ > 10
53
- # }
54
- # end
55
- # end
56
-
57
- end
58
- end
59
- end
60
- end