activerecord-jdbcsqlanywhere-adapter 1.0.0 → 1.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.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@ pkg/*
2
2
  *.gem
3
3
  .bundle
4
4
  *.jar
5
+ Gemfile.lock
6
+ .rvmrc
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source :gemcutter
2
2
 
3
+ gem 'rails', ENV['AR_VERSION'] if ENV['AR_VERSION']
4
+ gem 'activerecord', ENV['AR_VERSION']
5
+
3
6
  # Specify your gem's dependencies in activerecord-sqlanywhere-adapter.gemspec
4
7
  gemspec
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
- activerecord-sqlanywhere-adapter
1
+ activerecord-jdbcsqlanywhere-adapter
2
2
  ===========================
3
3
 
4
- This is an ActiveRecord adapter for Sybase SQLAnywhere.
4
+ This is an ActiveRecord JDBC adapter for Sybase SQLAnywhere.
5
5
 
data/Rakefile CHANGED
@@ -23,6 +23,14 @@ end
23
23
  task :build => [:java_compile, :git_local_check]
24
24
 
25
25
  require 'rake/testtask'
26
+
27
+ # overriding the default rake tests loader
28
+ class Rake::TestTask
29
+ def rake_loader
30
+ 'test/my-minitest-loader.rb'
31
+ end
32
+ end
33
+
26
34
  Rake::TestTask.new(:test) do |test|
27
35
  test.libs << 'lib' << 'test'
28
36
  ar_jdbc = ENV['AR_JDBC'] ||
@@ -1,5 +1,5 @@
1
1
  module ArJdbc
2
2
  module SybaseSQLAnywhere
3
- VERSION = "1.0.0"
3
+ VERSION = "1.0.1"
4
4
  end
5
5
  end
@@ -12,6 +12,152 @@ module ::ArJdbc
12
12
  def init_column(name, default, *args)
13
13
  @name = name.downcase
14
14
  end
15
+
16
+ # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
17
+ def default_value(value)
18
+ # jdbc returns column default strings with actual single quotes around the value.
19
+ return $1 if value =~ /^'(.*)'$/
20
+
21
+ value
22
+ end
23
+
24
+ def simplified_type(field_type)
25
+ case field_type
26
+ when /int|bigint|smallint|tinyint/i then :integer
27
+ when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
28
+ when /float|double|decimal|money|real|smallmoney/i then :decimal
29
+ when /datetime|smalldatetime/i then :datetime
30
+ when /timestamp/i then :timestamp
31
+ when /time/i then :time
32
+ when /date/i then :date
33
+ when /text|ntext|xml/i then :text
34
+ when /binary|image|varbinary/i then :binary
35
+ when /char|nchar|nvarchar|string|varchar/i then (@limit == 1073741823 ? (@limit = nil; :text) : :string)
36
+ when /bit/i then :boolean
37
+ when /uniqueidentifier/i then :string
38
+ end
39
+ end
40
+
41
+ def type_cast(value)
42
+ return nil if value.nil? || value == "(null)" || value == "(NULL)"
43
+ case type
44
+ when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
45
+ when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value=="1"
46
+ else
47
+ super
48
+ end
49
+ end
50
+ end
51
+
52
+ def quote_column_name(name)
53
+ "\"#{name}\""
54
+ end
55
+
56
+ def quoted_true
57
+ quote(1)
58
+ end
59
+
60
+ def quoted_false
61
+ quote(0)
62
+ end
63
+
64
+ def sybaseserver_version
65
+ @sybaseserver_version ||= select_value("select @@version").split('.').map(&:to_i)
66
+ end
67
+
68
+ def last_insert_id
69
+ Integer(select_value("SELECT @@IDENTITY"))
70
+ end
71
+
72
+ def _execute(sql, name = nil)
73
+ result = super
74
+ ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql) ? last_insert_id : result
75
+ end
76
+
77
+ def modify_types(tp) #:nodoc:
78
+ tp[:primary_key] = "NUMERIC(22,0) DEFAULT AUTOINCREMENT PRIMARY KEY"
79
+ tp[:integer][:limit] = nil
80
+ tp[:boolean] = {:name => "bit"}
81
+ tp[:binary] = {:name => "image"}
82
+ tp
83
+ end
84
+
85
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
86
+ limit = nil if %w(text binary).include? type.to_s
87
+ return 'uniqueidentifier' if (type.to_s == 'uniqueidentifier')
88
+ return super unless type.to_s == 'integer'
89
+
90
+ if limit.nil? || limit == 4
91
+ 'int'
92
+ elsif limit == 2
93
+ 'smallint'
94
+ elsif limit == 1
95
+ 'tinyint'
96
+ else
97
+ 'bigint'
98
+ end
99
+ end
100
+
101
+ def add_limit_offset!(sql, options)
102
+ if options[:limit] and options[:offset] and options[:offset] > 0
103
+ sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT\\1 TOP #{options[:limit]} START AT #{options[:offset]+1}")
104
+ elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
105
+ sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
106
+ "SELECT#{$1} TOP #{options[:limit]}"
107
+ end unless options[:limit].nil?
108
+ end
109
+ end
110
+
111
+ def add_column_options!(sql, options) #:nodoc:
112
+ super
113
+ if options[:null] != false
114
+ sql << " NULL"
115
+ end
116
+ end
117
+
118
+ # Adds a new column to the named table.
119
+ # See TableDefinition#column for details of the options you can use.
120
+ def add_column(table_name, column_name, type, options = {})
121
+ add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
122
+ fix_null = false
123
+ if options[:null] == false
124
+ fix_null = true
125
+ options.delete(:null)
126
+ end
127
+ add_column_options!(add_column_sql, options)
128
+ # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
129
+ # add_column_sql << " CONSTRAINT ck__#{table_name}__#{column_name}__date_only CHECK ( CONVERT(CHAR(12), #{quote_column_name(column_name)}, 14)='00:00:00:000' )" if type == :date
130
+ execute(add_column_sql)
131
+ execute("ALTER TABLE #{table_name} MODIFY #{quote_column_name(column_name)} NOT NULL") if fix_null
132
+ end
133
+
134
+ def remove_column(table_name, column_name)
135
+ remove_from_primary_key(table_name, column_name)
136
+ remove_from_index(table_name, column_name)
137
+ remove_check_constraints(table_name, column_name)
138
+ execute "ALTER TABLE #{table_name} DROP #{quote_column_name(column_name)}"
139
+ end
140
+
141
+ def remove_from_index(table_name, column_name)
142
+ end
143
+
144
+ def remove_check_constraints(table_name, column_name)
145
+ end
146
+
147
+ def remove_from_primary_key(table_name, column_name)
148
+ columns = select "select col.cname from SYS.syscolumns col WHERE col.tname = '#{table_name}' AND in_primary_key = 'Y'"
149
+ the_column, columns = columns.partition{|c| c['cname'].casecmp(column_name.to_s) == 0 }
150
+ if the_column.size > 0
151
+ execute "ALTER TABLE #{table_name} DROP PRIMARY KEY"
152
+ if columns.size > 0
153
+ columns.map! {|c| quote_column_name(c['cname'])}
154
+ execute "ALTER TABLE #{table_name} ADD PRIMARY KEY (#{columns.join(', ')})"
155
+ end
156
+ end
157
+ end
158
+
159
+ def remove_index(table_name, options = {})
160
+ execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
15
161
  end
16
162
  end
17
163
  end
@@ -1,9 +1,15 @@
1
1
  class ActiveRecord::Base
2
2
  class << self
3
3
  def sqlanywhere_connection( config )
4
+ version4 = true
5
+ begin
6
+ Java::com.sybase.jdbc4.jdbc.SybDriver
7
+ rescue NameError
8
+ version4 = false
9
+ end
4
10
  config[:port] ||= 2638
5
- config[:url] ||= "jdbc:sybase:Tds:#{config[:server]}:#{config[:port]}?serviceName=#{config[:database]}"
6
- config[:driver] ||= "com.sybase.jdbc3.jdbc.SybDriver"
11
+ config[:url] ||= "jdbc:sybase:Tds:#{config[:host]}:#{config[:port]}?serviceName=#{config[:database]}"
12
+ config[:driver] ||= version4 ? "com.sybase.jdbc4.jdbc.SybDriver" : "com.sybase.jdbc3.jdbc.SybDriver"
7
13
  config[:dialect] = "sqlanywhere"
8
14
  jdbc_connection(config)
9
15
  end
@@ -31,6 +31,7 @@ import java.sql.DatabaseMetaData;
31
31
  import java.sql.ResultSet;
32
32
  import java.sql.SQLException;
33
33
  import java.sql.Statement;
34
+ import java.sql.Connection;
34
35
  import java.sql.Types;
35
36
 
36
37
  import arjdbc.jdbc.RubyJdbcConnection;
@@ -64,18 +65,32 @@ public class SQLAnywhereRubyJdbcConnection extends RubyJdbcConnection {
64
65
  }
65
66
  };
66
67
 
68
+ protected static IRubyObject booleanToRuby(Ruby runtime, ResultSet resultSet, boolean booleanValue)
69
+ throws SQLException {
70
+ if (booleanValue == false && resultSet.wasNull()) return runtime.getNil();
71
+ return runtime.newBoolean(booleanValue);
72
+ }
73
+
67
74
  /**
68
75
  * Treat LONGVARCHAR as CLOB on Informix for purposes of converting a JDBC value to Ruby.
69
76
  */
