From 534cd75c682f7e1294a3d0fddc1f09e7dd0ec63a Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Tue, 22 May 2012 10:51:59 +0100 Subject: [PATCH 01/47] Have strings imported as UTF-8 Imported https://github.com/savitri02/activerecord-sqlanywhere-adapter/commit/066b348f1138d3334fb47fa0f7fbcf3132b9afa7 I'm not too familiar with git, so I'm not sure if I followed the correct patch import protocol here. --- lib/active_record/connection_adapters/sqlanywhere_adapter.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 667f0ce..11e52f1 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -747,6 +747,9 @@ def native_type_to_ruby_type(native_type, value) case native_type when 484 # DT_DECIMAL (also and more importantly numeric) BigDecimal.new(value) + when 448,452,456,460,640 # DT_VARCHAR, DT_FIXCHAR, DT_LONGVARCHAR, DT_STRING, DT_LONGNVARCHAR + # hack, not sure how to manage proper encoding + value.force_encoding("UTF-8") else value end From 8b7acc15e6c3adc392e0f93a0e9ed6540df6e6de Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 30 May 2012 14:04:36 +0100 Subject: [PATCH 02/47] Apply distinct when distinct is set and no limit or offset is set. If there was ever a good reason to require limit and offset before we do distinct, someone should let me know. --- lib/arel/visitors/sqlanywhere.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlanywhere.rb b/lib/arel/visitors/sqlanywhere.rb index 0496327..31485de 100755 --- a/lib/arel/visitors/sqlanywhere.rb +++ b/lib/arel/visitors/sqlanywhere.rb @@ -13,7 +13,7 @@ def visit_Arel_Nodes_SelectStatement o [ "SELECT", - ("DISTINCT" if (o.limit || o.offset) && is_distinct), + ("DISTINCT" if is_distinct), ("TOP #{o.limit}" if o.limit), (visit_Arel_Nodes_Offset(o.offset) if o.offset), o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join, From 25b775e18a8452c87b57c6e60b3962a49473a554 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Tue, 17 Jul 2012 15:04:18 +0100 Subject: [PATCH 03/47] Don't add views to the schema.rb file. Don't drop views when running db:test:purge. --- .../connection_adapters/sqlanywhere.rake | 10 ++++++++++ .../connection_adapters/sqlanywhere_adapter.rb | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere.rake b/lib/active_record/connection_adapters/sqlanywhere.rake index 95befe9..17f1569 100755 --- a/lib/active_record/connection_adapters/sqlanywhere.rake +++ b/lib/active_record/connection_adapters/sqlanywhere.rake @@ -45,4 +45,14 @@ namespace :db do end end end + + namespace :schema do + redefine_task :dump => :environment do |existing_actions| + if ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'sqlanywhere' + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Rails.env]) + ActiveRecord::SchemaDumper.ignore_tables = ActiveRecord::Base.connection.viewed_tables + end + Array(existing_actions).each{|action| action.call} + end + end end \ No newline at end of file diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 11e52f1..3136076 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -408,6 +408,16 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: super(type, limit, precision, scale) end end + + def viewed_tables(name = nil) + sql = "SELECT table_name FROM SYS.SYSTABLE WHERE table_type='view' and creator NOT IN (0,3,5)" + select(sql, name).map { |row| row["table_name"] } + end + + def base_tables(name = nil) + sql = "SELECT table_name FROM SYS.SYSTABLE WHERE table_type='base' and creator NOT IN (0,3,5)" + select(sql, name).map { |row| row["table_name"] } + end # Do not return SYS-owned or DBO-owned tables or RS_systabgroup-owned def tables(name = nil) #:nodoc: @@ -497,8 +507,8 @@ def remove_column(table_name, *column_names) def purge_database - tables.each do |table_name| - drop_table(table_name) + base_tables.each do |base_table_name| + drop_table(base_table_name) end end From efb8b6511c75d8ba64a2771163ee7202cc3b908b Mon Sep 17 00:00:00 2001 From: leander Date: Tue, 17 Jul 2012 15:57:35 +0100 Subject: [PATCH 04/47] lje - changes to handle the Sybase XML and uniqueindentifier datatypes, XML -> text, uniqueindetifier -> integer Current patch to fix issue where all binaries are going in as long binary(limit), path to just dump as binary(limit) untill a perminant fix is created. --- lib/active_record/connection_adapters/sqlanywhere_adapter.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 3136076..d02a4f8 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -96,6 +96,8 @@ def simplified_type(field_type) return :binary if field_type =~ /long binary/i return :datetime if field_type =~ /timestamp/i return :integer if field_type =~ /smallint|bigint/i + return :text if field_type =~ /xml/i + return :integer if field_type =~ /uniqueidentifier/i super end @@ -204,7 +206,7 @@ def native_database_types #:nodoc: :timestamp => { :name => "datetime" }, :time => { :name => "time" }, :date => { :name => "date" }, - :binary => { :name => "long binary" }, + :binary => { :name => "binary" }, :boolean => { :name => "tinyint", :limit => 1} } end From 0a16224c6f44c226aa602bb0557452670fff5a7f Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Tue, 17 Jul 2012 16:22:01 +0100 Subject: [PATCH 05/47] Refactor the list of tables. --- .../connection_adapters/sqlanywhere_adapter.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 3136076..2485b47 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -410,19 +410,16 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: end def viewed_tables(name = nil) - sql = "SELECT table_name FROM SYS.SYSTABLE WHERE table_type='view' and creator NOT IN (0,3,5)" - select(sql, name).map { |row| row["table_name"] } + list_of_tables(['view'], name) end def base_tables(name = nil) - sql = "SELECT table_name FROM SYS.SYSTABLE WHERE table_type='base' and creator NOT IN (0,3,5)" - select(sql, name).map { |row| row["table_name"] } + list_of_tables(['base'], name) end # Do not return SYS-owned or DBO-owned tables or RS_systabgroup-owned def tables(name = nil) #:nodoc: - sql = "SELECT table_name FROM SYS.SYSTABLE WHERE creator NOT IN (0,3,5)" - select(sql, name).map { |row| row["table_name"] } + list_of_tables(['base', 'view']) end def columns(table_name, name = nil) #:nodoc: @@ -513,6 +510,12 @@ def purge_database end protected + + def list_of_tables(types, name = nil) + sql = "SELECT table_name FROM SYS.SYSTABLE WHERE table_type in (#{types.map{|t| quote(t)}.join(', ')}) and creator NOT IN (0,3,5)" + select(sql, name).map { |row| row["table_name"] } + end + def select(sql, name = nil, binds = []) #:nodoc: exec_query(sql, name, binds).to_a end From e17c5afec0b1ce9680e1b3d746e1facb01b28820 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 30 Jul 2012 17:17:02 +0100 Subject: [PATCH 06/47] Make select a public method --- .../connection_adapters/sqlanywhere_adapter.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 1801ee0..1b2cd68 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -511,6 +511,10 @@ def purge_database end end + def select(sql, name = nil, binds = []) #:nodoc: + exec_query(sql, name, binds).to_a + end + protected def list_of_tables(types, name = nil) @@ -518,10 +522,6 @@ def list_of_tables(types, name = nil) select(sql, name).map { |row| row["table_name"] } end - def select(sql, name = nil, binds = []) #:nodoc: - exec_query(sql, name, binds).to_a - end - # ActiveRecord uses the OFFSET/LIMIT keywords at the end of query to limit the number of items in the result set. # This syntax is NOT supported by SQL Anywhere. In previous versions of this adapter this adapter simply # overrode the add_limit_offset function and added the appropriate TOP/START AT keywords to the start of the query. From a8e5daea1f412b22ebee59f3491cc95ae9e2f274 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Tue, 11 Sep 2012 11:09:39 +0100 Subject: [PATCH 07/47] Remove the whitespace from the ends of all strings. Why am I removing the whitespace from the end of the string? Sqlanywhere allowed us to create a string foreign key. Somehow on only one end of the foreign key, the values got spaces at the end. The foreign key was still valid in Sqlanywhere: It worked for joins and it worked for referencing constraints. It however does not work for the ActiveRecord includes method. Rails will bring back the associated records, but then it fails to pair the records correctly. Removing whitespace from the ends of all strings fixes this. It is a hack however, so I'm open for suggestions on coming up with a better method. --- .../connection_adapters/sqlanywhere_adapter.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 1b2cd68..2e8fb15 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -764,7 +764,19 @@ def native_type_to_ruby_type(native_type, value) BigDecimal.new(value) when 448,452,456,460,640 # DT_VARCHAR, DT_FIXCHAR, DT_LONGVARCHAR, DT_STRING, DT_LONGNVARCHAR # hack, not sure how to manage proper encoding - value.force_encoding("UTF-8") + value = value.force_encoding("UTF-8") + # Why am I removing the whitespace from the end of the string? + # + # Sqlanywhere allowed us to create a string foreign key. + # Somehow on only one end of the foreign key, the values got spaces at the end. + # The foreign key was still valid in Sqlanywhere: It worked for joins and it worked for referencing constraints. + # + # It however does not work for the ActiveRecord includes method. + # Rails will bring back the associated records, but then it fails to pair the records correctly. + # Removing whitespace from the ends of all strings fixes this. It is a hack however, so I'm open + # for suggestions on coming up with a better method. + # + value = value.gsub(/ +$/, "") else value end From 6cf5b398bf1856ea4e12980c4f768488d29f33e3 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 13 Sep 2012 15:50:40 +0100 Subject: [PATCH 08/47] Translate Arel::Nodes::True to 1=1 and the same for False --- lib/arel/visitors/sqlanywhere.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/arel/visitors/sqlanywhere.rb b/lib/arel/visitors/sqlanywhere.rb index 31485de..ad85298 100755 --- a/lib/arel/visitors/sqlanywhere.rb +++ b/lib/arel/visitors/sqlanywhere.rb @@ -38,6 +38,14 @@ def visit_Arel_Nodes_SelectCore o def visit_Arel_Nodes_Offset o "START AT #{visit(o.expr) + 1}" end + + def visit_Arel_Nodes_True o + "1=1" + end + + def visit_Arel_Nodes_False o + "1=0" + end def using_distinct?(o) From 4c66cd8044521628a68a008676c85baef5d95700 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Tue, 2 Oct 2012 10:45:29 +0100 Subject: [PATCH 09/47] Override Arel's node matches code, so that I can do integer column like '%01%' or something. --- lib/arel/visitors/sqlanywhere.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/arel/visitors/sqlanywhere.rb b/lib/arel/visitors/sqlanywhere.rb index ad85298..2bcfe06 100755 --- a/lib/arel/visitors/sqlanywhere.rb +++ b/lib/arel/visitors/sqlanywhere.rb @@ -46,6 +46,15 @@ def visit_Arel_Nodes_True o def visit_Arel_Nodes_False o "1=0" end + + def visit_Arel_Nodes_Matches o + # The version in arel cannot like integer columns + left = visit o.left # This method sets last column + # If last column was left, visit o.right would return 0 + self.last_column = nil + "#{left} LIKE #{visit o.right}" + end + def using_distinct?(o) From 9ba2c7a54288cddb1f33c24732f5512e8203c4bf Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 4 Oct 2012 11:25:40 +0100 Subject: [PATCH 10/47] Take text encoding from the database connection configuration --- lib/active_record/connection_adapters/sqlanywhere_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 2e8fb15..cdc86fb 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -764,7 +764,7 @@ def native_type_to_ruby_type(native_type, value) BigDecimal.new(value) when 448,452,456,460,640 # DT_VARCHAR, DT_FIXCHAR, DT_LONGVARCHAR, DT_STRING, DT_LONGNVARCHAR # hack, not sure how to manage proper encoding - value = value.force_encoding("UTF-8") + value = value.force_encoding(ActiveRecord::Base.connection_config['encoding'] || "UTF-8") # Why am I removing the whitespace from the end of the string? # # Sqlanywhere allowed us to create a string foreign key. From f397caecf241d9f73fe6b7cf7bc5ecd865ad6b52 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Fri, 12 Oct 2012 11:54:39 +0100 Subject: [PATCH 11/47] Fix the UTF-8 argument error problem on windows. --- lib/active_record/connection_adapters/sqlanywhere_adapter.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index cdc86fb..422fd5a 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -776,7 +776,10 @@ def native_type_to_ruby_type(native_type, value) # Removing whitespace from the ends of all strings fixes this. It is a hack however, so I'm open # for suggestions on coming up with a better method. # - value = value.gsub(/ +$/, "") + begin + value = value.rstrip + rescue ArgumentError # invalid byte sequence in UTF-8 + end else value end From 29647d68238efa2546e4a033a71891b4f0b9bbb0 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 5 Nov 2012 17:24:33 +0000 Subject: [PATCH 12/47] Check that the table belongs to dba. Check that the table belongs to dba when getting the table_structure. If 2 users had the same table name, we used to get the columns for both. --- lib/active_record/connection_adapters/sqlanywhere_adapter.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 422fd5a..50e2004 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -1,3 +1,4 @@ +#encoding: utf-8 #==================================================== # # Copyright 2008-2010 iAnywhere Solutions, Inc. @@ -613,6 +614,7 @@ def table_structure(table_name) INNER JOIN SYS.SYSTABLE ON SYS.SYSCOLUMN.table_id = SYS.SYSTABLE.table_id INNER JOIN SYS.SYSDOMAIN ON SYS.SYSCOLUMN.domain_id = SYS.SYSDOMAIN.domain_id WHERE + SYS.SYSTABLE.creator = 1 AND table_name = '#{table_name}' SQL structure = exec_query(sql, :skip_logging) @@ -764,7 +766,8 @@ def native_type_to_ruby_type(native_type, value) BigDecimal.new(value) when 448,452,456,460,640 # DT_VARCHAR, DT_FIXCHAR, DT_LONGVARCHAR, DT_STRING, DT_LONGNVARCHAR # hack, not sure how to manage proper encoding - value = value.force_encoding(ActiveRecord::Base.connection_config['encoding'] || "UTF-8") + value = value.force_encoding(ActiveRecord::Base.connection_config['encoding'] || 'UTF-8') + value = value.encode('UTF-8') # Why am I removing the whitespace from the end of the string? # # Sqlanywhere allowed us to create a string foreign key. From 322301b3500a7dce22e4fdff31955063cdd3e1a3 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 8 Nov 2012 14:47:06 +0000 Subject: [PATCH 13/47] Remove aliases from group by clauses. Sqlanywhere doesn't like groups being aliased. --- lib/arel/visitors/sqlanywhere.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/arel/visitors/sqlanywhere.rb b/lib/arel/visitors/sqlanywhere.rb index 2bcfe06..02979d1 100755 --- a/lib/arel/visitors/sqlanywhere.rb +++ b/lib/arel/visitors/sqlanywhere.rb @@ -34,6 +34,11 @@ def visit_Arel_Nodes_SelectCore o ].compact.join ' ' end + def visit_Arel_Nodes_Group o + expr = o.expr.clone + expr.alias = nil + visit expr + end def visit_Arel_Nodes_Offset o "START AT #{visit(o.expr) + 1}" From d7568a7f0e5df80a08d75a130ae5c6a5215c78e8 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 8 Nov 2012 15:00:51 +0000 Subject: [PATCH 14/47] Fixed issue with group by made by previous commit. --- lib/arel/visitors/sqlanywhere.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlanywhere.rb b/lib/arel/visitors/sqlanywhere.rb index 02979d1..83dd301 100755 --- a/lib/arel/visitors/sqlanywhere.rb +++ b/lib/arel/visitors/sqlanywhere.rb @@ -36,7 +36,9 @@ def visit_Arel_Nodes_SelectCore o def visit_Arel_Nodes_Group o expr = o.expr.clone - expr.alias = nil + if expr.class == Arel::Nodes::NamedFunction + expr.alias = nil + end visit expr end From 7c6d9d8c120bb20e3c55aedfe134742eaef67fb6 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 17 Jan 2013 09:38:47 +0000 Subject: [PATCH 15/47] Switch to using the new ffi backed sqlanywhere gem --- Gemfile | 4 ++ Rakefile | 122 ------------------------------------------------------- 2 files changed, 4 insertions(+), 122 deletions(-) create mode 100755 Gemfile delete mode 100644 Rakefile diff --git a/Gemfile b/Gemfile new file mode 100755 index 0000000..8a71cd2 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'http://rubygems.org' + +gem 'sqlanywhere', git: 'git://github.com/in4systems/sqlanywhere.git', branch: 'ffi_port_no_malloc' +gem 'activerecord', '>= 3.0.3' diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 81553ae..0000000 --- a/Rakefile +++ /dev/null @@ -1,122 +0,0 @@ -#==================================================== -# -# Copyright 2008-2010 iAnywhere Solutions, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# -# See the License for the specific language governing permissions and -# limitations under the License. -# -# While not a requirement of the license, if you do modify this file, we -# would appreciate hearing about it. Please email sqlany_interfaces@sybase.com -# -# -#==================================================== - -require 'rake/clean' -require 'rake/rdoctask' -require 'rubygems' -require 'rubygems/builder' - -PACKAGE_NAME = "activerecord-sqlanywhere-adapter" -ARCH=Config::CONFIG['arch'] - -pkg_version = "1.0.0" - -spec = Gem::Specification.new do |spec| - spec.authors = ["Eric Farrar"] - spec.email = 'eric.farrar@ianywhere.com' - spec.name = 'activerecord-sqlanywhere-adapter' - spec.summary = 'ActiveRecord driver for SQL Anywhere' - spec.description = <<-EOF - ActiveRecord driver for SQL Anywhere - EOF - spec.version = pkg_version - spec.has_rdoc = true - spec.rubyforge_project = 'sqlanywhere' - spec.homepage = 'http://sqlanywhere.rubyforge.org' - spec.files = Dir['lib/**/*.rb'] + Dir['test/**/*'] - spec.required_ruby_version = '>= 1.9.2' - spec.require_paths = ['lib'] - spec.add_dependency('sqlanywhere', '>= 0.1.5') - spec.add_dependency('activerecord', '>= 3.0.3') - spec.rdoc_options << '--title' << 'ActiveRecord Driver for SQL Anywhere' << - '--main' << 'README' << - '--line-numbers' - spec.extra_rdoc_files = ['README', 'CHANGELOG', 'LICENSE'] -end - -desc "Build the gem" -task :gem => ["activerecord-sqlanywhere-adapter-#{pkg_version}.gem"] do -end - -file "activerecord-sqlanywhere-adapter-#{pkg_version}.gem" => ['lib/active_record/connection_adapters/sqlanywhere_adapter.rb', 'README', 'Rakefile', 'lib/arel/visitors/sqlanywhere.rb'] do - Gem::Builder.new(spec).build -end - - -desc "Install the gem" -task :install => ["activerecord-sqlanywhere-adapter-#{pkg_version}.gem"] do - sh "gem install activerecord-sqlanywhere-adapter-#{pkg_version}.gem" -end - -desc "Build distributables (src zip, src tar.gz, gem)" -task :dist do |t| - puts "Cleaning Build Environment..." - Rake.application['clobber'].invoke - system "rake clobber" - - files = Dir.glob('*') - - puts "Creating #{File.join('build', PACKAGE_NAME)}-#{pkg_version} directory..." - FileUtils.mkdir_p "#{File.join('build', PACKAGE_NAME)}-#{pkg_version}" - - puts "Copying files to #{File.join('build', PACKAGE_NAME)}-#{pkg_version}..." - FileUtils.cp_r files, "#{File.join('build', PACKAGE_NAME)}-#{pkg_version}" - - if( ARCH =~ /win32/ ) then - system "attrib -R #{File.join('build', PACKAGE_NAME)}-#{pkg_version} /S" - else - system "find #{File.join('build', PACKAGE_NAME)}-#{pkg_version} -type d -exec chmod 755 {} \\;" - system "find #{File.join('build', PACKAGE_NAME)}-#{pkg_version} -type f -exec chmod 644 {} \\;" - end - - if( ARCH =~ /win32/ ) then - puts "Creating #{File.join('build', PACKAGE_NAME)}-#{pkg_version}.zip..." - system "cd build && zip -q -r #{PACKAGE_NAME}-#{pkg_version}.zip #{PACKAGE_NAME}-#{pkg_version}" - else - puts "Creating #{File.join('build', PACKAGE_NAME)}-#{pkg_version}.tar..." - system "tar cf #{File.join('build', PACKAGE_NAME)}-#{pkg_version}.tar -C build #{PACKAGE_NAME}-#{pkg_version}" - - puts "GZipping to create #{File.join('build', PACKAGE_NAME, PACKAGE_NAME)}-#{pkg_version}.tar.gz..." - system "gzip #{File.join('build', PACKAGE_NAME)}-#{pkg_version}.tar" - end - - puts "Building GEM distributable..." - Rake.application['gem'].invoke - - puts "Copying GEM to #{File.join('build', PACKAGE_NAME)}-#{pkg_version}.gem..." - FileUtils.cp "#{PACKAGE_NAME}-#{pkg_version}.gem", "build" -end - -Rake::RDocTask.new do |rd| - rd.title = "ActiveRecord Driver for SQL Anywhere" - rd.main = "README" - rd.rdoc_files.include('README', 'CHANGELOG', 'LICENSE', 'lib/**/*.rb') -end - -desc "Publish the RDOCs on RubyForge" -task :publish_rdoc => ["html/index.html"] do - system "pscp -r html/* efarrar@rubyforge.org:/var/www/gforge-projects/sqlanywhere/activerecord-sqlanywhere-adapter" -end - -CLOBBER.include("activerecord-sqlanywhere-adapter-#{pkg_version}.gem", "build/**/*", "build") From f1c954c5b7c0f19c7d169dce54f0fe0840018f38 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 17 Jan 2013 11:09:12 +0000 Subject: [PATCH 16/47] Update the adapter to use the new symbols --- .../connection_adapters/sqlanywhere_adapter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index 50e2004..fa274ec 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -663,7 +663,7 @@ def exec_query(sql, name = 'SQL', binds = []) result, bind_param = SA.instance.api.sqlany_describe_bind_param(stmt, i) sqlanywhere_error_test(sql) if result==0 - bind_param.set_direction(1) # https://github.com/sqlanywhere/sqlanywhere/blob/master/ext/sacapi.h#L175 + bind_param.set_direction(:input) if bind_value.nil? bind_param.set_value(nil) elsif bind_type == :datetime @@ -762,9 +762,9 @@ def query(sql) def native_type_to_ruby_type(native_type, value) return nil if value.nil? case native_type - when 484 # DT_DECIMAL (also and more importantly numeric) + when :decimal # (also and more importantly numeric) BigDecimal.new(value) - when 448,452,456,460,640 # DT_VARCHAR, DT_FIXCHAR, DT_LONGVARCHAR, DT_STRING, DT_LONGNVARCHAR + when :var_char, :fix_char, :long_var_char, :string, :long_n_var_char # hack, not sure how to manage proper encoding value = value.force_encoding(ActiveRecord::Base.connection_config['encoding'] || 'UTF-8') value = value.encode('UTF-8') From 5e066d7c35da4fd0ee22e051d4b0c91807b10f49 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 17 Jan 2013 12:17:43 +0000 Subject: [PATCH 17/47] Delete gemspec and update gemfile --- Gemfile | 3 +- activerecord-sqlanywhere-adapter.gemspec | 42 ------------------------ 2 files changed, 2 insertions(+), 43 deletions(-) delete mode 100755 activerecord-sqlanywhere-adapter.gemspec diff --git a/Gemfile b/Gemfile index 8a71cd2..fac96ea 100755 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,5 @@ source 'http://rubygems.org' -gem 'sqlanywhere', git: 'git://github.com/in4systems/sqlanywhere.git', branch: 'ffi_port_no_malloc' +gem 'sqlanywhere', git: 'git://github.com/in4systems/sqlanywhere.git', branch: 'ffi_port' gem 'activerecord', '>= 3.0.3' + diff --git a/activerecord-sqlanywhere-adapter.gemspec b/activerecord-sqlanywhere-adapter.gemspec deleted file mode 100755 index b358617..0000000 --- a/activerecord-sqlanywhere-adapter.gemspec +++ /dev/null @@ -1,42 +0,0 @@ -Gem::Specification.new do |s| - s.name = %q{activerecord-sqlanywhere-adapter} - s.version = "1.0.1" - - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = [%q{Eric Farar}] - s.description = %q{ActiveRecord driver for SQL Anywhere} - s.email = %q{eric.farrar@ianywhere.com} - s.files = [ - "CHANGELOG", - "LICENSE", - "README", - "Rakefile", - "test/connection.rb", - "lib/active_record/connection_adapters/sqlanywhere_adapter.rb", - "lib/arel/visitors/sqlanywhere.rb", - "lib/active_record/connection_adapters/sqlanywhere.rake", - "lib/activerecord-sqlanywhere-adapter.rb" - - ] - s.homepage = %q{http://sqlanywhere.rubyforge.org} - s.licenses = [%q{Apache License Version 2.0}] - s.require_paths = [%q{lib}] - s.rubygems_version = %q{>= 1.8.8} - s.summary = %q{ActiveRecord driver for SQL Anywhere} - - if s.respond_to? :specification_version then - s.specification_version = 3 - - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 0.1.5"]) - s.add_runtime_dependency(%q, [">= 3.0.3"]) - else - s.add_dependency(%q, [">= 0.1.5"]) - s.add_dependency(%q, [">= 3.0.3"]) - end - else - s.add_dependency(%q, [">= 0.1.5"]) - s.add_dependency(%q, [">= 3.0.3"]) - end -end - From d20cdd2494a3afd96e0407b1bf796baebcf946a5 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 17 Jan 2013 13:53:07 +0000 Subject: [PATCH 18/47] Readd Gemspec This reverts commit 5e066d7c35da4fd0ee22e051d4b0c91807b10f49. --- activerecord-sqlanywhere-adapter.gemspec | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 activerecord-sqlanywhere-adapter.gemspec diff --git a/activerecord-sqlanywhere-adapter.gemspec b/activerecord-sqlanywhere-adapter.gemspec new file mode 100755 index 0000000..f532957 --- /dev/null +++ b/activerecord-sqlanywhere-adapter.gemspec @@ -0,0 +1,26 @@ +Gem::Specification.new do |s| + s.name = %q{activerecord-sqlanywhere-adapter} + s.version = "1.0.1" + + s.authors = [%q{Eric Farar}] + s.description = %q{ActiveRecord driver for SQL Anywhere} + s.email = %q{eric.farrar@ianywhere.com} + s.files = [ + "CHANGELOG", + "LICENSE", + "README", + "test/connection.rb", + "lib/active_record/connection_adapters/sqlanywhere_adapter.rb", + "lib/arel/visitors/sqlanywhere.rb", + "lib/active_record/connection_adapters/sqlanywhere.rake", + "lib/activerecord-sqlanywhere-adapter.rb" + + ] + s.homepage = %q{http://sqlanywhere.rubyforge.org} + s.licenses = [%q{Apache License Version 2.0}] + s.require_paths = [%q{lib}] + s.summary = %q{ActiveRecord driver for SQL Anywhere} + s.platform = Gem::Platform::RUBY + +end + From 7a59e00b360066af5841481d6db4e2d4d4690c43 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 21 Jan 2013 14:43:43 +0000 Subject: [PATCH 19/47] Make a distinct gem --- ...erecord-sqlanywhere-adapter-in4systems.gemspec | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) rename activerecord-sqlanywhere-adapter.gemspec => activerecord-sqlanywhere-adapter-in4systems.gemspec (57%) diff --git a/activerecord-sqlanywhere-adapter.gemspec b/activerecord-sqlanywhere-adapter-in4systems.gemspec similarity index 57% rename from activerecord-sqlanywhere-adapter.gemspec rename to activerecord-sqlanywhere-adapter-in4systems.gemspec index f532957..63a0fd5 100755 --- a/activerecord-sqlanywhere-adapter.gemspec +++ b/activerecord-sqlanywhere-adapter-in4systems.gemspec @@ -1,9 +1,9 @@ Gem::Specification.new do |s| - s.name = %q{activerecord-sqlanywhere-adapter} + s.name = %q{activerecord-sqlanywhere-adapter-in4systems} s.version = "1.0.1" - s.authors = [%q{Eric Farar}] - s.description = %q{ActiveRecord driver for SQL Anywhere} + s.authors = ['Eric Farar', 'Chris Couzens'] + s.description = %q{ActiveRecord driver for SQL Anywhere customized for in4systems} s.email = %q{eric.farrar@ianywhere.com} s.files = [ "CHANGELOG", @@ -14,13 +14,16 @@ Gem::Specification.new do |s| "lib/arel/visitors/sqlanywhere.rb", "lib/active_record/connection_adapters/sqlanywhere.rake", "lib/activerecord-sqlanywhere-adapter.rb" - ] - s.homepage = %q{http://sqlanywhere.rubyforge.org} + s.executables = [] + s.test_files = [] + s.has_rdoc = false + s.homepage = 'https://github.com/in4systems/activerecord-sqlanywhere-adapter' s.licenses = [%q{Apache License Version 2.0}] s.require_paths = [%q{lib}] s.summary = %q{ActiveRecord driver for SQL Anywhere} s.platform = Gem::Platform::RUBY - + s.add_dependency 'sqlanywhere-ffi', '>=1.0.0' + s.add_dependency 'activerecord', '>= 3.0.3' end From 3acd4ae784c1df390884c12f2757d3e20ae63b4b Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 21 Jan 2013 14:56:11 +0000 Subject: [PATCH 20/47] Remove the gemfile --- Gemfile | 5 ----- 1 file changed, 5 deletions(-) delete mode 100755 Gemfile diff --git a/Gemfile b/Gemfile deleted file mode 100755 index fac96ea..0000000 --- a/Gemfile +++ /dev/null @@ -1,5 +0,0 @@ -source 'http://rubygems.org' - -gem 'sqlanywhere', git: 'git://github.com/in4systems/sqlanywhere.git', branch: 'ffi_port' -gem 'activerecord', '>= 3.0.3' - From be0a493ce637ce0119f5fb072403636c8ff5de4e Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 21 Jan 2013 15:02:06 +0000 Subject: [PATCH 21/47] Bumped version --- activerecord-sqlanywhere-adapter-in4systems.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlanywhere-adapter-in4systems.gemspec b/activerecord-sqlanywhere-adapter-in4systems.gemspec index 63a0fd5..81cc3ae 100755 --- a/activerecord-sqlanywhere-adapter-in4systems.gemspec +++ b/activerecord-sqlanywhere-adapter-in4systems.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-adapter-in4systems} - s.version = "1.0.1" + s.version = "1.0.2" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord driver for SQL Anywhere customized for in4systems} From c720810ec76637fd8c7a7599906a9ea421d796ff Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 30 Jan 2013 09:47:50 +0000 Subject: [PATCH 22/47] Restore the original gem name --- activerecord-sqlanywhere-adapter.gemspec | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 activerecord-sqlanywhere-adapter.gemspec diff --git a/activerecord-sqlanywhere-adapter.gemspec b/activerecord-sqlanywhere-adapter.gemspec new file mode 100755 index 0000000..742f388 --- /dev/null +++ b/activerecord-sqlanywhere-adapter.gemspec @@ -0,0 +1,29 @@ +Gem::Specification.new do |s| + s.name = %q{activerecord-sqlanywhere-adapter} + s.version = "1.0.2" + + s.authors = ['Eric Farar', 'Chris Couzens'] + s.description = %q{ActiveRecord driver for SQL Anywhere customized for in4systems} + s.email = %q{eric.farrar@ianywhere.com} + s.files = [ + "CHANGELOG", + "LICENSE", + "README", + "test/connection.rb", + "lib/active_record/connection_adapters/sqlanywhere_adapter.rb", + "lib/arel/visitors/sqlanywhere.rb", + "lib/active_record/connection_adapters/sqlanywhere.rake", + "lib/activerecord-sqlanywhere-adapter.rb" + ] + s.executables = [] + s.test_files = [] + s.has_rdoc = false + s.homepage = 'https://github.com/in4systems/activerecord-sqlanywhere-adapter' + s.licenses = [%q{Apache License Version 2.0}] + s.require_paths = [%q{lib}] + s.summary = %q{ActiveRecord driver for SQL Anywhere} + s.platform = Gem::Platform::RUBY + s.add_dependency 'sqlanywhere', '>=1.0.0' + s.add_dependency 'activerecord', '>= 3.0.3' +end + From be3e2243d511c21efdf42ee5c45eeb58e56fbdc7 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 30 Jan 2013 09:49:54 +0000 Subject: [PATCH 23/47] Fix version of sqlanywhere gem --- activerecord-sqlanywhere-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlanywhere-adapter.gemspec b/activerecord-sqlanywhere-adapter.gemspec index 742f388..364524f 100755 --- a/activerecord-sqlanywhere-adapter.gemspec +++ b/activerecord-sqlanywhere-adapter.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.require_paths = [%q{lib}] s.summary = %q{ActiveRecord driver for SQL Anywhere} s.platform = Gem::Platform::RUBY - s.add_dependency 'sqlanywhere', '>=1.0.0' + s.add_dependency 'sqlanywhere', '>=0.1.5' s.add_dependency 'activerecord', '>= 3.0.3' end From ece264a4f203d22cda9d9c3454314f6a59470abd Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 30 Jan 2013 11:46:25 +0000 Subject: [PATCH 24/47] Revert "Update the adapter to use the new symbols" This commit broke using the sqlanywhere gem This reverts commit f1c954c5b7c0f19c7d169dce54f0fe0840018f38. --- .../connection_adapters/sqlanywhere_adapter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb index fa274ec..50e2004 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb @@ -663,7 +663,7 @@ def exec_query(sql, name = 'SQL', binds = []) result, bind_param = SA.instance.api.sqlany_describe_bind_param(stmt, i) sqlanywhere_error_test(sql) if result==0 - bind_param.set_direction(:input) + bind_param.set_direction(1) # https://github.com/sqlanywhere/sqlanywhere/blob/master/ext/sacapi.h#L175 if bind_value.nil? bind_param.set_value(nil) elsif bind_type == :datetime @@ -762,9 +762,9 @@ def query(sql) def native_type_to_ruby_type(native_type, value) return nil if value.nil? case native_type - when :decimal # (also and more importantly numeric) + when 484 # DT_DECIMAL (also and more importantly numeric) BigDecimal.new(value) - when :var_char, :fix_char, :long_var_char, :string, :long_n_var_char + when 448,452,456,460,640 # DT_VARCHAR, DT_FIXCHAR, DT_LONGVARCHAR, DT_STRING, DT_LONGNVARCHAR # hack, not sure how to manage proper encoding value = value.force_encoding(ActiveRecord::Base.connection_config['encoding'] || 'UTF-8') value = value.encode('UTF-8') From a69632db12f9f51460c53521a03bcada33033235 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 6 Feb 2013 10:24:59 +0000 Subject: [PATCH 25/47] Start our JDBC adapter --- ...ord-sqlanywhere-adapter-in4systems.gemspec | 29 ---------- ...qlanywhere-jdbc-in4systems-adapter.gemspec | 12 ++-- .../connection_adapters/sqlanywhere.rake | 58 ------------------- lib/activerecord-sqlanywhere-adapter.rb | 16 ----- 4 files changed, 5 insertions(+), 110 deletions(-) delete mode 100755 activerecord-sqlanywhere-adapter-in4systems.gemspec rename activerecord-sqlanywhere-adapter.gemspec => activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec (69%) delete mode 100755 lib/active_record/connection_adapters/sqlanywhere.rake delete mode 100755 lib/activerecord-sqlanywhere-adapter.rb diff --git a/activerecord-sqlanywhere-adapter-in4systems.gemspec b/activerecord-sqlanywhere-adapter-in4systems.gemspec deleted file mode 100755 index 81cc3ae..0000000 --- a/activerecord-sqlanywhere-adapter-in4systems.gemspec +++ /dev/null @@ -1,29 +0,0 @@ -Gem::Specification.new do |s| - s.name = %q{activerecord-sqlanywhere-adapter-in4systems} - s.version = "1.0.2" - - s.authors = ['Eric Farar', 'Chris Couzens'] - s.description = %q{ActiveRecord driver for SQL Anywhere customized for in4systems} - s.email = %q{eric.farrar@ianywhere.com} - s.files = [ - "CHANGELOG", - "LICENSE", - "README", - "test/connection.rb", - "lib/active_record/connection_adapters/sqlanywhere_adapter.rb", - "lib/arel/visitors/sqlanywhere.rb", - "lib/active_record/connection_adapters/sqlanywhere.rake", - "lib/activerecord-sqlanywhere-adapter.rb" - ] - s.executables = [] - s.test_files = [] - s.has_rdoc = false - s.homepage = 'https://github.com/in4systems/activerecord-sqlanywhere-adapter' - s.licenses = [%q{Apache License Version 2.0}] - s.require_paths = [%q{lib}] - s.summary = %q{ActiveRecord driver for SQL Anywhere} - s.platform = Gem::Platform::RUBY - s.add_dependency 'sqlanywhere-ffi', '>=1.0.0' - s.add_dependency 'activerecord', '>= 3.0.3' -end - diff --git a/activerecord-sqlanywhere-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec similarity index 69% rename from activerecord-sqlanywhere-adapter.gemspec rename to activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 364524f..d7b1ff9 100755 --- a/activerecord-sqlanywhere-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,10 +1,10 @@ Gem::Specification.new do |s| - s.name = %q{activerecord-sqlanywhere-adapter} - s.version = "1.0.2" + s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} + s.version = "1.0.0" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord driver for SQL Anywhere customized for in4systems} - s.email = %q{eric.farrar@ianywhere.com} + s.email = %q{chris.couzens@in4systems.com} s.files = [ "CHANGELOG", "LICENSE", @@ -12,8 +12,6 @@ Gem::Specification.new do |s| "test/connection.rb", "lib/active_record/connection_adapters/sqlanywhere_adapter.rb", "lib/arel/visitors/sqlanywhere.rb", - "lib/active_record/connection_adapters/sqlanywhere.rake", - "lib/activerecord-sqlanywhere-adapter.rb" ] s.executables = [] s.test_files = [] @@ -22,8 +20,8 @@ Gem::Specification.new do |s| s.licenses = [%q{Apache License Version 2.0}] s.require_paths = [%q{lib}] s.summary = %q{ActiveRecord driver for SQL Anywhere} - s.platform = Gem::Platform::RUBY - s.add_dependency 'sqlanywhere', '>=0.1.5' + s.platform = 'java' + s.add_dependency 'activerecord-jdbc-adapter' s.add_dependency 'activerecord', '>= 3.0.3' end diff --git a/lib/active_record/connection_adapters/sqlanywhere.rake b/lib/active_record/connection_adapters/sqlanywhere.rake deleted file mode 100755 index 17f1569..0000000 --- a/lib/active_record/connection_adapters/sqlanywhere.rake +++ /dev/null @@ -1,58 +0,0 @@ -# Taken from https://github.com/rsim/oracle-enhanced/blob/master/lib/active_record/connection_adapters/oracle_enhanced.rake - -# implementation idea taken from JDBC adapter -# added possibility to execute previously defined task (passed as argument to task block) -def redefine_task(*args, &block) - task_name = Hash === args.first ? args.first.keys[0] : args.first - existing_task = Rake.application.lookup task_name - existing_actions = nil - if existing_task - class << existing_task; public :instance_variable_set, :instance_variable_get; end - existing_task.instance_variable_set "@prerequisites", FileList[] - existing_actions = existing_task.instance_variable_get "@actions" - existing_task.instance_variable_set "@actions", [] - end - task(*args) do - block.call(existing_actions) - end - end - - -# https://github.com/rsim/oracle-enhanced/blob/master/lib/active_record/connection_adapters/oracle_enhanced.rake - -if defined?(drop_database) == 'method' - def drop_database_with_sqlanywhere(config) - if config['adapter'] == 'sqlanywhere' - ActiveRecord::Base.establish_connection(config) - ActiveRecord::Base.connection.purge_database - else - drop_database_without_sqlanywhere(config) - end - end - alias :drop_database_without_sqlanywhere :drop_database - alias :drop_database :drop_database_with_sqlanywhere -end - -namespace :db do - namespace :test do - redefine_task :purge => :environment do |existing_actions| - abcs = ActiveRecord::Base.configurations - if abcs['test']['adapter'] == 'sqlanywhere' - ActiveRecord::Base.establish_connection(:test) - ActiveRecord::Base.connection.purge_database - else - Array(existing_actions).each{|action| action.call} - end - end - end - - namespace :schema do - redefine_task :dump => :environment do |existing_actions| - if ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'sqlanywhere' - ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Rails.env]) - ActiveRecord::SchemaDumper.ignore_tables = ActiveRecord::Base.connection.viewed_tables - end - Array(existing_actions).each{|action| action.call} - end - end -end \ No newline at end of file diff --git a/lib/activerecord-sqlanywhere-adapter.rb b/lib/activerecord-sqlanywhere-adapter.rb deleted file mode 100755 index fa979e5..0000000 --- a/lib/activerecord-sqlanywhere-adapter.rb +++ /dev/null @@ -1,16 +0,0 @@ -# https://github.com/rsim/oracle-enhanced/blob/master/lib/activerecord-oracle_enhanced-adapter.rb - -if defined?(::Rails::Railtie) - - module ActiveRecord - module ConnectionAdapters - class SqlanywhereRailtie < ::Rails::Railtie - rake_tasks do - load 'active_record/connection_adapters/sqlanywhere.rake' - end - - end - end - end - -end \ No newline at end of file From ad260c8c3949870c39dbb2a17c6ee2794f688821 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 6 Feb 2013 12:09:41 +0000 Subject: [PATCH 26/47] rename files and delete methods that I don't expect to be used. --- ...qlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- .../sqlanywhere_adapter.rb | 793 ------------------ .../sqlanywhere_jdbc_in4systems_adapter.rb | 422 ++++++++++ 3 files changed, 423 insertions(+), 794 deletions(-) delete mode 100755 lib/active_record/connection_adapters/sqlanywhere_adapter.rb create mode 100755 lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index d7b1ff9..1abc52b 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |s| "LICENSE", "README", "test/connection.rb", - "lib/active_record/connection_adapters/sqlanywhere_adapter.rb", + "lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb", "lib/arel/visitors/sqlanywhere.rb", ] s.executables = [] diff --git a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_adapter.rb deleted file mode 100755 index 50e2004..0000000 --- a/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +++ /dev/null @@ -1,793 +0,0 @@ -#encoding: utf-8 -#==================================================== -# -# Copyright 2008-2010 iAnywhere Solutions, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# -# See the License for the specific language governing permissions and -# limitations under the License. -# -# While not a requirement of the license, if you do modify this file, we -# would appreciate hearing about it. Please email sqlany_interfaces@sybase.com -# -# -#==================================================== - -require 'active_record/connection_adapters/abstract_adapter' -require 'arel/visitors/sqlanywhere.rb' - -# Singleton class to hold a valid instance of the SQLAnywhereInterface across all connections -class SA - include Singleton - attr_accessor :api - - def initialize - require 'sqlanywhere' unless defined? SQLAnywhere - @api = SQLAnywhere::SQLAnywhereInterface.new() - raise LoadError, "Could not load SQLAnywhere DBCAPI library" if SQLAnywhere::API.sqlany_initialize_interface(@api) == 0 - raise LoadError, "Could not initialize SQLAnywhere DBCAPI library" if @api.sqlany_init() == 0 - end -end - -module ActiveRecord - class Base - DEFAULT_CONFIG = { :username => 'dba', :password => 'sql' } - # Main connection function to SQL Anywhere - # Connection Adapter takes four parameters: - # * :database (required, no default). Corresponds to "DatabaseName=" in connection string - # * :server (optional, defaults to :databse). Corresponds to "ServerName=" in connection string - # * :username (optional, default to 'dba') - # * :password (optional, deafult to 'sql') - # * :encoding (optional, defaults to charset of OS) - # * :commlinks (optional). Corresponds to "CommLinks=" in connection string - # * :connection_name (optional). Corresponds to "ConnectionName=" in connection string - - def self.sqlanywhere_connection(config) - - if config[:connection_string] - connection_string = config[:connection_string] - else - config = DEFAULT_CONFIG.merge(config) - - raise ArgumentError, "No database name was given. Please add a :database option." unless config.has_key?(:database) - - connection_string = "ServerName=#{(config[:server] || config[:database])};DatabaseName=#{config[:database]};UserID=#{config[:username]};Password=#{config[:password]};" - connection_string += "CommLinks=#{config[:commlinks]};" unless config[:commlinks].nil? - connection_string += "ConnectionName=#{config[:connection_name]};" unless config[:connection_name].nil? - connection_string += "CharSet=#{config[:encoding]};" unless config[:encoding].nil? - connection_string += "Idle=0" # Prevent the server from disconnecting us if we're idle for >240mins (by default) - end - - db = SA.instance.api.sqlany_new_connection() - - ConnectionAdapters::SQLAnywhereAdapter.new(db, logger, connection_string) - end - end - - module ConnectionAdapters - class SQLAnywhereException < StandardError - attr_reader :errno - attr_reader :sql - - def initialize(message, errno, sql) - super(message) - @errno = errno - @sql = sql - end - end - - class SQLAnywhereColumn < Column - private - # Overridden to handle SQL Anywhere integer, varchar, binary, and timestamp types - def simplified_type(field_type) - return :boolean if field_type =~ /tinyint/i - return :boolean if field_type =~ /bit/i - return :text if field_type =~ /long varchar/i - return :string if field_type =~ /varchar/i - return :binary if field_type =~ /long binary/i - return :datetime if field_type =~ /timestamp/i - return :integer if field_type =~ /smallint|bigint/i - return :text if field_type =~ /xml/i - return :integer if field_type =~ /uniqueidentifier/i - super - end - - def extract_limit(sql_type) - case sql_type - when /^tinyint/i - 1 - when /^smallint/i - 2 - when /^integer/i - 4 - when /^bigint/i - 8 - else super - end - end - - protected - # Handles the encoding of a binary object into SQL Anywhere - # SQL Anywhere requires that binary values be encoded as \xHH, where HH is a hexadecimal number - # This function encodes the binary string in this format - def self.string_to_binary(value) - "\\x" + value.unpack("H*")[0].scan(/../).join("\\x") - end - - def self.binary_to_string(value) - value.gsub(/\\x[0-9]{2}/) { |byte| byte[2..3].hex } - end - - # Should override the time column values. - # Sybase doesn't like the time zones. - - end - - class SQLAnywhereAdapter < AbstractAdapter - def initialize( connection, logger, connection_string = "") #:nodoc: - super(connection, logger) - @auto_commit = true - @affected_rows = 0 - @connection_string = connection_string - @visitor = Arel::Visitors::SQLAnywhere.new self - connect! - end - - def self.visitor_for(pool) - config = pool.spec.config - - if config.fetch(:prepared_statements) {true} - Arel::Visitors::SQLAnywhere.new pool - else - BindSubstitution.new pool - end - end - - def adapter_name #:nodoc: - 'SQLAnywhere' - end - - def supports_migrations? #:nodoc: - true - end - - def requires_reloading? - true - end - - def active? - # The liveness variable is used a low-cost "no-op" to test liveness - SA.instance.api.sqlany_execute_immediate(@connection, "SET liveness = 1") == 1 - rescue - false - end - - def disconnect! - result = SA.instance.api.sqlany_disconnect( @connection ) - super - end - - def reconnect! - disconnect! - connect! - end - - def supports_count_distinct? #:nodoc: - true - end - - def supports_autoincrement? #:nodoc: - true - end - - # Maps native ActiveRecord/Ruby types into SQLAnywhere types - # TINYINTs are treated as the default boolean value - # ActiveRecord allows NULLs in boolean columns, and the SQL Anywhere BIT type does not - # As a result, TINYINT must be used. All TINYINT columns will be assumed to be boolean and - # should not be used as single-byte integer columns. This restriction is similar to other ActiveRecord database drivers - def native_database_types #:nodoc: - { - :primary_key => 'INTEGER PRIMARY KEY DEFAULT AUTOINCREMENT NOT NULL', - :string => { :name => "varchar", :limit => 255 }, - :text => { :name => "long varchar" }, - :integer => { :name => "integer", :limit => 4 }, - :float => { :name => "float" }, - :decimal => { :name => "decimal" }, - :datetime => { :name => "datetime" }, - :timestamp => { :name => "datetime" }, - :time => { :name => "time" }, - :date => { :name => "date" }, - :binary => { :name => "binary" }, - :boolean => { :name => "tinyint", :limit => 1} - } - end - - # QUOTING ================================================== - - # Applies quotations around column names in generated queries - def quote_column_name(name) #:nodoc: - %Q("#{name}") - end - - # Handles special quoting of binary columns. Binary columns will be treated as strings inside of ActiveRecord. - # ActiveRecord requires that any strings it inserts into databases must escape the backslash (\). - # Since in the binary case, the (\x) is significant to SQL Anywhere, it cannot be escaped. - def quote(value, column = nil) - case value - when String, ActiveSupport::Multibyte::Chars - value_S = value.to_s - if column && column.type == :binary && column.class.respond_to?(:string_to_binary) - "'#{column.class.string_to_binary(value_S)}'" - else - super(value, column) - end - else - super(value, column) - end - end - - def quoted_true - '1' - end - - def quoted_false - '0' - end - - - # This function (distinct) is based on the Oracle Enhacned ActiveRecord driver maintained by Raimonds Simanovskis (2010) - # (https://github.com/rsim/oracle-enhanced) - def distinct(columns, order_by) #:nodoc: - return "DISTINCT #{columns}" if order_by.blank? - - # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using - # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT - order_columns = if order_by.is_a?(String) - order_by.split(',').map { |s| s.strip }.reject(&:blank?) - else # in latest ActiveRecord versions order_by is already Array - order_by - end - order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i| - # remove any ASC/DESC modifiers - value = c =~ /^(.+)\s+(ASC|DESC)\s*$/i ? $1 : c - "FIRST_VALUE(#{value}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__" - end - sql = "DISTINCT #{columns}, " - sql << order_columns * ", " - end - - # The database execution function - def execute(sql, name = nil) #:nodoc: - if name == :skip_logging - r = SA.instance.api.sqlany_execute_immediate(@connection, sql) - sqlanywhere_error_test(sql) if r==0 - else - log(sql, name) { execute(sql, :skip_logging) } - end - end - - def sqlanywhere_error_test(sql = '') - error_code, error_message = SA.instance.api.sqlany_error(@connection) - if error_code != 0 - sqlanywhere_error(error_code, error_message, sql) - end - end - - def sqlanywhere_error(code, message, sql) - raise SQLAnywhereException.new(message, code, sql) - end - - def translate_exception(exception, message) - return super unless exception.respond_to?(:errno) - case exception.errno - when -143 - if exception.sql !~ /^SELECT/i then - raise ActiveRecord::ActiveRecordError.new(message) - else - super - end - when -194 - raise InvalidForeignKey.new(message, exception) - when -196 - raise RecordNotUnique.new(message, exception) - when -183 - raise ArgumentError, message - else - super - end - end - - # The database update function. - def update_sql(sql, name = nil) - execute( sql, name ) - return @affected_rows - end - - # The database delete function. - def delete_sql(sql, name = nil) #:nodoc: - execute( sql, name ) - return @affected_rows - end - - # The database insert function. - # ActiveRecord requires that insert_sql returns the primary key of the row just inserted. In most cases, this can be accomplished - # by immediatly querying the @@identity property. If the @@identity property is 0, then passed id_value is used - def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc: - execute(sql, name) - - retval = last_inserted_id(nil) - retval = id_value if retval == 0 - return retval - end - - def exec_delete(sql, name = 'SQL', binds = []) - exec_query(sql, name, binds) - @affected_rows - end - alias :exec_update :exec_delete - - def last_inserted_id(result) - identity = SA.instance.api.sqlany_execute_direct(@connection, 'SELECT @@identity') - raise ActiveRecord::StatementInvalid.new("#{SA.instance.api.sqlany_error(@connection)}:#{sql}") if identity.nil? - SA.instance.api.sqlany_fetch_next(identity) - retval = SA.instance.api.sqlany_get_column(identity, 0)[1] - SA.instance.api.sqlany_free_stmt(identity) - - return retval - end - - # Returns a query as an array of arrays - def select_rows(sql, name = nil) - rs = SA.instance.api.sqlany_execute_direct(@connection, sql) - raise ActiveRecord::StatementInvalid.new("#{SA.instance.api.sqlany_error(@connection)}:#{sql}") if rs.nil? - record = [] - while SA.instance.api.sqlany_fetch_next(rs) == 1 - max_cols = SA.instance.api.sqlany_num_cols(rs) - result = Array.new(max_cols) - max_cols.times do |cols| - result[cols] = SA.instance.api.sqlany_get_column(rs, cols)[1] - end - record << result - end - SA.instance.api.sqlany_free_stmt(rs) - return record - end - - def begin_db_transaction #:nodoc: - @auto_commit = false; - end - - def commit_db_transaction #:nodoc: - SA.instance.api.sqlany_commit(@connection) - @auto_commit = true; - end - - def rollback_db_transaction #:nodoc: - SA.instance.api.sqlany_rollback(@connection) - @auto_commit = true; - end - - def add_lock!(sql, options) #:nodoc: - sql - end - - # SQL Anywhere does not support sizing of integers based on the sytax INTEGER(size). Integer sizes - # must be captured when generating the SQL and replaced with the appropriate size. - def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: - type = type.to_sym - if native = native_database_types[type] - if type == :integer - case limit - when 1 - column_type_sql = 'tinyint' - when 2 - column_type_sql = 'smallint' - when 3..4 - column_type_sql = 'integer' - when 5..8 - column_type_sql = 'bigint' - else - column_type_sql = 'integer' - end - column_type_sql - elsif type == :string and !limit.nil? - "varchar (#{limit})" - elsif type == :boolean - column_type_sql = 'tinyint' - else - super(type, limit, precision, scale) - end - else - super(type, limit, precision, scale) - end - end - - def viewed_tables(name = nil) - list_of_tables(['view'], name) - end - - def base_tables(name = nil) - list_of_tables(['base'], name) - end - - # Do not return SYS-owned or DBO-owned tables or RS_systabgroup-owned - def tables(name = nil) #:nodoc: - list_of_tables(['base', 'view']) - end - - def columns(table_name, name = nil) #:nodoc: - table_structure(table_name).map do |field| - SQLAnywhereColumn.new(field['name'], field['default'], field['domain'], (field['nulls'] == 1)) - end - end - - def indexes(table_name, name = nil) #:nodoc: - if @major_version <= 11 # the sql doesn't work in older databases. - return [] - end - sql = "SELECT DISTINCT index_name, \"unique\" FROM SYS.SYSTABLE INNER JOIN SYS.SYSIDXCOL ON SYS.SYSTABLE.table_id = SYS.SYSIDXCOL.table_id INNER JOIN SYS.SYSIDX ON SYS.SYSTABLE.table_id = SYS.SYSIDX.table_id AND SYS.SYSIDXCOL.index_id = SYS.SYSIDX.index_id WHERE table_name = '#{table_name}' AND index_category > 2" - select(sql, name).map do |row| - index = IndexDefinition.new(table_name, row['index_name']) - index.unique = row['unique'] == 1 - sql = "SELECT column_name FROM SYS.SYSIDX INNER JOIN SYS.SYSIDXCOL ON SYS.SYSIDXCOL.table_id = SYS.SYSIDX.table_id AND SYS.SYSIDXCOL.index_id = SYS.SYSIDX.index_id INNER JOIN SYS.SYSCOLUMN ON SYS.SYSCOLUMN.table_id = SYS.SYSIDXCOL.table_id AND SYS.SYSCOLUMN.column_id = SYS.SYSIDXCOL.column_id WHERE index_name = '#{row['index_name']}'" - index.columns = select(sql).map { |col| col['column_name'] } - index - end - end - - def primary_key(table_name) #:nodoc: - sql = "SELECT cname from SYS.SYSCOLUMNS where tname = '#{table_name}' and in_primary_key = 'Y'" - rs = exec_query(sql) - if !rs.nil? and !rs.first.nil? - rs.first['cname'] - else - nil - end - end - - def remove_index(table_name, options={}) #:nodoc: - execute "DROP INDEX #{quote_table_name(table_name)}.#{quote_column_name(index_name(table_name, options))}" - end - - def rename_table(name, new_name) - execute "ALTER TABLE #{quote_table_name(name)} RENAME #{quote_table_name(new_name)}" - end - - def change_column_default(table_name, column_name, default) #:nodoc: - execute "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} DEFAULT #{quote(default)}" - end - - def change_column_null(table_name, column_name, null, default = nil) - unless null || default.nil? - execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") - end - execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? '' : 'NOT'} NULL") - end - - def change_column(table_name, column_name, type, options = {}) #:nodoc: - add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - add_column_options!(add_column_sql, options) - add_column_sql << ' NULL' if options[:null] - execute(add_column_sql) - end - - def rename_column(table_name, column_name, new_column_name) #:nodoc: - if column_name.downcase == new_column_name.downcase - whine = "if_the_only_change_is_case_sqlanywhere_doesnt_rename_the_column" - rename_column table_name, column_name, "#{new_column_name}#{whine}" - rename_column table_name, "#{new_column_name}#{whine}", new_column_name - else - execute "ALTER TABLE #{quote_table_name(table_name)} RENAME #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" - end - end - - def remove_column(table_name, *column_names) - column_names = column_names.flatten - column_names.zip(columns_for_remove(table_name, *column_names)).each do |unquoted_column_name, column_name| - sql = <<-SQL - SELECT "index_name" FROM SYS.SYSTAB join SYS.SYSTABCOL join SYS.SYSIDXCOL join SYS.SYSIDX - WHERE "column_name" = '#{unquoted_column_name}' AND "table_name" = '#{table_name}' - SQL - select(sql, nil).each do |row| - execute "DROP INDEX \"#{table_name}\".\"#{row['index_name']}\"" - end - execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}" - end - end - - - def purge_database - base_tables.each do |base_table_name| - drop_table(base_table_name) - end - end - - def select(sql, name = nil, binds = []) #:nodoc: - exec_query(sql, name, binds).to_a - end - - protected - - def list_of_tables(types, name = nil) - sql = "SELECT table_name FROM SYS.SYSTABLE WHERE table_type in (#{types.map{|t| quote(t)}.join(', ')}) and creator NOT IN (0,3,5)" - select(sql, name).map { |row| row["table_name"] } - end - - # ActiveRecord uses the OFFSET/LIMIT keywords at the end of query to limit the number of items in the result set. - # This syntax is NOT supported by SQL Anywhere. In previous versions of this adapter this adapter simply - # overrode the add_limit_offset function and added the appropriate TOP/START AT keywords to the start of the query. - # However, this will not work for cases where add_limit_offset is being used in a subquery since add_limit_offset - # is called with the WHERE clause. - # - # As a result, the following function must be called before every SELECT statement against the database. It - # recursivly walks through all subqueries in the SQL statment and replaces the instances of OFFSET/LIMIT with the - # corresponding TOP/START AT. It was my intent to do the entire thing using regular expressions, but it would seem - # that it is not possible given that it must count levels of nested brackets. - def modify_limit_offset(sql) - modified_sql = "" - subquery_sql = "" - in_single_quote = false - in_double_quote = false - nesting_level = 0 - if sql =~ /(OFFSET|LIMIT)/xmi then - if sql =~ /\(/ then - sql.split(//).each_with_index do |x, i| - case x[0] - when 40 # left brace - ( - modified_sql << x if nesting_level == 0 - subquery_sql << x if nesting_level > 0 - nesting_level = nesting_level + 1 unless in_double_quote || in_single_quote - when 41 # right brace - ) - nesting_level = nesting_level - 1 unless in_double_quote || in_single_quote - if nesting_level == 0 and !in_double_quote and !in_single_quote then - modified_sql << modify_limit_offset(subquery_sql) - subquery_sql = "" - end - modified_sql << x if nesting_level == 0 - subquery_sql << x if nesting_level > 0 - when 39 # single quote - ' - in_single_quote = in_single_quote ^ true unless in_double_quote - modified_sql << x if nesting_level == 0 - subquery_sql << x if nesting_level > 0 - when 34 # double quote - " - in_double_quote = in_double_quote ^ true unless in_single_quote - modified_sql << x if nesting_level == 0 - subquery_sql << x if nesting_level > 0 - else - modified_sql << x if nesting_level == 0 - subquery_sql << x if nesting_level > 0 - end - raise ActiveRecord::StatementInvalid.new("Braces do not match: #{sql}") if nesting_level < 0 - end - else - modified_sql = sql - end - raise ActiveRecord::StatementInvalid.new("Quotes do not match: #{sql}") if in_double_quote or in_single_quote - return "" if modified_sql.nil? - select_components = modified_sql.scan(/\ASELECT\s+(DISTINCT)?(.*?)(?:\s+LIMIT\s+(.*?))?(?:\s+OFFSET\s+(.*?))?\Z/xmi) - return modified_sql if select_components[0].nil? - final_sql = "SELECT #{select_components[0][0]} " - final_sql << "TOP #{select_components[0][2].nil? ? 1000000 : select_components[0][2]} " - final_sql << "START AT #{(select_components[0][3].to_i + 1).to_s} " unless select_components[0][3].nil? - final_sql << "#{select_components[0][1]}" - return final_sql - else - return sql - end - end - - # Queries the structure of a table including the columns names, defaults, type, and nullability - # ActiveRecord uses the type to parse scale and precision information out of the types. As a result, - # chars, varchars, binary, nchars, nvarchars must all be returned in the form type(width) - # numeric and decimal must be returned in the form type(width, scale) - # Nullability is returned as 0 (no nulls allowed) or 1 (nulls allowed) - # Alos, ActiveRecord expects an autoincrement column to have default value of NULL - - def table_structure(table_name) - sql = <<-SQL -SELECT SYS.SYSCOLUMN.column_name AS name, - if left("default",1)='''' then substring("default", 2, length("default")-2) // remove the surrounding quotes - else NULLIF(SYS.SYSCOLUMN."default", 'autoincrement') - endif AS "default", - IF SYS.SYSCOLUMN.domain_id IN (7,8,9,11,33,34,35,3,27) THEN - IF SYS.SYSCOLUMN.domain_id IN (3,27) THEN - SYS.SYSDOMAIN.domain_name || '(' || SYS.SYSCOLUMN.width || ',' || SYS.SYSCOLUMN.scale || ')' - ELSE - SYS.SYSDOMAIN.domain_name || '(' || SYS.SYSCOLUMN.width || ')' - ENDIF - ELSE - SYS.SYSDOMAIN.domain_name - ENDIF AS domain, - IF SYS.SYSCOLUMN.nulls = 'Y' THEN 1 ELSE 0 ENDIF AS nulls -FROM - SYS.SYSCOLUMN - INNER JOIN SYS.SYSTABLE ON SYS.SYSCOLUMN.table_id = SYS.SYSTABLE.table_id - INNER JOIN SYS.SYSDOMAIN ON SYS.SYSCOLUMN.domain_id = SYS.SYSDOMAIN.domain_id -WHERE - SYS.SYSTABLE.creator = 1 AND - table_name = '#{table_name}' -SQL - structure = exec_query(sql, :skip_logging) - raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure == false - structure - end - - # Required to prevent DEFAULT NULL being added to primary keys - def options_include_default?(options) - options.include?(:default) && !(options[:null] == false && options[:default].nil?) - end - - private - - def connect! - result = SA.instance.api.sqlany_connect(@connection, @connection_string) - if result == 1 then - set_connection_options - else - error = SA.instance.api.sqlany_error(@connection) - raise ActiveRecord::ActiveRecordError.new("#{error}: Cannot Establish Connection") - end - version = exec_query('select @@version').rows[0][0] - @major_version = /^\d+/.match(version).to_s.to_i - end - - def set_connection_options - SA.instance.api.sqlany_execute_immediate(@connection, "SET TEMPORARY OPTION non_keywords = 'LOGIN'") rescue nil - SA.instance.api.sqlany_execute_immediate(@connection, "SET TEMPORARY OPTION timestamp_format = 'YYYY-MM-DD HH:NN:SS'") rescue nil - #SA.instance.api.sqlany_execute_immediate(@connection, "SET OPTION reserved_keywords = 'LIMIT'") rescue nil - # The liveness variable is used a low-cost "no-op" to test liveness - SA.instance.api.sqlany_execute_immediate(@connection, "CREATE VARIABLE liveness INT") rescue nil - end - - def exec_query(sql, name = 'SQL', binds = []) - log(sql, name, binds) do - stmt = SA.instance.api.sqlany_prepare(@connection, sql) - - if stmt.nil? - sqlanywhere_error_test(sql) - end - - for i in 0...binds.length - bind_type = binds[i][0].type - bind_value = binds[i][1] - result, bind_param = SA.instance.api.sqlany_describe_bind_param(stmt, i) - sqlanywhere_error_test(sql) if result==0 - - bind_param.set_direction(1) # https://github.com/sqlanywhere/sqlanywhere/blob/master/ext/sacapi.h#L175 - if bind_value.nil? - bind_param.set_value(nil) - elsif bind_type == :datetime - bind_param.set_value(bind_value.to_datetime.to_s :db) - elsif bind_type == :boolean - bind_param.set_value(bind_value ? 1 : 0) - elsif bind_type == :decimal - bind_param.set_value(bind_value.to_s) - elsif bind_type == :date - bind_param.set_value(bind_value.to_s) - else - bind_param.set_value(bind_value) - end - result = SA.instance.api.sqlany_bind_param(stmt, i, bind_param) - sqlanywhere_error_test(sql) if result==0 - - end - - if SA.instance.api.sqlany_execute(stmt) == 0 - sqlanywhere_error_test(sql) - end - - fields = [] - native_types = [] - - num_cols = SA.instance.api.sqlany_num_cols(stmt) - sqlanywhere_error_test(sql) if num_cols == -1 - - for i in 0...num_cols - result, col_num, name, ruby_type, native_type, precision, scale, max_size, nullable = SA.instance.api.sqlany_get_column_info(stmt, i) - sqlanywhere_error_test(sql) if result==0 - fields << name - native_types << native_type - end - rows = [] - while SA.instance.api.sqlany_fetch_next(stmt) == 1 - row = [] - for i in 0...num_cols - r, value = SA.instance.api.sqlany_get_column(stmt, i) - row << native_type_to_ruby_type(native_types[i], value) - end - rows << row - end - SA.instance.api.sqlany_free_stmt(stmt) - - if @auto_commit - result = SA.instance.api.sqlany_commit(@connection) - sqlanywhere_error_test(sql) if result==0 - end - return ActiveRecord::Result.new(fields, rows) - end - end - - def query(sql) - return if sql.nil? - #sql = modify_limit_offset(sql) - - # ActiveRecord allows a query to return TOP 0. SQL Anywhere requires that the TOP value is a positive integer. - return Array.new() if sql =~ /TOP 0/i - - # Executes the query, iterates through the results, and builds an array of hashes. - rs = SA.instance.api.sqlany_execute_direct(@connection, sql) - if rs.nil? - result, errstr = SA.instance.api.sqlany_error(@connection) - raise SQLAnywhereException.new(errstr, result, sql) - end - - record = [] - if( SA.instance.api.sqlany_num_cols(rs) > 0 ) - while SA.instance.api.sqlany_fetch_next(rs) == 1 - max_cols = SA.instance.api.sqlany_num_cols(rs) - result = Hash.new() - max_cols.times do |cols| - col_content=SA.instance.api.sqlany_get_column(rs, cols)[1] - if !col_content.nil? && col_content.is_a?(String) - puts ":encoding missing in database.yml" if ActiveRecord::Base.configurations[Rails.env]['encoding'].nil? - col_content = col_content.force_encoding(ActiveRecord::Base.configurations[Rails.env]['encoding']) - end - result[SA.instance.api.sqlany_get_column_info(rs, cols)[2]] = col_content - end - record << result - end - @affected_rows = 0 - else - @affected_rows = SA.instance.api.sqlany_affected_rows(rs) - end - SA.instance.api.sqlany_free_stmt(rs) - - SA.instance.api.sqlany_commit(@connection) if @auto_commit - return record - end - - # convert sqlany type to ruby type - # the types are taken from here - # http://dcx.sybase.com/1101/en/dbprogramming_en11/pg-c-api-native-type-enum.html - def native_type_to_ruby_type(native_type, value) - return nil if value.nil? - case native_type - when 484 # DT_DECIMAL (also and more importantly numeric) - BigDecimal.new(value) - when 448,452,456,460,640 # DT_VARCHAR, DT_FIXCHAR, DT_LONGVARCHAR, DT_STRING, DT_LONGNVARCHAR - # hack, not sure how to manage proper encoding - value = value.force_encoding(ActiveRecord::Base.connection_config['encoding'] || 'UTF-8') - value = value.encode('UTF-8') - # Why am I removing the whitespace from the end of the string? - # - # Sqlanywhere allowed us to create a string foreign key. - # Somehow on only one end of the foreign key, the values got spaces at the end. - # The foreign key was still valid in Sqlanywhere: It worked for joins and it worked for referencing constraints. - # - # It however does not work for the ActiveRecord includes method. - # Rails will bring back the associated records, but then it fails to pair the records correctly. - # Removing whitespace from the ends of all strings fixes this. It is a hack however, so I'm open - # for suggestions on coming up with a better method. - # - begin - value = value.rstrip - rescue ArgumentError # invalid byte sequence in UTF-8 - end - else - value - end - end - end - end -end - diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb new file mode 100755 index 0000000..c0f0f1f --- /dev/null +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -0,0 +1,422 @@ +#encoding: utf-8 +#==================================================== +# +# Copyright 2008-2010 iAnywhere Solutions, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +# See the License for the specific language governing permissions and +# limitations under the License. +# +# While not a requirement of the license, if you do modify this file, we +# would appreciate hearing about it. Please email sqlany_interfaces@sybase.com +# +# +#==================================================== +#require 'active_record' +require 'activerecord-jdbc-adapter' + +require 'active_record/connection_adapters/abstract_adapter' +require 'arel/visitors/sqlanywhere.rb' +require 'pathname' + +module ActiveRecord + class Base + DEFAULT_CONFIG = { :username => 'dba', :password => 'sql' } + # Main connection function to SQL Anywhere + # Connection Adapter takes four parameters: + # * :database (required, no default). Corresponds to "DatabaseName=" in connection string + # * :server (optional, defaults to :databse). Corresponds to "ServerName=" in connection string + # * :username (optional, default to 'dba') + # * :password (optional, deafult to 'sql') + # * :encoding (optional, defaults to charset of OS) + # * :commlinks (optional). Corresponds to "CommLinks=" in connection string + # * :connection_name (optional). Corresponds to "ConnectionName=" in connection string + + def self.sqlanywhere_jdbc_in4systems_connection(config) + + if config[:connection_string] + connection_string = config[:connection_string] + else + config = DEFAULT_CONFIG.merge(config) + + raise ArgumentError, "No database name was given. Please add a :database option." unless config.has_key?(:database) + + connection_string = "ServerName=#{(config[:server] || config[:database])};DatabaseName=#{config[:database]};UserID=#{config[:username]};Password=#{config[:password]};" + connection_string += "CommLinks=#{config[:commlinks]};" unless config[:commlinks].nil? + connection_string += "ConnectionName=#{config[:connection_name]};" unless config[:connection_name].nil? + connection_string += "CharSet=#{config[:encoding]};" unless config[:encoding].nil? + connection_string += "Idle=0" # Prevent the server from disconnecting us if we're idle for >240mins (by default) + end + + url = 'jdbc:sqlanywhere:' + connection_string + + if ENV["SQLANY12"] + $CLASSPATH << Pathname.new(ENV["SQLANY12"]).join('java').join('sajdbc4.jar').to_s + driver = 'sybase.jdbc4.sqlanywhere.IDriver' + elsif ENV["SQLANY11"] + $CLASSPATH << Pathname.new(ENV["SQLANY11"]).join('java').join('sajdbc.jar').to_s + driver = 'sybase.jdbc.sqlanywhere.IDriver' + else + raise "Cannot find SqlAnywhere11 or 12 installation directory" + end + + ActiveRecord::Base.jdbc_connection({ + :adapter => 'jdbc', + :driver => driver, + :url => url + }) + + ConnectionAdapters::SQLAnywhereJdbcIn4systemsAdapter.new( logger, connection_string) + end + end + + module ConnectionAdapters + class SQLAnywhereException < StandardError + attr_reader :errno + attr_reader :sql + + def initialize(message, errno, sql) + super(message) + @errno = errno + @sql = sql + end + end + + class SQLAnywhereColumn < Column + private + # Overridden to handle SQL Anywhere integer, varchar, binary, and timestamp types + def simplified_type(field_type) + return :boolean if field_type =~ /tinyint/i + return :boolean if field_type =~ /bit/i + return :text if field_type =~ /long varchar/i + return :string if field_type =~ /varchar/i + return :binary if field_type =~ /long binary/i + return :datetime if field_type =~ /timestamp/i + return :integer if field_type =~ /smallint|bigint/i + return :text if field_type =~ /xml/i + return :integer if field_type =~ /uniqueidentifier/i + super + end + + def extract_limit(sql_type) + case sql_type + when /^tinyint/i + 1 + when /^smallint/i + 2 + when /^integer/i + 4 + when /^bigint/i + 8 + else super + end + end + + protected + # Handles the encoding of a binary object into SQL Anywhere + # SQL Anywhere requires that binary values be encoded as \xHH, where HH is a hexadecimal number + # This function encodes the binary string in this format + def self.string_to_binary(value) + "\\x" + value.unpack("H*")[0].scan(/../).join("\\x") + end + + def self.binary_to_string(value) + value.gsub(/\\x[0-9]{2}/) { |byte| byte[2..3].hex } + end + + # Should override the time column values. + # Sybase doesn't like the time zones. + + end + + class SQLAnywhereJdbcIn4systemsAdapter < AbstractAdapter + def initialize( logger, connection_string = "") #:nodoc: + @visitor = Arel::Visitors::SQLAnywhere.new self + end + + def self.visitor_for(pool) + config = pool.spec.config + + if config.fetch(:prepared_statements) {true} + Arel::Visitors::SQLAnywhere.new pool + else + BindSubstitution.new pool + end + end + + def adapter_name #:nodoc: + 'SQLAnywhere' + end + + def supports_migrations? #:nodoc: + true + end + + def requires_reloading? + true + end + + def active? + # The liveness variable is used a low-cost "no-op" to test liveness + SA.instance.api.sqlany_execute_immediate(@connection, "SET liveness = 1") == 1 + rescue + false + end + + def supports_count_distinct? #:nodoc: + true + end + + def supports_autoincrement? #:nodoc: + true + end + + # Maps native ActiveRecord/Ruby types into SQLAnywhere types + # TINYINTs are treated as the default boolean value + # ActiveRecord allows NULLs in boolean columns, and the SQL Anywhere BIT type does not + # As a result, TINYINT must be used. All TINYINT columns will be assumed to be boolean and + # should not be used as single-byte integer columns. This restriction is similar to other ActiveRecord database drivers + def native_database_types #:nodoc: + { + :primary_key => 'INTEGER PRIMARY KEY DEFAULT AUTOINCREMENT NOT NULL', + :string => { :name => "varchar", :limit => 255 }, + :text => { :name => "long varchar" }, + :integer => { :name => "integer", :limit => 4 }, + :float => { :name => "float" }, + :decimal => { :name => "decimal" }, + :datetime => { :name => "datetime" }, + :timestamp => { :name => "datetime" }, + :time => { :name => "time" }, + :date => { :name => "date" }, + :binary => { :name => "binary" }, + :boolean => { :name => "tinyint", :limit => 1} + } + end + + # QUOTING ================================================== + + # Applies quotations around column names in generated queries + def quote_column_name(name) #:nodoc: + %Q("#{name}") + end + + # Handles special quoting of binary columns. Binary columns will be treated as strings inside of ActiveRecord. + # ActiveRecord requires that any strings it inserts into databases must escape the backslash (\). + # Since in the binary case, the (\x) is significant to SQL Anywhere, it cannot be escaped. + def quote(value, column = nil) + case value + when String, ActiveSupport::Multibyte::Chars + value_S = value.to_s + if column && column.type == :binary && column.class.respond_to?(:string_to_binary) + "'#{column.class.string_to_binary(value_S)}'" + else + super(value, column) + end + else + super(value, column) + end + end + + def quoted_true + '1' + end + + def quoted_false + '0' + end + + # SQL Anywhere does not support sizing of integers based on the sytax INTEGER(size). Integer sizes + # must be captured when generating the SQL and replaced with the appropriate size. + def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: + type = type.to_sym + if native = native_database_types[type] + if type == :integer + case limit + when 1 + column_type_sql = 'tinyint' + when 2 + column_type_sql = 'smallint' + when 3..4 + column_type_sql = 'integer' + when 5..8 + column_type_sql = 'bigint' + else + column_type_sql = 'integer' + end + column_type_sql + elsif type == :string and !limit.nil? + "varchar (#{limit})" + elsif type == :boolean + column_type_sql = 'tinyint' + else + super(type, limit, precision, scale) + end + else + super(type, limit, precision, scale) + end + end + + def viewed_tables(name = nil) + list_of_tables(['view'], name) + end + + def base_tables(name = nil) + list_of_tables(['base'], name) + end + + # Do not return SYS-owned or DBO-owned tables or RS_systabgroup-owned + def tables(name = nil) #:nodoc: + list_of_tables(['base', 'view']) + end + + def columns(table_name, name = nil) #:nodoc: + table_structure(table_name).map do |field| + SQLAnywhereColumn.new(field['name'], field['default'], field['domain'], (field['nulls'] == 1)) + end + end + + def indexes(table_name, name = nil) #:nodoc: + if @major_version <= 11 # the sql doesn't work in older databases. + return [] + end + sql = "SELECT DISTINCT index_name, \"unique\" FROM SYS.SYSTABLE INNER JOIN SYS.SYSIDXCOL ON SYS.SYSTABLE.table_id = SYS.SYSIDXCOL.table_id INNER JOIN SYS.SYSIDX ON SYS.SYSTABLE.table_id = SYS.SYSIDX.table_id AND SYS.SYSIDXCOL.index_id = SYS.SYSIDX.index_id WHERE table_name = '#{table_name}' AND index_category > 2" + select(sql, name).map do |row| + index = IndexDefinition.new(table_name, row['index_name']) + index.unique = row['unique'] == 1 + sql = "SELECT column_name FROM SYS.SYSIDX INNER JOIN SYS.SYSIDXCOL ON SYS.SYSIDXCOL.table_id = SYS.SYSIDX.table_id AND SYS.SYSIDXCOL.index_id = SYS.SYSIDX.index_id INNER JOIN SYS.SYSCOLUMN ON SYS.SYSCOLUMN.table_id = SYS.SYSIDXCOL.table_id AND SYS.SYSCOLUMN.column_id = SYS.SYSIDXCOL.column_id WHERE index_name = '#{row['index_name']}'" + index.columns = select(sql).map { |col| col['column_name'] } + index + end + end + + def primary_key(table_name) #:nodoc: + sql = "SELECT cname from SYS.SYSCOLUMNS where tname = '#{table_name}' and in_primary_key = 'Y'" + rs = exec_query(sql) + if !rs.nil? and !rs.first.nil? + rs.first['cname'] + else + nil + end + end + + def remove_index(table_name, options={}) #:nodoc: + execute "DROP INDEX #{quote_table_name(table_name)}.#{quote_column_name(index_name(table_name, options))}" + end + + def rename_table(name, new_name) + execute "ALTER TABLE #{quote_table_name(name)} RENAME #{quote_table_name(new_name)}" + end + + def change_column_default(table_name, column_name, default) #:nodoc: + execute "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} DEFAULT #{quote(default)}" + end + + def change_column_null(table_name, column_name, null, default = nil) + unless null || default.nil? + execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") + end + execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? '' : 'NOT'} NULL") + end + + def change_column(table_name, column_name, type, options = {}) #:nodoc: + add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + add_column_options!(add_column_sql, options) + add_column_sql << ' NULL' if options[:null] + execute(add_column_sql) + end + + def rename_column(table_name, column_name, new_column_name) #:nodoc: + if column_name.downcase == new_column_name.downcase + whine = "if_the_only_change_is_case_sqlanywhere_doesnt_rename_the_column" + rename_column table_name, column_name, "#{new_column_name}#{whine}" + rename_column table_name, "#{new_column_name}#{whine}", new_column_name + else + execute "ALTER TABLE #{quote_table_name(table_name)} RENAME #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" + end + end + + def remove_column(table_name, *column_names) + column_names = column_names.flatten + column_names.zip(columns_for_remove(table_name, *column_names)).each do |unquoted_column_name, column_name| + sql = <<-SQL + SELECT "index_name" FROM SYS.SYSTAB join SYS.SYSTABCOL join SYS.SYSIDXCOL join SYS.SYSIDX + WHERE "column_name" = '#{unquoted_column_name}' AND "table_name" = '#{table_name}' + SQL + select(sql, nil).each do |row| + execute "DROP INDEX \"#{table_name}\".\"#{row['index_name']}\"" + end + execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}" + end + end + + protected + + def list_of_tables(types, name = nil) + sql = "SELECT table_name FROM SYS.SYSTABLE WHERE table_type in (#{types.map{|t| quote(t)}.join(', ')}) and creator NOT IN (0,3,5)" + select(sql, name).map { |row| row["table_name"] } + end + + # Queries the structure of a table including the columns names, defaults, type, and nullability + # ActiveRecord uses the type to parse scale and precision information out of the types. As a result, + # chars, varchars, binary, nchars, nvarchars must all be returned in the form type(width) + # numeric and decimal must be returned in the form type(width, scale) + # Nullability is returned as 0 (no nulls allowed) or 1 (nulls allowed) + # Alos, ActiveRecord expects an autoincrement column to have default value of NULL + + def table_structure(table_name) + sql = <<-SQL +SELECT SYS.SYSCOLUMN.column_name AS name, + if left("default",1)='''' then substring("default", 2, length("default")-2) // remove the surrounding quotes + else NULLIF(SYS.SYSCOLUMN."default", 'autoincrement') + endif AS "default", + IF SYS.SYSCOLUMN.domain_id IN (7,8,9,11,33,34,35,3,27) THEN + IF SYS.SYSCOLUMN.domain_id IN (3,27) THEN + SYS.SYSDOMAIN.domain_name || '(' || SYS.SYSCOLUMN.width || ',' || SYS.SYSCOLUMN.scale || ')' + ELSE + SYS.SYSDOMAIN.domain_name || '(' || SYS.SYSCOLUMN.width || ')' + ENDIF + ELSE + SYS.SYSDOMAIN.domain_name + ENDIF AS domain, + IF SYS.SYSCOLUMN.nulls = 'Y' THEN 1 ELSE 0 ENDIF AS nulls +FROM + SYS.SYSCOLUMN + INNER JOIN SYS.SYSTABLE ON SYS.SYSCOLUMN.table_id = SYS.SYSTABLE.table_id + INNER JOIN SYS.SYSDOMAIN ON SYS.SYSCOLUMN.domain_id = SYS.SYSDOMAIN.domain_id +WHERE + SYS.SYSTABLE.creator = 1 AND + table_name = '#{table_name}' +SQL + structure = exec_query(sql, :skip_logging) + raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure == false + structure + end + + # Required to prevent DEFAULT NULL being added to primary keys + def options_include_default?(options) + options.include?(:default) && !(options[:null] == false && options[:default].nil?) + end + + private + + def set_connection_options + SA.instance.api.sqlany_execute_immediate(@connection, "SET TEMPORARY OPTION non_keywords = 'LOGIN'") rescue nil + SA.instance.api.sqlany_execute_immediate(@connection, "SET TEMPORARY OPTION timestamp_format = 'YYYY-MM-DD HH:NN:SS'") rescue nil + #SA.instance.api.sqlany_execute_immediate(@connection, "SET OPTION reserved_keywords = 'LIMIT'") rescue nil + # The liveness variable is used a low-cost "no-op" to test liveness + SA.instance.api.sqlany_execute_immediate(@connection, "CREATE VARIABLE liveness INT") rescue nil + end + end + end +end + From 69c797198cd3f19dbd7faf249d793d08f188b9e6 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 6 Feb 2013 15:27:21 +0000 Subject: [PATCH 27/47] Use the jdbc connection as the base connection It does seem to be working- no doubt I'll find a problem later on --- .../sqlanywhere_jdbc_in4systems_adapter.rb | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index c0f0f1f..42392a1 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -59,7 +59,7 @@ def self.sqlanywhere_jdbc_in4systems_connection(config) end url = 'jdbc:sqlanywhere:' + connection_string - + if ENV["SQLANY12"] $CLASSPATH << Pathname.new(ENV["SQLANY12"]).join('java').join('sajdbc4.jar').to_s driver = 'sybase.jdbc4.sqlanywhere.IDriver' @@ -70,17 +70,17 @@ def self.sqlanywhere_jdbc_in4systems_connection(config) raise "Cannot find SqlAnywhere11 or 12 installation directory" end - ActiveRecord::Base.jdbc_connection({ - :adapter => 'jdbc', - :driver => driver, - :url => url - }) - - ConnectionAdapters::SQLAnywhereJdbcIn4systemsAdapter.new( logger, connection_string) + conn = ActiveRecord::Base.jdbc_connection({adapter: 'jdbc', driver: driver, url: url}) + + ConnectionAdapters::SQLAnywhereJdbcIn4systemsAdapter.new( conn, logger, connection_string) end end module ConnectionAdapters + class JdbcTypeConverter + AR_TO_JDBC_TYPES[:text] << lambda {|r| r['type_name'] =~ /^long varchar$/i} + end + class SQLAnywhereException < StandardError attr_reader :errno attr_reader :sql @@ -140,8 +140,13 @@ def self.binary_to_string(value) end class SQLAnywhereJdbcIn4systemsAdapter < AbstractAdapter - def initialize( logger, connection_string = "") #:nodoc: + delegate :select, :exec_query, to: :conn + + attr_reader :conn + def initialize( conn, logger, connection_string = "") #:nodoc: + super @visitor = Arel::Visitors::SQLAnywhere.new self + @conn = conn end def self.visitor_for(pool) From 0ac78c576afea3de75d92869bae5ab26787242f1 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 6 Feb 2013 17:34:13 +0000 Subject: [PATCH 28/47] Add last_inserted_id method --- .../sqlanywhere_jdbc_in4systems_adapter.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index 42392a1..290946a 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -364,6 +364,10 @@ def remove_column(table_name, *column_names) end end + def last_inserted_id(result) + select_value('SELECT @@identity') + end + protected def list_of_tables(types, name = nil) From ddc5cf361b16c63aea6638dbcbf97ac95ca6f698 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Wed, 6 Feb 2013 17:34:46 +0000 Subject: [PATCH 29/47] wrap exec_query so we can modify the binds --- .../sqlanywhere_jdbc_in4systems_adapter.rb | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index 290946a..ed23ad5 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -140,7 +140,7 @@ def self.binary_to_string(value) end class SQLAnywhereJdbcIn4systemsAdapter < AbstractAdapter - delegate :select, :exec_query, to: :conn + delegate :select, to: :conn attr_reader :conn def initialize( conn, logger, connection_string = "") #:nodoc: @@ -363,7 +363,24 @@ def remove_column(table_name, *column_names) execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}" end end - + + def exec_query(sql, name = 'SQL', binds = []) + binds.map! do |column, value| + type = column.type + if value != nil + case type + when :boolean + [column, value ? 1 : 0] + else + [column, value] + end + else + [column, value] + end + end + conn.exec_query(sql, name, binds) + end + def last_inserted_id(result) select_value('SELECT @@identity') end From dafde3359b0996bfa94816781e779b5bc895c8ea Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 7 Feb 2013 10:03:10 +0000 Subject: [PATCH 30/47] Teach the adapter the select_rows method --- activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 4 ++-- .../sqlanywhere_jdbc_in4systems_adapter.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 1abc52b..657ec5a 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,9 +1,9 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.0" + s.version = "1.0.1" s.authors = ['Eric Farar', 'Chris Couzens'] - s.description = %q{ActiveRecord driver for SQL Anywhere customized for in4systems} + s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} s.email = %q{chris.couzens@in4systems.com} s.files = [ "CHANGELOG", diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index ed23ad5..a0942bf 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -140,7 +140,7 @@ def self.binary_to_string(value) end class SQLAnywhereJdbcIn4systemsAdapter < AbstractAdapter - delegate :select, to: :conn + delegate :select, :select_rows, to: :conn attr_reader :conn def initialize( conn, logger, connection_string = "") #:nodoc: From 251f51189eb235a3cd2c420216e2c530f6bb9281 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 7 Feb 2013 11:13:57 +0000 Subject: [PATCH 31/47] Delegate the execute method to the connection --- activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- .../connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 657ec5a..ca682c6 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.1" + s.version = "1.0.2" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index a0942bf..c0b140b 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -140,7 +140,7 @@ def self.binary_to_string(value) end class SQLAnywhereJdbcIn4systemsAdapter < AbstractAdapter - delegate :select, :select_rows, to: :conn + delegate :select, :select_rows, :execute, to: :conn attr_reader :conn def initialize( conn, logger, connection_string = "") #:nodoc: From 2925d1ff8d7366e9d6190c0fc39f6ec0472b2390 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 7 Feb 2013 16:57:19 +0000 Subject: [PATCH 32/47] Support loading the jar file even when sqlany environmental variable isn't set Assuming it has been installed to /opt/sqlanywhere(11/12) --- activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- .../sqlanywhere_jdbc_in4systems_adapter.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index ca682c6..d0d9e35 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.2" + s.version = "1.0.3" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index c0b140b..73be63e 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -60,11 +60,11 @@ def self.sqlanywhere_jdbc_in4systems_connection(config) url = 'jdbc:sqlanywhere:' + connection_string - if ENV["SQLANY12"] - $CLASSPATH << Pathname.new(ENV["SQLANY12"]).join('java').join('sajdbc4.jar').to_s + if ENV["SQLANY12"] || Pathname.new('/opt/sqlanywhere12/java/sajdbc4.jar').exist? + $CLASSPATH << Pathname.new(ENV["SQLANY12"] || '/opt/sqlanywhere12/').join('java').join('sajdbc4.jar').to_s driver = 'sybase.jdbc4.sqlanywhere.IDriver' - elsif ENV["SQLANY11"] - $CLASSPATH << Pathname.new(ENV["SQLANY11"]).join('java').join('sajdbc.jar').to_s + elsif ENV["SQLANY11"] || Pathname.new('/opt/sqlanywhere11/java/sajdbc.jar').exist? + $CLASSPATH << Pathname.new(ENV["SQLANY11"] || '/opt/sqlanywhere11/').join('java').join('sajdbc.jar').to_s driver = 'sybase.jdbc.sqlanywhere.IDriver' else raise "Cannot find SqlAnywhere11 or 12 installation directory" From ab663fe3f6c08ec1f6fa4cb3d09f780f917a9c6b Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 7 Feb 2013 17:31:08 +0000 Subject: [PATCH 33/47] neaten and rewrite the code that sets the classpath --- ...cord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- .../sqlanywhere_jdbc_in4systems_adapter.rb | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index d0d9e35..2981500 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.3" + s.version = "1.0.4" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index 73be63e..ef38286 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -60,11 +60,16 @@ def self.sqlanywhere_jdbc_in4systems_connection(config) url = 'jdbc:sqlanywhere:' + connection_string - if ENV["SQLANY12"] || Pathname.new('/opt/sqlanywhere12/java/sajdbc4.jar').exist? - $CLASSPATH << Pathname.new(ENV["SQLANY12"] || '/opt/sqlanywhere12/').join('java').join('sajdbc4.jar').to_s + sa12_directory = Pathname.new(ENV["SQLANY12"] || '/opt/sqlanywhere12/') + sa11_directory = Pathname.new(ENV["SQLANY11"] || '/opt/sqlanywhere11/') + sa12_jar_file = sa12_directory.join('java').join('sajdbc4.jar') + sa11_jar_file = sa11_directory.join('java').join('sajdbc.jar') + + if sa12_jar_file.exist? + $CLASSPATH << sa12_jar_file.to_s driver = 'sybase.jdbc4.sqlanywhere.IDriver' - elsif ENV["SQLANY11"] || Pathname.new('/opt/sqlanywhere11/java/sajdbc.jar').exist? - $CLASSPATH << Pathname.new(ENV["SQLANY11"] || '/opt/sqlanywhere11/').join('java').join('sajdbc.jar').to_s + elsif sa11_jar_file.exist? + $CLASSPATH << sa11_jar_file.to_s driver = 'sybase.jdbc.sqlanywhere.IDriver' else raise "Cannot find SqlAnywhere11 or 12 installation directory" From cf23497a020d1c60182ba1b464cd02d9db8d3490 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Tue, 12 Feb 2013 09:38:38 +0000 Subject: [PATCH 34/47] Hopefully have it find the jar file in a generic manner --- ...rd-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- .../sqlanywhere_jdbc_in4systems_adapter.rb | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 2981500..bf94a25 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.4" + s.version = "1.0.5" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index ef38286..31557ef 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -60,16 +60,13 @@ def self.sqlanywhere_jdbc_in4systems_connection(config) url = 'jdbc:sqlanywhere:' + connection_string - sa12_directory = Pathname.new(ENV["SQLANY12"] || '/opt/sqlanywhere12/') - sa11_directory = Pathname.new(ENV["SQLANY11"] || '/opt/sqlanywhere11/') - sa12_jar_file = sa12_directory.join('java').join('sajdbc4.jar') - sa11_jar_file = sa11_directory.join('java').join('sajdbc.jar') - - if sa12_jar_file.exist? - $CLASSPATH << sa12_jar_file.to_s + if ENV['SQLANY12'] + $CLASSPATH << 'sajdbc4.jar' + $CLASSPATH << Pathname.new(ENV['SQLANY12']).join('java').join('sajdbc4.jar').to_s driver = 'sybase.jdbc4.sqlanywhere.IDriver' - elsif sa11_jar_file.exist? - $CLASSPATH << sa11_jar_file.to_s + elsif ENV['SQLANY11'] + $CLASSPATH << 'sajdbc.jar' + $CLASSPATH << Pathname.new(ENV['SQLANY11']).join('java').join('sajdbc.jar').to_s driver = 'sybase.jdbc.sqlanywhere.IDriver' else raise "Cannot find SqlAnywhere11 or 12 installation directory" From b8e07a2f9709060b31db3e2601077b8d0d5245fa Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Fri, 8 Mar 2013 16:54:41 +0000 Subject: [PATCH 35/47] Remove broken version check in indexes method. --- .../connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index 31557ef..d3bc0b2 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -293,9 +293,6 @@ def columns(table_name, name = nil) #:nodoc: end def indexes(table_name, name = nil) #:nodoc: - if @major_version <= 11 # the sql doesn't work in older databases. - return [] - end sql = "SELECT DISTINCT index_name, \"unique\" FROM SYS.SYSTABLE INNER JOIN SYS.SYSIDXCOL ON SYS.SYSTABLE.table_id = SYS.SYSIDXCOL.table_id INNER JOIN SYS.SYSIDX ON SYS.SYSTABLE.table_id = SYS.SYSIDX.table_id AND SYS.SYSIDXCOL.index_id = SYS.SYSIDX.index_id WHERE table_name = '#{table_name}' AND index_category > 2" select(sql, name).map do |row| index = IndexDefinition.new(table_name, row['index_name']) From 51d5555d77979c16615ebb29d79e806974574eaf Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Fri, 8 Mar 2013 16:55:27 +0000 Subject: [PATCH 36/47] bump version --- activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index bf94a25..6943793 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.5" + s.version = "1.0.6" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} From 2a423aa2a8b3e79ae8b9c9e01a100554ac925adc Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 17 Jun 2013 10:53:38 +0100 Subject: [PATCH 37/47] Clear whitespace on the ends of lines in the adapter code --- .../sqlanywhere_jdbc_in4systems_adapter.rb | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index d3bc0b2..80f36cd 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -6,7 +6,7 @@ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # # http://www.apache.org/licenses/LICENSE-2.0 # @@ -35,13 +35,13 @@ class Base # Main connection function to SQL Anywhere # Connection Adapter takes four parameters: # * :database (required, no default). Corresponds to "DatabaseName=" in connection string - # * :server (optional, defaults to :databse). Corresponds to "ServerName=" in connection string + # * :server (optional, defaults to :databse). Corresponds to "ServerName=" in connection string # * :username (optional, default to 'dba') # * :password (optional, deafult to 'sql') # * :encoding (optional, defaults to charset of OS) # * :commlinks (optional). Corresponds to "CommLinks=" in connection string # * :connection_name (optional). Corresponds to "ConnectionName=" in connection string - + def self.sqlanywhere_jdbc_in4systems_connection(config) if config[:connection_string] @@ -54,7 +54,7 @@ def self.sqlanywhere_jdbc_in4systems_connection(config) connection_string = "ServerName=#{(config[:server] || config[:database])};DatabaseName=#{config[:database]};UserID=#{config[:username]};Password=#{config[:password]};" connection_string += "CommLinks=#{config[:commlinks]};" unless config[:commlinks].nil? connection_string += "ConnectionName=#{config[:connection_name]};" unless config[:connection_name].nil? - connection_string += "CharSet=#{config[:encoding]};" unless config[:encoding].nil? + connection_string += "CharSet=#{config[:encoding]};" unless config[:encoding].nil? connection_string += "Idle=0" # Prevent the server from disconnecting us if we're idle for >240mins (by default) end @@ -93,7 +93,7 @@ def initialize(message, errno, sql) @sql = sql end end - + class SQLAnywhereColumn < Column private # Overridden to handle SQL Anywhere integer, varchar, binary, and timestamp types @@ -114,12 +114,12 @@ def extract_limit(sql_type) case sql_type when /^tinyint/i 1 - when /^smallint/i + when /^smallint/i 2 - when /^integer/i - 4 - when /^bigint/i - 8 + when /^integer/i + 4 + when /^bigint/i + 8 else super end end @@ -131,16 +131,16 @@ def extract_limit(sql_type) def self.string_to_binary(value) "\\x" + value.unpack("H*")[0].scan(/../).join("\\x") end - + def self.binary_to_string(value) value.gsub(/\\x[0-9]{2}/) { |byte| byte[2..3].hex } end - + # Should override the time column values. # Sybase doesn't like the time zones. - + end - + class SQLAnywhereJdbcIn4systemsAdapter < AbstractAdapter delegate :select, :select_rows, :execute, to: :conn @@ -150,10 +150,10 @@ def initialize( conn, logger, connection_string = "") #:nodoc: @visitor = Arel::Visitors::SQLAnywhere.new self @conn = conn end - + def self.visitor_for(pool) config = pool.spec.config - + if config.fetch(:prepared_statements) {true} Arel::Visitors::SQLAnywhere.new pool else @@ -172,7 +172,7 @@ def supports_migrations? #:nodoc: def requires_reloading? true end - + def active? # The liveness variable is used a low-cost "no-op" to test liveness SA.instance.api.sqlany_execute_immediate(@connection, "SET liveness = 1") == 1 @@ -252,7 +252,7 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: when 1 column_type_sql = 'tinyint' when 2 - column_type_sql = 'smallint' + column_type_sql = 'smallint' when 3..4 column_type_sql = 'integer' when 5..8 @@ -265,18 +265,18 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: "varchar (#{limit})" elsif type == :boolean column_type_sql = 'tinyint' - else + else super(type, limit, precision, scale) end else super(type, limit, precision, scale) end end - + def viewed_tables(name = nil) list_of_tables(['view'], name) end - + def base_tables(name = nil) list_of_tables(['base'], name) end @@ -291,13 +291,13 @@ def columns(table_name, name = nil) #:nodoc: SQLAnywhereColumn.new(field['name'], field['default'], field['domain'], (field['nulls'] == 1)) end end - + def indexes(table_name, name = nil) #:nodoc: sql = "SELECT DISTINCT index_name, \"unique\" FROM SYS.SYSTABLE INNER JOIN SYS.SYSIDXCOL ON SYS.SYSTABLE.table_id = SYS.SYSIDXCOL.table_id INNER JOIN SYS.SYSIDX ON SYS.SYSTABLE.table_id = SYS.SYSIDX.table_id AND SYS.SYSIDXCOL.index_id = SYS.SYSIDX.index_id WHERE table_name = '#{table_name}' AND index_category > 2" select(sql, name).map do |row| index = IndexDefinition.new(table_name, row['index_name']) index.unique = row['unique'] == 1 - sql = "SELECT column_name FROM SYS.SYSIDX INNER JOIN SYS.SYSIDXCOL ON SYS.SYSIDXCOL.table_id = SYS.SYSIDX.table_id AND SYS.SYSIDXCOL.index_id = SYS.SYSIDX.index_id INNER JOIN SYS.SYSCOLUMN ON SYS.SYSCOLUMN.table_id = SYS.SYSIDXCOL.table_id AND SYS.SYSCOLUMN.column_id = SYS.SYSIDXCOL.column_id WHERE index_name = '#{row['index_name']}'" + sql = "SELECT column_name FROM SYS.SYSIDX INNER JOIN SYS.SYSIDXCOL ON SYS.SYSIDXCOL.table_id = SYS.SYSIDX.table_id AND SYS.SYSIDXCOL.index_id = SYS.SYSIDX.index_id INNER JOIN SYS.SYSCOLUMN ON SYS.SYSCOLUMN.table_id = SYS.SYSIDXCOL.table_id AND SYS.SYSCOLUMN.column_id = SYS.SYSIDXCOL.column_id WHERE index_name = '#{row['index_name']}'" index.columns = select(sql).map { |col| col['column_name'] } index end @@ -330,15 +330,15 @@ def change_column_null(table_name, column_name, null, default = nil) execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") end execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? '' : 'NOT'} NULL") - end + end - def change_column(table_name, column_name, type, options = {}) #:nodoc: + def change_column(table_name, column_name, type, options = {}) #:nodoc: add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" add_column_options!(add_column_sql, options) add_column_sql << ' NULL' if options[:null] execute(add_column_sql) end - + def rename_column(table_name, column_name, new_column_name) #:nodoc: if column_name.downcase == new_column_name.downcase whine = "if_the_only_change_is_case_sqlanywhere_doesnt_rename_the_column" @@ -357,7 +357,7 @@ def remove_column(table_name, *column_names) WHERE "column_name" = '#{unquoted_column_name}' AND "table_name" = '#{table_name}' SQL select(sql, nil).each do |row| - execute "DROP INDEX \"#{table_name}\".\"#{row['index_name']}\"" + execute "DROP INDEX \"#{table_name}\".\"#{row['index_name']}\"" end execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}" end @@ -385,13 +385,13 @@ def last_inserted_id(result) end protected - + def list_of_tables(types, name = nil) sql = "SELECT table_name FROM SYS.SYSTABLE WHERE table_type in (#{types.map{|t| quote(t)}.join(', ')}) and creator NOT IN (0,3,5)" select(sql, name).map { |row| row["table_name"] } end - # Queries the structure of a table including the columns names, defaults, type, and nullability + # Queries the structure of a table including the columns names, defaults, type, and nullability # ActiveRecord uses the type to parse scale and precision information out of the types. As a result, # chars, varchars, binary, nchars, nvarchars must all be returned in the form type(width) # numeric and decimal must be returned in the form type(width, scale) @@ -400,9 +400,9 @@ def list_of_tables(types, name = nil) def table_structure(table_name) sql = <<-SQL -SELECT SYS.SYSCOLUMN.column_name AS name, +SELECT SYS.SYSCOLUMN.column_name AS name, if left("default",1)='''' then substring("default", 2, length("default")-2) // remove the surrounding quotes - else NULLIF(SYS.SYSCOLUMN."default", 'autoincrement') + else NULLIF(SYS.SYSCOLUMN."default", 'autoincrement') endif AS "default", IF SYS.SYSCOLUMN.domain_id IN (7,8,9,11,33,34,35,3,27) THEN IF SYS.SYSCOLUMN.domain_id IN (3,27) THEN @@ -411,12 +411,12 @@ def table_structure(table_name) SYS.SYSDOMAIN.domain_name || '(' || SYS.SYSCOLUMN.width || ')' ENDIF ELSE - SYS.SYSDOMAIN.domain_name - ENDIF AS domain, + SYS.SYSDOMAIN.domain_name + ENDIF AS domain, IF SYS.SYSCOLUMN.nulls = 'Y' THEN 1 ELSE 0 ENDIF AS nulls -FROM - SYS.SYSCOLUMN - INNER JOIN SYS.SYSTABLE ON SYS.SYSCOLUMN.table_id = SYS.SYSTABLE.table_id +FROM + SYS.SYSCOLUMN + INNER JOIN SYS.SYSTABLE ON SYS.SYSCOLUMN.table_id = SYS.SYSTABLE.table_id INNER JOIN SYS.SYSDOMAIN ON SYS.SYSCOLUMN.domain_id = SYS.SYSDOMAIN.domain_id WHERE SYS.SYSTABLE.creator = 1 AND @@ -426,7 +426,7 @@ def table_structure(table_name) raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure == false structure end - + # Required to prevent DEFAULT NULL being added to primary keys def options_include_default?(options) options.include?(:default) && !(options[:null] == false && options[:default].nil?) From bcd0c20aad11032036ec9f3d7a5e61749c1afd5f Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 17 Jun 2013 11:37:09 +0100 Subject: [PATCH 38/47] Remove whitespace on the ends of the lines in the arel code --- lib/arel/visitors/sqlanywhere.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/arel/visitors/sqlanywhere.rb b/lib/arel/visitors/sqlanywhere.rb index 83dd301..79f7950 100755 --- a/lib/arel/visitors/sqlanywhere.rb +++ b/lib/arel/visitors/sqlanywhere.rb @@ -6,7 +6,7 @@ def visit_Arel_Nodes_SelectStatement o o = order_hacks(o) is_distinct = using_distinct?(o) - + o.limit = 1000000 if (o.offset && !o.limit) o.limit = o.limit.expr if(o.limit.is_a?(Arel::Nodes::Limit)) o.limit = o.limit if(o.limit.is_a?(Fixnum)) @@ -45,15 +45,15 @@ def visit_Arel_Nodes_Group o def visit_Arel_Nodes_Offset o "START AT #{visit(o.expr) + 1}" end - + def visit_Arel_Nodes_True o "1=1" end - + def visit_Arel_Nodes_False o "1=0" end - + def visit_Arel_Nodes_Matches o # The version in arel cannot like integer columns left = visit o.left # This method sets last column @@ -61,14 +61,14 @@ def visit_Arel_Nodes_Matches o self.last_column = nil "#{left} LIKE #{visit o.right}" end - + def using_distinct?(o) o.cores.any? do |core| core.set_quantifier.class == Arel::Nodes::Distinct end - end + end # The functions (order_hacks, split_order_string) are based on the Oracle Enhacned ActiveRecord driver maintained by Raimonds Simanovskis (2010) # (https://github.com/rsim/oracle-enhanced) From b4e4dbf432f22f7696b6b3d887c4770032b22ea5 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 17 Jun 2013 11:41:00 +0100 Subject: [PATCH 39/47] When quoting a table_name stick dba in front if owner isn't given --- .../sqlanywhere_jdbc_in4systems_adapter.rb | 14 +++++++++ lib/arel/visitors/sqlanywhere.rb | 30 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index 80f36cd..715ac77 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -217,6 +217,20 @@ def quote_column_name(name) #:nodoc: %Q("#{name}") end + def quote_table_name(name) + owner, table = name.to_s.split('.', 2) + if table == nil + table = owner + owner = :dba + end + "#{quote_column_name(owner)}.#{quote_column_name(table)}" + end + + def quote_table_alias_name(name) + quote_column_name name + end + + # Handles special quoting of binary columns. Binary columns will be treated as strings inside of ActiveRecord. # ActiveRecord requires that any strings it inserts into databases must escape the backslash (\). # Since in the binary case, the (\x) is significant to SQL Anywhere, it cannot be escaped. diff --git a/lib/arel/visitors/sqlanywhere.rb b/lib/arel/visitors/sqlanywhere.rb index 79f7950..bc49908 100755 --- a/lib/arel/visitors/sqlanywhere.rb +++ b/lib/arel/visitors/sqlanywhere.rb @@ -1,6 +1,11 @@ module Arel module Visitors class SQLAnywhere < Arel::Visitors::ToSql + def initialize connection + super + @quoted_table_aliases = {} + end + private def visit_Arel_Nodes_SelectStatement o o = order_hacks(o) @@ -62,6 +67,31 @@ def visit_Arel_Nodes_Matches o "#{left} LIKE #{visit o.right}" end + def visit_Arel_Table o, a=nil + if o.table_alias + "#{quote_table_name o.name} #{quote_table_alias_name o.table_alias}" + else + quote_table_name o.name + end + end + + def visit_Arel_Attributes_Attribute o, a=nil + if o.relation.table_alias + join_name = o.relation.table_alias + "#{quote_table_alias_name join_name}.#{quote_column_name o.name}" + else + join_name = o.relation.name + "#{quote_table_name join_name}.#{quote_column_name o.name}" + end + end + + def quote_table_alias_name name + return name if Arel::Nodes::SqlLiteral === name + @quoted_table_aliases[name] ||= @connection.quote_table_alias_name(name) + end + + + def using_distinct?(o) From 846b445468f307439a60e2bbde20df333e282980 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 17 Jun 2013 11:41:14 +0100 Subject: [PATCH 40/47] bump to version 1.0.7 --- activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 6943793..56fde09 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.6" + s.version = "1.0.7" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} From b78ed88103d430ab31d315662bb780bc2aea2038 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 17 Jun 2013 17:12:53 +0100 Subject: [PATCH 41/47] Use the quote table alias name method for visit_Arel_Nodes_TableAlias --- lib/arel/visitors/sqlanywhere.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/arel/visitors/sqlanywhere.rb b/lib/arel/visitors/sqlanywhere.rb index bc49908..e1363a4 100755 --- a/lib/arel/visitors/sqlanywhere.rb +++ b/lib/arel/visitors/sqlanywhere.rb @@ -67,6 +67,10 @@ def visit_Arel_Nodes_Matches o "#{left} LIKE #{visit o.right}" end + def visit_Arel_Nodes_TableAlias o + "#{visit o.relation} #{quote_table_alias_name o.name}" + end + def visit_Arel_Table o, a=nil if o.table_alias "#{quote_table_name o.name} #{quote_table_alias_name o.table_alias}" From c6f35695f4a8a1227fe9fdec07dc73507f29de32 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Mon, 17 Jun 2013 17:13:09 +0100 Subject: [PATCH 42/47] Bump the version to 1.0.8 --- activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 56fde09..003629a 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.7" + s.version = "1.0.8" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} From 7779ddfe8a01e30aebf5f019334a43e694eca619 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 26 Sep 2013 11:34:12 +0100 Subject: [PATCH 43/47] Computed values no longer show up as the column default --- ...ord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- .../sqlanywhere_jdbc_in4systems_adapter.rb | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 003629a..65ace64 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.8" + s.version = "1.0.9" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index 715ac77..9ce74fd 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -302,7 +302,15 @@ def tables(name = nil) #:nodoc: def columns(table_name, name = nil) #:nodoc: table_structure(table_name).map do |field| - SQLAnywhereColumn.new(field['name'], field['default'], field['domain'], (field['nulls'] == 1)) + default = field['default'] + if default == nil # Nil is the usual case + elsif default.starts_with?("'") # If string, remove first and last quotes and the last \n character + default = default[1..-2] + elsif default =~ /^-?\d+(\.\d+)?$/ # If a number string, leave as it is + else # Otherwise, it's probably something (LAST USER, CURRENT TIMESTAMP, etc) that wouldn't work in Rails, so return nil + default = nil + end + SQLAnywhereColumn.new(field['name'], default, field['domain'], (field['nulls'] == 1)) end end @@ -415,9 +423,7 @@ def list_of_tables(types, name = nil) def table_structure(table_name) sql = <<-SQL SELECT SYS.SYSCOLUMN.column_name AS name, - if left("default",1)='''' then substring("default", 2, length("default")-2) // remove the surrounding quotes - else NULLIF(SYS.SYSCOLUMN."default", 'autoincrement') - endif AS "default", + "default" AS "default", IF SYS.SYSCOLUMN.domain_id IN (7,8,9,11,33,34,35,3,27) THEN IF SYS.SYSCOLUMN.domain_id IN (3,27) THEN SYS.SYSDOMAIN.domain_name || '(' || SYS.SYSCOLUMN.width || ',' || SYS.SYSCOLUMN.scale || ')' From 9ca004727f17ea663f4a42daf54c75ea97d7e337 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Thu, 31 Oct 2013 10:38:05 +0000 Subject: [PATCH 44/47] Add methods to get and set the current database user --- ...qlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- .../sqlanywhere_jdbc_in4systems_adapter.rb | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 65ace64..567c12f 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.9" + s.version = "1.0.10" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index 9ce74fd..d7ba78f 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -406,6 +406,27 @@ def last_inserted_id(result) select_value('SELECT @@identity') end + def select_user(cache=true) + @user_id = (cache && @user_id) || select_value('SELECT USER') + end + + # Set the database user to be user_id. + # If a block is given, then after running the block, the previous user_id is restored. + def set_user(user_id) + previous_user_id = select_user(true) + if previous_user_id != user_id + execute("SETUSER #{quote_column_name(user_id)}") + @user_id = user_id + end + if block_given? + begin + yield + ensure + set_user(previous_user_id) + end + end + end + protected def list_of_tables(types, name = nil) From 49bf2070328de643306872e72ef1661c1bbaea46 Mon Sep 17 00:00:00 2001 From: in4systems Date: Tue, 3 Dec 2013 10:32:33 +0000 Subject: [PATCH 45/47] added and order by to the column select --- .../connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index d7ba78f..b014b11 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -462,6 +462,7 @@ def table_structure(table_name) WHERE SYS.SYSTABLE.creator = 1 AND table_name = '#{table_name}' +ORDER BY SYS.SYSCOLUMN.colno SQL structure = exec_query(sql, :skip_logging) raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure == false From 7cb9ec0b5865c745d9ae6891d92c048c6cd37ed7 Mon Sep 17 00:00:00 2001 From: Chris Couzens Date: Tue, 27 May 2014 14:11:56 +0100 Subject: [PATCH 46/47] Be more meaningful in how we filter by dba --- activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec | 2 +- .../connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec index 567c12f..d749ee9 100755 --- a/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec +++ b/activerecord-sqlanywhere-jdbc-in4systems-adapter.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = %q{activerecord-sqlanywhere-jdbc-in4systems-adapter} - s.version = "1.0.10" + s.version = "1.0.11" s.authors = ['Eric Farar', 'Chris Couzens'] s.description = %q{ActiveRecord JDBC driver for SQL Anywhere customized for in4systems} diff --git a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb index b014b11..ba88c6b 100755 --- a/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb +++ b/lib/active_record/connection_adapters/sqlanywhere_jdbc_in4systems_adapter.rb @@ -459,10 +459,9 @@ def table_structure(table_name) SYS.SYSCOLUMN INNER JOIN SYS.SYSTABLE ON SYS.SYSCOLUMN.table_id = SYS.SYSTABLE.table_id INNER JOIN SYS.SYSDOMAIN ON SYS.SYSCOLUMN.domain_id = SYS.SYSDOMAIN.domain_id + INNER JOIN sys.sysUser ON sys.systable.creator = sys.sysuser.user_id AND sys.sysuser.user_name = 'dba' WHERE - SYS.SYSTABLE.creator = 1 AND table_name = '#{table_name}' -ORDER BY SYS.SYSCOLUMN.colno SQL structure = exec_query(sql, :skip_logging) raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure == false From b276d048d739d0078edf4ab71a08a6f95161ee10 Mon Sep 17 00:00:00 2001 From: Sri Date: Fri, 13 Feb 2015 16:03:25 +0000 Subject: [PATCH 47/47] Add 16 sqlanywhere --- ...here-jdbc-in4systems-adapter-1.0.13-java.gem | Bin 0 -> 13312 bytes ...-sqlanywhere-jdbc-in4systems-adapter.gemspec | 6 +++--- .../sqlanywhere_jdbc_in4systems_adapter.rb | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 activerecord-sqlanywhere-jdbc-in4systems-adapter-1.0.13-java.gem diff --git a/activerecord-sqlanywhere-jdbc-in4systems-adapter-1.0.13-java.gem b/activerecord-sqlanywhere-jdbc-in4systems-adapter-1.0.13-java.gem new file mode 100644 index 0000000000000000000000000000000000000000..22d8fa554df81c054a947652975773ee34d103b6 GIT binary patch literal 13312 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

sf6PO!8PJ# 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 iArVX-4&l9K