mirror of
https://github.com/github/rails.git
synced 2026-04-04 03:00:58 -04:00
Added migrations support to the Sybase adapter (closes #4293) [John R. Sheets]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3905 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
@@ -177,7 +177,7 @@
|
||||
|
||||
* Fixed that calling HasOneProxy#build_model repeatedly would cause saving to happen #4058 [anna@wota.jp]
|
||||
|
||||
* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant, but has the following caveats:
|
||||
* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant (including migrations), but has the following caveats:
|
||||
|
||||
* Does not support DATE SQL column types; use DATETIME instead.
|
||||
* Date columns on HABTM join tables are returned as String, not Time.
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
# Date: 01 Mar 2006
|
||||
#
|
||||
# Based on code from Will Sobel (http://dev.rubyonrails.org/ticket/2030)
|
||||
#
|
||||
# 17 Mar 2006: Added support for migrations; fixed issues with :boolean columns.
|
||||
#
|
||||
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
|
||||
@@ -49,20 +52,34 @@ module ActiveRecord
|
||||
# * <tt>:password</tt> -- Defaults to empty string.
|
||||
#
|
||||
# Usage Notes:
|
||||
# * Does not support DATE SQL column types; use DATETIME instead.
|
||||
# * Date columns on HABTM join tables are returned as String, not Time.
|
||||
# * Insertions are potentially broken for :polymorphic join tables
|
||||
# * BLOB column access not yet fully supported
|
||||
#
|
||||
#
|
||||
# * The sybase-ctlib bindings do not support the DATE SQL column type; use DATETIME instead.
|
||||
# * Table and column names are limited to 30 chars in Sybase 12.5
|
||||
# * :binary columns not yet supported
|
||||
# * :boolean columns use the BIT SQL type, which does not allow nulls or
|
||||
# indexes. If a DEFAULT is not specified for ALTER TABLE commands, the
|
||||
# column will be declared with DEFAULT 0 (false).
|
||||
#
|
||||
# Migrations:
|
||||
#
|
||||
# The Sybase adapter supports migrations, but for ALTER TABLE commands to
|
||||
# work, the database must have the database option 'select into' set to
|
||||
# 'true' with sp_dboption (see below). The sp_helpdb command lists the current
|
||||
# options for all databases.
|
||||
#
|
||||
# 1> use mydb
|
||||
# 2> go
|
||||
# 1> master..sp_dboption mydb, "select into", true
|
||||
# 2> go
|
||||
# 1> checkpoint
|
||||
# 2> go
|
||||
class SybaseAdapter < AbstractAdapter # :nodoc:
|
||||
class ColumnWithIdentity < Column
|
||||
attr_reader :identity, :primary
|
||||
|
||||
def initialize(name, default, sql_type = nil, nullable = nil, identity = nil, primary = nil)
|
||||
super(name, default, sql_type, nullable)
|
||||
@default = type_cast(default)
|
||||
@identity = identity
|
||||
@primary = primary
|
||||
@default, @identity, @primary = type_cast(default), identity, primary
|
||||
end
|
||||
|
||||
def simplified_type(field_type)
|
||||
@@ -102,7 +119,7 @@ module ActiveRecord
|
||||
def native_database_types
|
||||
{
|
||||
:primary_key => "numeric(9,0) IDENTITY PRIMARY KEY",
|
||||
:string => { :name => "varchar", :limit => 255 },
|
||||
:string => { :name => "varchar", :limit => 255 },
|
||||
:text => { :name => "text" },
|
||||
:integer => { :name => "int" },
|
||||
:float => { :name => "float", :limit => 8 },
|
||||
@@ -111,7 +128,7 @@ module ActiveRecord
|
||||
:time => { :name => "time" },
|
||||
:date => { :name => "datetime" },
|
||||
:binary => { :name => "image"},
|
||||
:boolean => { :name => "tinyint", :limit => 1 }
|
||||
:boolean => { :name => "bit" }
|
||||
}
|
||||
end
|
||||
|
||||
@@ -129,7 +146,8 @@ module ActiveRecord
|
||||
|
||||
def reconnect!
|
||||
raise "Sybase Connection Adapter does not yet support reconnect!"
|
||||
#@connection.close rescue nil
|
||||
# disconnect!
|
||||
# connect! # Not yet implemented
|
||||
end
|
||||
|
||||
# Check for a limit statement and parse out the limit and
|
||||
@@ -230,11 +248,6 @@ module ActiveRecord
|
||||
indexes
|
||||
end
|
||||
|
||||
def remove_index(table_name, options = {})
|
||||
# Override for different Sybase SQL syntax.
|
||||
execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
|
||||
end
|
||||
|
||||
def quoted_true
|
||||
"1"
|
||||
end
|
||||
@@ -253,7 +266,7 @@ module ActiveRecord
|
||||
else
|
||||
"'#{quote_string(value)}'"
|
||||
end
|
||||
when NilClass then "NULL"
|
||||
when NilClass then (column && column.type == :boolean) ? '0' : "NULL"
|
||||
when TrueClass then '1'
|
||||
when FalseClass then '0'
|
||||
when Float, Fixnum, Bignum then value.to_s
|
||||
@@ -318,7 +331,70 @@ module ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
def supports_migrations? #:nodoc:
|
||||
true
|
||||
end
|
||||
|
||||
def rename_table(name, new_name)
|
||||
execute "EXEC sp_rename '#{name}', '#{new_name}'"
|
||||
end
|
||||
|
||||
def rename_column(table, column, new_column_name)
|
||||
execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
|
||||
end
|
||||
|
||||
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
||||
sql_commands = ["ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"]
|
||||
if options[:default]
|
||||
remove_default_constraint(table_name, column_name)
|
||||
sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{options[:default]} FOR #{column_name}"
|
||||
end
|
||||
sql_commands.each { |c| execute(c) }
|
||||
end
|
||||
|
||||
def remove_column(table_name, column_name)
|
||||
remove_default_constraint(table_name, column_name)
|
||||
execute "ALTER TABLE #{table_name} DROP #{column_name}"
|
||||
end
|
||||
|
||||
def remove_default_constraint(table_name, column_name)
|
||||
defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
|
||||
defaults.each {|constraint|
|
||||
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
|
||||
}
|
||||
end
|
||||
|
||||
def remove_index(table_name, options = {})
|
||||
execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
|
||||
end
|
||||
|
||||
def add_column_options!(sql, options) #:nodoc:
|
||||
sql << " DEFAULT #{quote(options[:default], options[:column])}" unless options[:default].nil?
|
||||
|
||||
if check_null_for_column?(options[:column], sql)
|
||||
sql << (options[:null] == false ? " NOT NULL" : " NULL")
|
||||
end
|
||||
sql
|
||||
end
|
||||
|
||||
private
|
||||
def check_null_for_column?(col, sql)
|
||||
# Sybase columns are NOT NULL by default, so explicitly set NULL
|
||||
# if :null option is omitted. Disallow NULLs for boolean.
|
||||
type = col.nil? ? "" : col[:type]
|
||||
|
||||
# Ignore :null if a primary key
|
||||
return false if type =~ /PRIMARY KEY/i
|
||||
|
||||
# Ignore :null if a :boolean or BIT column
|
||||
if (sql =~ /\s+bit(\s+DEFAULT)?/i) || type == :boolean
|
||||
# If no default clause found on a boolean column, add one.
|
||||
sql << " DEFAULT 0" if $1.nil?
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# Return the last value of the identity global value.
|
||||
def last_insert_id
|
||||
@connection.sql("SELECT @@IDENTITY")
|
||||
@@ -364,7 +440,7 @@ module ActiveRecord
|
||||
log(sql, name) do
|
||||
if normal_select?
|
||||
# If limit is not explicitly set, return all results.
|
||||
@logger.debug "Setting row count to (#{@limit})" if @logger
|
||||
@logger.debug "Setting row count to (#{@limit || 'off'})" if @logger
|
||||
|
||||
# Run a normal select
|
||||
@connection.set_rowcount(@limit || 0)
|
||||
|
||||
@@ -101,7 +101,7 @@ module ActiveRecord
|
||||
# == Database support
|
||||
#
|
||||
# Migrations are currently supported in MySQL, PostgreSQL, SQLite,
|
||||
# SQL Server, and Oracle (all supported databases except DB2).
|
||||
# SQL Server, Sybase, and Oracle (all supported databases except DB2).
|
||||
#
|
||||
# == More examples
|
||||
#
|
||||
|
||||
@@ -22,9 +22,10 @@ class Test::Unit::TestCase #:nodoc:
|
||||
def assert_date_from_db(expected, actual, message = nil)
|
||||
# SQL Server doesn't have a separate column type just for dates,
|
||||
# so the time is in the string and incorrectly formatted
|
||||
|
||||
if current_adapter?(:SQLServerAdapter) || current_adapter?(:SybaseAdapter)
|
||||
if current_adapter?(:SQLServerAdapter)
|
||||
assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
|
||||
elsif current_adapter?(:SybaseAdapter)
|
||||
assert_equal expected.to_s, actual.to_date.to_s, message
|
||||
else
|
||||
assert_equal expected.to_s, actual.to_s, message
|
||||
end
|
||||
|
||||
@@ -569,7 +569,13 @@ class BasicsTest < Test::Unit::TestCase
|
||||
|
||||
topic = Topic.find(topic.id)
|
||||
assert_nil topic.last_read
|
||||
assert_nil topic.approved
|
||||
|
||||
# Sybase adapter does not allow nulls in boolean columns
|
||||
if current_adapter?(:SybaseAdapter)
|
||||
assert topic.approved == false
|
||||
else
|
||||
assert_nil topic.approved
|
||||
end
|
||||
end
|
||||
|
||||
def test_equality
|
||||
@@ -1177,7 +1183,11 @@ class BasicsTest < Test::Unit::TestCase
|
||||
assert xml.include?(%(<content>Have a nice day</content>))
|
||||
assert xml.include?(%(<author-email-address>david@loudthinking.com</author-email-address>))
|
||||
assert xml.include?(%(<parent-id></parent-id>))
|
||||
assert xml.include?(%(<last-read type="date">2004-04-15</last-read>))
|
||||
if current_adapter?(:SybaseAdapter)
|
||||
assert xml.include?(%(<last-read type="datetime">2004-04-15T00:00:00-05:00</last-read>))
|
||||
else
|
||||
assert xml.include?(%(<last-read type="date">2004-04-15</last-read>))
|
||||
end
|
||||
# Oracle doesn't have true boolean or time-only fields
|
||||
unless current_adapter?(:OracleAdapter)
|
||||
assert xml.include?(%(<approved type="boolean">false</approved>)), "Approved should be a boolean"
|
||||
|
||||
@@ -20,7 +20,7 @@ class BinaryTest < Test::Unit::TestCase
|
||||
# Without using prepared statements, it makes no sense to test
|
||||
# BLOB data with DB2 or Firebird, because the length of a statement
|
||||
# is limited to 32KB.
|
||||
unless %w(SQLServer DB2 Oracle Firebird).include? ActiveRecord::Base.connection.adapter_name
|
||||
unless %w(SQLServer Sybase DB2 Oracle Firebird).include? ActiveRecord::Base.connection.adapter_name
|
||||
def test_load_save
|
||||
bin = Binary.new
|
||||
bin.data = @data
|
||||
|
||||
@@ -29,8 +29,8 @@ CREATE TABLE topics (
|
||||
bonus_time time NULL,
|
||||
last_read datetime NULL,
|
||||
content varchar(255) NULL,
|
||||
approved tinyint default 1 NULL,
|
||||
replies_count int default 0 NULL,
|
||||
approved bit default 1,
|
||||
replies_count int default 0,
|
||||
parent_id int NULL,
|
||||
type varchar(50) NULL
|
||||
)
|
||||
@@ -46,7 +46,7 @@ CREATE TABLE developers (
|
||||
CREATE TABLE projects (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
name varchar(100) NULL,
|
||||
type VARCHAR(255) NULL
|
||||
type varchar(255) NULL
|
||||
)
|
||||
|
||||
CREATE TABLE developers_projects (
|
||||
@@ -85,18 +85,18 @@ CREATE TABLE subscribers (
|
||||
|
||||
CREATE TABLE booleantests (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
value integer NULL
|
||||
value int NULL
|
||||
)
|
||||
|
||||
CREATE TABLE auto_id_tests (
|
||||
auto_id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
value integer NULL
|
||||
value int NULL
|
||||
)
|
||||
|
||||
CREATE TABLE entrants (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
course_id INTEGER NOT NULL
|
||||
name varchar(255) NOT NULL,
|
||||
course_id int NOT NULL
|
||||
)
|
||||
|
||||
CREATE TABLE colnametests (
|
||||
@@ -118,8 +118,8 @@ CREATE TABLE mixins (
|
||||
|
||||
CREATE TABLE people (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
first_name VARCHAR(40) NOT NULL,
|
||||
lock_version INTEGER DEFAULT 0
|
||||
first_name varchar(40) NOT NULL,
|
||||
lock_version int DEFAULT 0
|
||||
)
|
||||
|
||||
CREATE TABLE readers (
|
||||
@@ -135,28 +135,28 @@ CREATE TABLE binaries (
|
||||
|
||||
CREATE TABLE computers (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
developer INTEGER NOT NULL,
|
||||
extendedWarranty INTEGER NOT NULL
|
||||
developer int NOT NULL,
|
||||
extendedWarranty int NOT NULL
|
||||
)
|
||||
|
||||
CREATE TABLE posts (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
author_id INTEGER NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
body VARCHAR(2048) NOT NULL,
|
||||
type VARCHAR(255) NOT NULL
|
||||
author_id int NULL,
|
||||
title varchar(255) NOT NULL,
|
||||
body varchar(2048) NOT NULL,
|
||||
type varchar(255) NOT NULL
|
||||
)
|
||||
|
||||
CREATE TABLE comments (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
post_id INTEGER NOT NULL,
|
||||
body VARCHAR(2048) NOT NULL,
|
||||
type VARCHAR(255) NOT NULL
|
||||
post_id int NOT NULL,
|
||||
body varchar(2048) NOT NULL,
|
||||
type varchar(255) NOT NULL
|
||||
)
|
||||
|
||||
CREATE TABLE authors (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL
|
||||
name varchar(255) NOT NULL
|
||||
)
|
||||
|
||||
CREATE TABLE tasks (
|
||||
@@ -167,8 +167,8 @@ CREATE TABLE tasks (
|
||||
|
||||
CREATE TABLE categories (
|
||||
id numeric(9,0) IDENTITY PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(255) NOT NULL
|
||||
name varchar(255) NOT NULL,
|
||||
type varchar(255) NOT NULL
|
||||
)
|
||||
|
||||
CREATE TABLE categories_posts (
|
||||
|
||||
@@ -54,8 +54,11 @@ if ActiveRecord::Base.connection.supports_migrations?
|
||||
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
|
||||
assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
|
||||
|
||||
assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
|
||||
assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
|
||||
# Sybase adapter does not support indexes on :boolean columns
|
||||
unless current_adapter?(:SybaseAdapter)
|
||||
assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
|
||||
assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
|
||||
end
|
||||
end
|
||||
|
||||
def test_create_table_adds_id
|
||||
@@ -110,10 +113,10 @@ if ActiveRecord::Base.connection.supports_migrations?
|
||||
Person.connection.drop_table :testings rescue nil
|
||||
end
|
||||
|
||||
# SQL Server will not allow you to add a NOT NULL column
|
||||
# SQL Server and Sybase will not allow you to add a NOT NULL column
|
||||
# to a table without specifying a default value, so the
|
||||
# following test must be skipped
|
||||
unless current_adapter?(:SQLServerAdapter)
|
||||
unless current_adapter?(:SQLServerAdapter) || current_adapter?(:SybaseAdapter)
|
||||
def test_add_column_not_null_without_default
|
||||
Person.connection.create_table :testings do |t|
|
||||
t.column :foo, :string
|
||||
@@ -165,8 +168,8 @@ if ActiveRecord::Base.connection.supports_migrations?
|
||||
assert_equal Fixnum, bob.age.class
|
||||
assert_equal Time, bob.birthday.class
|
||||
|
||||
if current_adapter?(:SQLServerAdapter) or current_adapter?(:OracleAdapter)
|
||||
# SQL Server and Oracle don't differentiate between date/time
|
||||
if current_adapter?(:SQLServerAdapter) || current_adapter?(:OracleAdapter) || current_adapter?(:SybaseAdapter)
|
||||
# SQL Server, Sybase, and Oracle don't differentiate between date/time
|
||||
assert_equal Time, bob.favorite_day.class
|
||||
else
|
||||
assert_equal Date, bob.favorite_day.class
|
||||
|
||||
Reference in New Issue
Block a user