70
77
  @Override
71
78
  protected IRubyObject jdbcToRuby(Ruby runtime, int column, int type, ResultSet resultSet)
72
79
  throws SQLException {
80
+ if ( Types.BOOLEAN == type || Types.BIT == type ) {
81
+ return booleanToRuby(runtime, resultSet, resultSet.getBoolean(column));
82
+ }
73
83
  if (type == Types.LONGVARCHAR) {
74
84
  type = Types.CLOB;
75
85
  }
76
86
  return super.jdbcToRuby(runtime, column, type, resultSet);
77
87
  }
78
88
 
89
+ @Override
90
+ protected IRubyObject unmarshalKeysOrUpdateCount(ThreadContext context, Connection c, Statement stmt) throws SQLException {
91
+ return context.getRuntime().newFixnum(stmt.getUpdateCount());
92
+ }
93
+
79
94
  @Override
80
95
  protected IRubyObject unmarshalResults(ThreadContext context, DatabaseMetaData metadata,
81
96
  Statement stmt, boolean downCase) throws SQLException {
@@ -1,9 +1,10 @@
1
1
  config = {
2
2
  :username => 'dba',
3
- :password => 'weblog',
3
+ :password => 'sql',
4
4
  :adapter => 'sqlanywhere',
5
5
  :host => ENV[ "SQLANYWHERE_HOST" ] || 'localhost',
6
- :database => ENV[ "SQLANYWHERE_NAMESPACE" ] || 'weblog_development'
6
+ :port => ENV[ "SQLANYWHERE_PORT" ] || nil,
7
+ :database => ENV[ "SQLANYWHERE_NAMESPACE" ] || 'ARTest'
7
8
  }
8
9
 
9
10
  ActiveRecord::Base.establish_connection( config )
@@ -0,0 +1,13 @@
1
+ require 'rake'
2
+
3
+ # Load the test files from the command line.
4
+
5
+ ARGV.each do |f|
6
+ break if f =~ /^-/
7
+
8
+ if f =~ /\*/
9
+ FileList[f].to_a.each { |fn| require File.expand_path(fn) }
10
+ else
11
+ require File.expand_path(f)
12
+ end
13
+ end
@@ -0,0 +1,159 @@
1
+ require 'jdbc_common'
2
+ require 'db/sqlanywhere'
3
+
4
+ ActiveRecord::Schema.verbose = false
5
+
6
+ class CreateLongShips < ActiveRecord::Migration
7
+
8
+ def self.up
9
+ create_table "long_ships", :force => true do |t|
10
+ t.string "name", :limit => 50, :null => false
11
+ t.integer "width", :default => 123
12
+ t.integer "length", :default => 456
13
+ end
14
+ end
15
+
16
+ def self.down
17
+ drop_table "long_ships"
18
+ end
19
+
20
+ end
21
+
22
+ class LongShip < ActiveRecord::Base
23
+ has_many :vikings
24
+ end
25
+
26
+ class CreateVikings < ActiveRecord::Migration
27
+
28
+ def self.up
29
+ create_table "vikings", :force => true do |t|
30
+ t.integer "long_ship_id", :null => false
31
+ t.string "name", :limit => 50, :default => "Sven"
32
+ end
33
+ end
34
+
35
+ def self.down
36
+ drop_table "vikings"
37
+ end
38
+
39
+ end
40
+
41
+ class Viking < ActiveRecord::Base
42
+ belongs_to :long_ship
43
+ end
44
+
45
+
46
+ class CreateNoIdVikings < ActiveRecord::Migration
47
+ def self.up
48
+ create_table "no_id_vikings", :force => true do |t|
49
+ t.string "name", :limit => 50, :default => "Sven"
50
+ end
51
+ remove_column "no_id_vikings", "id"
52
+ end
53
+
54
+ def self.down
55
+ drop_table "no_id_vikings"
56
+ end
57
+ end
58
+
59
+ class NoIdViking < ActiveRecord::Base
60
+ end
61
+
62
+
63
+
64
+ class SQLAnywhereLimitOffsetTest < Test::Unit::TestCase
65
+
66
+ def setup
67
+ CreateLongShips.up
68
+ CreateVikings.up
69
+ CreateNoIdVikings.up
70
+ @connection = ActiveRecord::Base.connection
71
+ end
72
+
73
+ def teardown
74
+ CreateVikings.down
75
+ CreateLongShips.down
76
+ CreateNoIdVikings.down
77
+ ActiveRecord::Base.clear_active_connections!
78
+ end
79
+
80
+ def test_limit_with_no_id_column_available
81
+ NoIdViking.create!(:name => 'Erik')
82
+ assert_nothing_raised(ActiveRecord::StatementInvalid) do
83
+ NoIdViking.find(:first)
84
+ end
85
+ end
86
+
87
+ def test_limit
88
+ %w(one two three four five six seven eight).each do |name|
89
+ LongShip.create!(:name => name)
90
+ end
91
+ ship_names = LongShip.find(:all, :limit => 3).map(&:name)
92
+ assert_equal(%w(one two three), ship_names)
93
+ end
94
+
95
+ def test_limit_and_offset
96
+ return if @connection.sybaseserver_version[0] < 10
97
+
98
+ %w(one two three four five six seven eight).each do |name|
99
+ LongShip.create!(:name => name)
100
+ end
101
+ ship_names = LongShip.find(:all, :offset => 2, :limit => 3).map(&:name)
102
+ assert_equal(%w(three four five), ship_names)
103
+ end
104
+
105
+ def test_limit_with_order
106
+ %w(one two three four five six seven eight).each do |name|
107
+ LongShip.create!(:name => name)
108
+ end
109
+ ship_names = LongShip.find(:all, :order => "name", :limit => 2).map(&:name)
110
+ assert_equal(%w(eight five), ship_names)
111
+ end
112
+
113
+ def test_limit_and_offset_with_order
114
+ return if @connection.sybaseserver_version[0] < 10
115
+
116
+ %w(one two three four five six seven eight).each do |name|
117
+ LongShip.create!(:name => name)
118
+ end
119
+ ship_names = LongShip.find(:all, :order => "name", :offset => 4, :limit => 2).map(&:name)
120
+ assert_equal(%w(seven six), ship_names)
121
+ end
122
+
123
+ # TODO: work out how to fix DISTINCT support without breaking :include
124
+ # def test_limit_and_offset_with_distinct
125
+ # %w(c a b a b a c d c d).each do |name|
126
+ # LongShip.create!(:name => name)
127
+ # end
128
+ # ship_names = LongShip.find(:all, :select => "DISTINCT name", :order => "name", :offset => 1, :limit => 2).map(&:name)
129
+ # assert_equal(%w(b c), ship_names)
130
+ # end
131
+
132
+ def test_limit_and_offset_with_include
133
+ return if @connection.sybaseserver_version[0] < 10
134
+
135
+ skei = LongShip.create!(:name => "Skei")
136
+ skei.vikings.create!(:name => "Bob")
137
+ skei.vikings.create!(:name => "Ben")
138
+ skei.vikings.create!(:name => "Basil")
139
+ ships = Viking.find(:all, :include => :long_ship, :offset => 1, :limit => 2)
140
+ assert_equal(2, ships.size)
141
+ end
142
+
143
+ def test_limit_and_offset_with_include_and_order
144
+ return if @connection.sybaseserver_version[0] < 10
145
+
146
+ boat1 = LongShip.create!(:name => "1-Skei")
147
+ boat2 = LongShip.create!(:name => "2-Skei")
148
+
149
+ boat1.vikings.create!(:name => "Adam")
150
+ boat2.vikings.create!(:name => "Ben")
151
+ boat1.vikings.create!(:name => "Carl")
152
+ boat2.vikings.create!(:name => "Donald")
153
+
154
+ vikings = Viking.find(:all, :include => :long_ship, :order => "long_ships.name, vikings.name", :offset => 0, :limit => 3)
155
+ assert_equal(["Adam", "Carl", "Ben"], vikings.map(&:name))
156
+
157
+ end
158
+
159
+ end
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-jdbcsqlanywhere-adapter
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 1
7
- - 0
8
- - 0
9
- version: 1.0.0
4
+ prerelease:
5
+ version: 1.0.1
10
6
  platform: ruby
11
7
  authors:
12
8
  - Brian Olsen
@@ -14,20 +10,16 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2011-05-09 00:00:00 +02:00
18
- default_executable:
13
+ date: 2011-10-17 00:00:00 Z
19
14
  dependencies:
20
15
  - !ruby/object:Gem::Dependency
21
16
  name: activerecord-jdbc-adapter
22
17
  prerelease: false
23
18
  requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
24
20
  requirements:
25
21
  - - ~>
26
22
  - !ruby/object:Gem::Version
27
- segments:
28
- - 1
29
- - 1
30
- - 1
31
23
  version: 1.1.1
32
24
  type: :runtime
33
25
  version_requirements: *id001
@@ -35,13 +27,10 @@ dependencies:
35
27
  name: bundler
36
28
  prerelease: false
37
29
  requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
38
31
  requirements:
39
32
  - - ">="
40
33
  - !ruby/object:Gem::Version
41
- segments:
42
- - 1
43
- - 0
44
- - 0
45
34
  version: 1.0.0
46
35
  type: :development
47
36
  version_requirements: *id002
@@ -70,9 +59,10 @@ files:
70
59
  - src/java/arjdbc/sqlanywhere/AdapterJavaService.java
71
60
  - src/java/arjdbc/sqlanywhere/SQLAnywhereRubyJdbcConnection.java
72
61
  - test/db/sqlanywhere.rb
62
+ - test/my-minitest-loader.rb
63
+ - test/sqlanywhere_limit_offset_test.rb
73
64
  - test/sqlanywhere_simple_test.rb
74
65
  - lib/arjdbc/sqlanywhere/adapter_java.jar
75
- has_rdoc: true
76
66
  homepage: http://github.com/griff/activerecord-jdbcsqlanywhere-adapter
77
67
  licenses: []
78
68
 
@@ -82,25 +72,21 @@ rdoc_options: []
82
72
  require_paths:
83
73
  - lib
84
74
  required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
85
76
  requirements:
86
77
  - - ">="
87
78
  - !ruby/object:Gem::Version
88
- segments:
89
- - 0
90
79
  version: "0"
91
80
  required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
92
82
  requirements:
93
83
  - - ">="
94
84
  - !ruby/object:Gem::Version
95
- segments:
96
- - 1
97
- - 3
98
- - 6
99
85
  version: 1.3.6
100
86
  requirements: []
101
87
 
102
88
  rubyforge_project:
103
- rubygems_version: 1.3.6
89
+ rubygems_version: 1.8.10
104
90
  signing_key:
105
91
  specification_version: 3
106
92
  summary: Sybase SQLAnywhere JDBC adapter for JRuby on Rails