0
-# Author: John Sheets <dev@metacasa.net>
0
+# Author: John R. Sheets
0
+# 01 Mar 2006: Initial version. Based on code from Will Sobel
0
+# (http://dev.rubyonrails.org/ticket/2030)
0
-# 01 Mar 2006: Initial version.
0
# 17 Mar 2006: Added support for migrations; fixed issues with :boolean columns.
0
# 13 Apr 2006: Improved column type support to properly handle dates and user-defined
0
# types; fixed quoting of integer columns.
0
-# Based on code from Will Sobel (http://dev.rubyonrails.org/ticket/2030)
0
+# 05 Jan 2007: Updated for Rails 1.2 release:
0
+# restricted Fixtures#insert_fixtures monkeypatch to Sybase adapter;
0
+# removed SQL type precision from TEXT type to fix broken
0
+# ActiveRecordStore (jburks, #6878); refactored select() to use execute();
0
+# fixed leaked exception for no-op change_column(); removed verbose SQL dump
0
+# from columns(); added missing scale parameter in normalize_type().
0
require 'active_record/connection_adapters/abstract_adapter'
0
@@ -77,7 +84,7 @@ module ActiveRecord
0
class SybaseAdapter < AbstractAdapter # :nodoc:
0
class ColumnWithIdentity < Column
0
- attr_reader :identity
, :primary0
def initialize(name, default, sql_type = nil, nullable = nil, identity = nil, primary = nil)
0
super(name, default, sql_type, nullable)
0
@@ -98,16 +105,6 @@ module ActiveRecord
0
- def self.string_to_time(string)
0
- return string unless string.is_a?(String)
0
- # Since Sybase doesn't handle DATE or TIME, handle it here.
0
- # Populate nil year/month/day with string_to_dummy_time() values.
0
- time_array = ParseDate.parsedate(string)[0..5]
0
- time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
0
- Time.send(Base.default_timezone, *time_array) rescue nil
0
def self.string_to_binary(value)
0
"0x#{value.unpack("H*")[0]}"
0
@@ -148,6 +145,15 @@ module ActiveRecord
0
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
0
+ return super unless type.to_s == 'integer'
0
+ if !limit.nil? && limit < 4
0
@@ -170,14 +176,6 @@ module ActiveRecord
0
- def columns(table_name, name = nil)
0
- table_structure(table_name).inject([]) do |columns, column|
0
- name, default, type, nullable, identity, primary = column
0
- columns << ColumnWithIdentity.new(name, default, type, nullable, identity, primary)
0
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
0
table_name = get_table_name(sql)
0
@@ -212,46 +210,62 @@ module ActiveRecord
0
def execute(sql, name = nil)
0
- @connection.context.reset
0
- @connection.set_rowcount(@limit || 0)
0
- @limit = @offset = nil
0
- @connection.sql_norow(sql)
0
- if @connection.cmd_fail? or @connection.context.failed?
0
- raise "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}"
0
- # Return rows affected
0
+ raw_execute(sql, name)
0
@connection.results[0].row_count
0
- def begin_db_transaction() execute "BEGIN TRAN" end
0
- def commit_db_transaction() execute "COMMIT TRAN" end
0
- def rollback_db_transaction() execute "ROLLBACK TRAN" end
0
+ def begin_db_transaction() raw_execute "BEGIN TRAN" end
0
+ def commit_db_transaction() raw_execute "COMMIT TRAN" end
0
+ def rollback_db_transaction() raw_execute "ROLLBACK TRAN" end
0
select_one("select DB_NAME() as name")["name"]
0
- select("select name from sysobjects where type='U'", name).each do |row|
0
+ select("select name from sysobjects where type='U'", name).map { |row| row['name'] }
0
def indexes(table_name, name = nil)
0
- select("exec sp_helpindex #{table_name}", name).each do |index|
0
+ select("exec sp_helpindex #{table_name}", name).map do |index|
0
unique = index["index_description"] =~ /unique/
0
primary = index["index_description"] =~ /^clustered/
0
cols = index["index_keys"].split(", ").each { |col| col.strip! }
0
-
indexes << IndexDefinition.new(table_name, index["index_name"], unique, cols)
0
+
IndexDefinition.new(table_name, index["index_name"], unique, cols)
0
+ def columns(table_name, name = nil)
0
+SELECT col.name AS name, type.name AS type, col.prec, col.scale,
0
+ col.length, col.status, obj.sysstat2, def.text
0
+ FROM sysobjects obj, syscolumns col, systypes type, syscomments def
0
+ WHERE obj.id = col.id AND col.usertype = type.usertype AND col.cdefault *= def.id
0
+ AND obj.type = 'U' AND obj.name = '#{table_name}' ORDER BY col.colid
0
+ @logger.debug "Get Column Info for table '#{table_name}'" if @logger
0
+ @connection.set_rowcount(0)
0
+ raise "SQL Command for table_structure for #{table_name} failed\nMessage: #{@connection.context.message}" if @connection.context.failed?
0
+ return nil if @connection.cmd_fail?
0
+ @connection.top_row_result.rows.map do |row|
0
+ name, type, prec, scale, length, status, sysstat2, default = row
0
+ type = normalize_type(type, prec, scale, length)
0
+ if default =~ /DEFAULT\s+(.+)/o
0
+ default_value = $1.strip
0
+ default_value = default_value[1...-1] if default_value =~ /^['"]/o
0
+ nullable = (status & 8) == 8
0
+ identity = status >= 128
0
+ primary = (sysstat2 & 8) == 8
0
+ ColumnWithIdentity.new(name, default_value, type, nullable, identity, primary)
0
@@ -302,7 +316,7 @@ module ActiveRecord
0
def add_limit_offset!(sql, options) # :nodoc:
0
@limit = options[:limit]
0
@offset = options[:offset]
0
# Use temp table to hack offset with Sybase
0
sql.sub!(/ FROM /i, ' INTO #artemp FROM ')
0
@@ -318,6 +332,11 @@ module ActiveRecord
0
+ def add_lock!(sql, options) #:nodoc:
0
+ @logger.info "Warning: Sybase :lock option '#{options[:lock].inspect}' not supported" if @logger && options.has_key?(:lock)
0
def supports_migrations? #:nodoc:
0
@@ -334,13 +353,14 @@ module ActiveRecord
0
execute "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
0
rescue StatementInvalid => e
0
- # Swallow exception
if no-op.
0
+ # Swallow exception
and reset context if no-op.
0
raise e unless e.message =~ /no columns to drop, add or modify/
0
+ @connection.context.reset
0
+ if options
.has_key?(:default)0
remove_default_constraint(table_name, column_name)
0
- execute "ALTER TABLE #{table_name} REPLACE #{column_name} DEFAULT #{
options[:default]}"
0
+ execute "ALTER TABLE #{table_name} REPLACE #{column_name} DEFAULT #{
quote options[:default]}"
0
@@ -350,10 +370,10 @@ module ActiveRecord
0
def remove_default_constraint(table_name, column_name)
0
- 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"
0
- defaults.each {|constraint|
0
+ sql = "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"
0
+ select(sql).each do |constraint|
0
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
0
def remove_index(table_name, options = {})
0
@@ -418,34 +438,44 @@ module ActiveRecord
0
- # If limit is not set at all, we can ignore offset;
0
- # if limit *is* set but offset is zero, use normal select
0
- # with simple SET ROWCOUNT. Thus, only use the temp table
0
- # if limit is set and offset > 0.
0
- has_limit = !@limit.nil?
0
- has_offset = !@offset.nil? && @offset > 0
0
- !has_limit || !has_offset
0
+ # If limit is not set at all, we can ignore offset;
0
+ # if limit *is* set but offset is zero, use normal select
0
+ # with simple SET ROWCOUNT. Thus, only use the temp table
0
+ # if limit is set and offset > 0.
0
+ !@limit.nil? && !@offset.nil? && @offset > 0
0
!@limit.nil? && @limit == 0
0
- # Select limit number of rows starting at optional offset.
0
- def select(sql, name = nil)
0
- @connection.context.reset
0
+ def raw_execute(sql, name = nil)
0
- # If limit is not explicitly set, return all results.
0
- @logger.debug "Setting row count to (#{@limit || 'off'})" if @logger
0
- @connection.set_rowcount(@limit || 0)
0
+ @connection.context.reset
0
+ @logger.debug "Setting row count to (#{@limit})" if @logger && @limit
0
+ @connection.set_rowcount(@limit || 0)
0
+ if sql =~ /^\s*SELECT/i
0
+ @connection.sql_norow(sql)
0
+ @limit = @offset = nil
0
+ if @connection.cmd_fail? or @connection.context.failed?
0
+ raise "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}"
0
+ # Select limit number of rows starting at optional offset.
0
+ def select(sql, name = nil)
0
# Select into a temp table and prune results
0
@logger.debug "Selecting #{@limit + (@offset || 0)} or fewer rows into #artemp" if @logger
0
+ @connection.context.reset
0
@connection.set_rowcount(@limit + (@offset || 0))
0
@connection.sql_norow(sql) # Select into temp table
0
@logger.debug "Deleting #{@offset || 0} or fewer rows from #artemp" if @logger
0
@@ -456,23 +486,21 @@ module ActiveRecord
0
+ raise StatementInvalid, "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}" if @connection.context.failed? or @connection.cmd_fail?
0
- if @connection.context.failed? or @connection.cmd_fail?
0
- raise StatementInvalid, "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}"
0
- results = @connection.top_row_result
0
- if results && results.rows.length > 0
0
- fields = results.columns.map { |column| column.sub(/_$/, '') }
0
- results.rows.each do |row|
0
- row.zip(fields) { |cell, column| hashed_row[column] = cell }
0
+ results = @connection.top_row_result
0
+ if results && results.rows.length > 0
0
+ fields = results.columns.map { |column| column.sub(/_$/, '') }
0
+ results.rows.each do |row|
0
+ row.zip(fields) { |cell, column| hashed_row[column] = cell }
0
- @connection.sql_norow("drop table #artemp") if
!normal_select?
0
+ @connection.sql_norow("drop table #artemp") if
use_temp_table?
0
def get_table_name(sql)
0
@@ -490,79 +518,42 @@ module ActiveRecord
0
def get_identity_column(table_name)
0
- @table_columns[table_name] ||= columns(table_name)
0
- @table_columns[table_name].each do |col|
0
- return col.name if col.identity
0
+ if !@id_columns.has_key?(table_name)
0
+ @logger.debug "Looking up identity column for table '#{table_name}'" if @logger
0
+ col = columns(table_name).detect { |col| col.identity }
0
+ @id_columns[table_name] = col.nil? ? nil : col.name
0
+
@id_columns[table_name]0
def query_contains_identity_column(sql, col)
0
- def table_structure(table_name)
0
-SELECT col.name AS name, type.name AS type, col.prec, col.scale, col.length,
0
- col.status, obj.sysstat2, def.text
0
- FROM sysobjects obj, syscolumns col, systypes type, syscomments def
0
- WHERE obj.id = col.id AND col.usertype = type.usertype AND col.cdefault *= def.id
0
- AND obj.type = 'U' AND obj.name = '#{table_name}' ORDER BY col.colid
0
- log(sql, "Get Column Info ") do
0
- @connection.set_rowcount(0)
0
- if @connection.context.failed?
0
- raise "SQL Command for table_structure for #{table_name} failed\nMessage: #{@connection.context.message}"
0
- elsif !@connection.cmd_fail?
0
- results = @connection.top_row_result
0
- results.rows.each do |row|
0
- name, type, prec, scale, length, status, sysstat2, default = row
0
- type = normalize_type(type, prec, scale, length)
0
- if default =~ /DEFAULT\s+(.+)/o
0
- default_value = $1.strip
0
- default_value = default_value[1...-1] if default_value =~ /^['"]/o
0
- nullable = (status & 8) == 8
0
- identity = status >= 128
0
- primary = (sysstat2 & 8) == 8
0
- columns << [name, default_value, type, nullable, identity, primary]
0
# Resolve all user-defined types (udt) to their fundamental types.
0
def resolve_type(field_type)
0
(@udts ||= {})[field_type] ||= select_one("sp_help #{field_type}")["Storage_type"].strip
0
def normalize_type(field_type, prec, scale, length)
0
- if field_type =~ /numeric/i and (scale.nil? or scale == 0)
0
+ has_scale = (!scale.nil? && scale > 0)
0
+ type = if field_type =~ /numeric/i and !has_scale
0
elsif field_type =~ /money/i
0
-
type = resolve_type(field_type.strip)
0
+
resolve_type(field_type.strip)
0
- elsif length && !(type =~ /date|time/)
0
- def default_value(value)
0
+ has_scale ? "(#{prec},#{scale})" : "(#{prec})"
0
+ elsif length && !(type =~ /date|time|text/)
0
end # class SybaseAdapter
0
@@ -654,10 +645,14 @@ class Fixtures
0
alias :original_insert_fixtures :insert_fixtures
0
- values.each do |fixture|
0
- @connection.enable_identity_insert(table_name, true)
0
- @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
0
- @connection.enable_identity_insert(table_name, false)
0
+ if @connection.instance_of?(ActiveRecord::ConnectionAdapters::SybaseAdapter)
0
+ values.each do |fixture|
0
+ @connection.enable_identity_insert(table_name, true)
0
+ @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
0
+ @connection.enable_identity_insert(table_name, false)
0
+ original_insert_fixtures
Comments
No one has commented yet.