activerecord 1.15.6 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +2454 -34
- data/README +1 -1
- data/RUNNING_UNIT_TESTS +3 -34
- data/Rakefile +98 -77
- data/install.rb +1 -1
- data/lib/active_record.rb +13 -22
- data/lib/active_record/aggregations.rb +38 -49
- data/lib/active_record/associations.rb +452 -333
- data/lib/active_record/associations/association_collection.rb +66 -20
- data/lib/active_record/associations/association_proxy.rb +9 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +38 -18
- data/lib/active_record/associations/has_one_association.rb +30 -14
- data/lib/active_record/attribute_methods.rb +253 -0
- data/lib/active_record/base.rb +719 -494
- data/lib/active_record/calculations.rb +62 -63
- data/lib/active_record/callbacks.rb +57 -83
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
- data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
- data/lib/active_record/fixtures.rb +503 -113
- data/lib/active_record/locking/optimistic.rb +72 -34
- data/lib/active_record/migration.rb +80 -57
- data/lib/active_record/observer.rb +13 -10
- data/lib/active_record/query_cache.rb +16 -57
- data/lib/active_record/reflection.rb +35 -38
- data/lib/active_record/schema.rb +5 -5
- data/lib/active_record/schema_dumper.rb +35 -13
- data/lib/active_record/serialization.rb +98 -0
- data/lib/active_record/serializers/json_serializer.rb +71 -0
- data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
- data/lib/active_record/timestamp.rb +20 -21
- data/lib/active_record/transactions.rb +39 -43
- data/lib/active_record/validations.rb +256 -107
- data/lib/active_record/version.rb +3 -3
- data/lib/activerecord.rb +1 -0
- data/test/aaa_create_tables_test.rb +15 -2
- data/test/abstract_unit.rb +24 -17
- data/test/active_schema_test_mysql.rb +20 -8
- data/test/adapter_test.rb +23 -5
- data/test/adapter_test_sqlserver.rb +15 -1
- data/test/aggregations_test.rb +16 -1
- data/test/all.sh +2 -2
- data/test/associations/ar_joins_test.rb +0 -0
- data/test/associations/callbacks_test.rb +51 -30
- data/test/associations/cascaded_eager_loading_test.rb +1 -29
- data/test/associations/eager_singularization_test.rb +145 -0
- data/test/associations/eager_test.rb +42 -6
- data/test/associations/extension_test.rb +6 -1
- data/test/associations/inner_join_association_test.rb +88 -0
- data/test/associations/join_model_test.rb +47 -16
- data/test/associations_test.rb +449 -226
- data/test/attribute_methods_test.rb +97 -0
- data/test/base_test.rb +251 -105
- data/test/binary_test.rb +22 -27
- data/test/calculations_test.rb +37 -5
- data/test/callbacks_test.rb +23 -0
- data/test/connection_test_firebird.rb +2 -2
- data/test/connection_test_mysql.rb +30 -0
- data/test/connections/native_mysql/connection.rb +3 -0
- data/test/connections/native_sqlite/connection.rb +5 -14
- data/test/connections/native_sqlite3/connection.rb +5 -14
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
- data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
- data/test/datatype_test_postgresql.rb +178 -27
- data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
- data/test/defaults_test.rb +8 -1
- data/test/deprecated_finder_test.rb +7 -128
- data/test/finder_test.rb +192 -54
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author.rb +12 -5
- data/test/fixtures/binaries.yml +130 -435
- data/test/fixtures/category.rb +6 -0
- data/test/fixtures/company.rb +8 -1
- data/test/fixtures/computer.rb +1 -0
- data/test/fixtures/contact.rb +16 -0
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +4 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
- data/test/fixtures/db_definitions/firebird.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase.sql +5 -0
- data/test/fixtures/db_definitions/openbase.sql +41 -25
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +5 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
- data/test/fixtures/db_definitions/postgresql.sql +87 -58
- data/test/fixtures/db_definitions/postgresql2.sql +1 -2
- data/test/fixtures/db_definitions/schema.rb +280 -0
- data/test/fixtures/db_definitions/schema2.rb +11 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +4 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
- data/test/fixtures/db_definitions/sybase.sql +4 -0
- data/test/fixtures/developer.rb +10 -0
- data/test/fixtures/example.log +1 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/item.rb +7 -0
- data/test/fixtures/items.yml +4 -0
- data/test/fixtures/joke.rb +0 -3
- data/test/fixtures/matey.rb +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/minimalistic.rb +2 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/mixins.yml +2 -100
- data/test/fixtures/parrot.rb +13 -0
- data/test/fixtures/parrots.yml +27 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/pirate.rb +5 -0
- data/test/fixtures/pirates.yml +9 -0
- data/test/fixtures/post.rb +1 -0
- data/test/fixtures/project.rb +3 -2
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ship.rb +3 -0
- data/test/fixtures/ships.yml +5 -0
- data/test/fixtures/tagging.rb +4 -0
- data/test/fixtures/taggings.yml +8 -1
- data/test/fixtures/topic.rb +13 -1
- data/test/fixtures/treasure.rb +4 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures_test.rb +205 -24
- data/test/inheritance_test.rb +7 -1
- data/test/json_serialization_test.rb +180 -0
- data/test/lifecycle_test.rb +1 -1
- data/test/locking_test.rb +85 -2
- data/test/migration_test.rb +206 -40
- data/test/mixin_test.rb +13 -515
- data/test/pk_test.rb +3 -6
- data/test/query_cache_test.rb +104 -0
- data/test/reflection_test.rb +16 -0
- data/test/reserved_word_test_mysql.rb +177 -0
- data/test/schema_dumper_test.rb +38 -3
- data/test/serialization_test.rb +47 -0
- data/test/transactions_test.rb +74 -23
- data/test/unconnected_test.rb +1 -1
- data/test/validations_test.rb +322 -32
- data/test/xml_serialization_test.rb +121 -44
- metadata +48 -41
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -85
- data/lib/active_record/acts/list.rb +0 -256
- data/lib/active_record/acts/nested_set.rb +0 -211
- data/lib/active_record/acts/tree.rb +0 -96
- data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
- data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
- data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
- data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
- data/lib/active_record/deprecated_associations.rb +0 -104
- data/lib/active_record/deprecated_finders.rb +0 -44
- data/lib/active_record/vendor/simple.rb +0 -693
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -58
- data/test/connections/native_sqlserver/connection.rb +0 -23
- data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
- data/test/deprecated_associations_test.rb +0 -396
- data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
- data/test/fixtures/db_definitions/mysql.sql +0 -234
- data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
- data/test/fixtures/db_definitions/mysql2.sql +0 -5
- data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
- data/test/fixtures/db_definitions/sqlserver.sql +0 -243
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
- data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
- data/test/fixtures/mixin.rb +0 -63
- data/test/mixin_nested_set_test.rb +0 -196
@@ -1,662 +0,0 @@
|
|
1
|
-
# sybase_adaptor.rb
|
2
|
-
# Author: John R. Sheets
|
3
|
-
#
|
4
|
-
# 01 Mar 2006: Initial version. Based on code from Will Sobel
|
5
|
-
# (http://dev.rubyonrails.org/ticket/2030)
|
6
|
-
#
|
7
|
-
# 17 Mar 2006: Added support for migrations; fixed issues with :boolean columns.
|
8
|
-
#
|
9
|
-
# 13 Apr 2006: Improved column type support to properly handle dates and user-defined
|
10
|
-
# types; fixed quoting of integer columns.
|
11
|
-
#
|
12
|
-
# 05 Jan 2007: Updated for Rails 1.2 release:
|
13
|
-
# restricted Fixtures#insert_fixtures monkeypatch to Sybase adapter;
|
14
|
-
# removed SQL type precision from TEXT type to fix broken
|
15
|
-
# ActiveRecordStore (jburks, #6878); refactored select() to use execute();
|
16
|
-
# fixed leaked exception for no-op change_column(); removed verbose SQL dump
|
17
|
-
# from columns(); added missing scale parameter in normalize_type().
|
18
|
-
|
19
|
-
require 'active_record/connection_adapters/abstract_adapter'
|
20
|
-
|
21
|
-
begin
|
22
|
-
require 'sybsql'
|
23
|
-
|
24
|
-
module ActiveRecord
|
25
|
-
class Base
|
26
|
-
# Establishes a connection to the database that's used by all Active Record objects
|
27
|
-
def self.sybase_connection(config) # :nodoc:
|
28
|
-
config = config.symbolize_keys
|
29
|
-
|
30
|
-
username = config[:username] ? config[:username].to_s : 'sa'
|
31
|
-
password = config[:password] ? config[:password].to_s : ''
|
32
|
-
|
33
|
-
if config.has_key?(:host)
|
34
|
-
host = config[:host]
|
35
|
-
else
|
36
|
-
raise ArgumentError, "No database server name specified. Missing argument: host."
|
37
|
-
end
|
38
|
-
|
39
|
-
if config.has_key?(:database)
|
40
|
-
database = config[:database]
|
41
|
-
else
|
42
|
-
raise ArgumentError, "No database specified. Missing argument: database."
|
43
|
-
end
|
44
|
-
|
45
|
-
ConnectionAdapters::SybaseAdapter.new(
|
46
|
-
SybSQL.new({'S' => host, 'U' => username, 'P' => password},
|
47
|
-
ConnectionAdapters::SybaseAdapterContext), database, config, logger)
|
48
|
-
end
|
49
|
-
end # class Base
|
50
|
-
|
51
|
-
module ConnectionAdapters
|
52
|
-
|
53
|
-
# ActiveRecord connection adapter for Sybase Open Client bindings
|
54
|
-
# (see http://raa.ruby-lang.org/project/sybase-ctlib).
|
55
|
-
#
|
56
|
-
# Options:
|
57
|
-
#
|
58
|
-
# * <tt>:host</tt> -- The name of the database server. No default, must be provided.
|
59
|
-
# * <tt>:database</tt> -- The name of the database. No default, must be provided.
|
60
|
-
# * <tt>:username</tt> -- Defaults to "sa".
|
61
|
-
# * <tt>:password</tt> -- Defaults to empty string.
|
62
|
-
#
|
63
|
-
# Usage Notes:
|
64
|
-
#
|
65
|
-
# * The sybase-ctlib bindings do not support the DATE SQL column type; use DATETIME instead.
|
66
|
-
# * Table and column names are limited to 30 chars in Sybase 12.5
|
67
|
-
# * :binary columns not yet supported
|
68
|
-
# * :boolean columns use the BIT SQL type, which does not allow nulls or
|
69
|
-
# indexes. If a DEFAULT is not specified for ALTER TABLE commands, the
|
70
|
-
# column will be declared with DEFAULT 0 (false).
|
71
|
-
#
|
72
|
-
# Migrations:
|
73
|
-
#
|
74
|
-
# The Sybase adapter supports migrations, but for ALTER TABLE commands to
|
75
|
-
# work, the database must have the database option 'select into' set to
|
76
|
-
# 'true' with sp_dboption (see below). The sp_helpdb command lists the current
|
77
|
-
# options for all databases.
|
78
|
-
#
|
79
|
-
# 1> use mydb
|
80
|
-
# 2> go
|
81
|
-
# 1> master..sp_dboption mydb, "select into", true
|
82
|
-
# 2> go
|
83
|
-
# 1> checkpoint
|
84
|
-
# 2> go
|
85
|
-
class SybaseAdapter < AbstractAdapter # :nodoc:
|
86
|
-
class ColumnWithIdentity < Column
|
87
|
-
attr_reader :identity
|
88
|
-
|
89
|
-
def initialize(name, default, sql_type = nil, nullable = nil, identity = nil, primary = nil)
|
90
|
-
super(name, default, sql_type, nullable)
|
91
|
-
@default, @identity, @primary = type_cast(default), identity, primary
|
92
|
-
end
|
93
|
-
|
94
|
-
def simplified_type(field_type)
|
95
|
-
case field_type
|
96
|
-
when /int|bigint|smallint|tinyint/i then :integer
|
97
|
-
when /float|double|real/i then :float
|
98
|
-
when /decimal|money|numeric|smallmoney/i then :decimal
|
99
|
-
when /text|ntext/i then :text
|
100
|
-
when /binary|image|varbinary/i then :binary
|
101
|
-
when /char|nchar|nvarchar|string|varchar/i then :string
|
102
|
-
when /bit/i then :boolean
|
103
|
-
when /datetime|smalldatetime/i then :datetime
|
104
|
-
else super
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def self.string_to_binary(value)
|
109
|
-
"0x#{value.unpack("H*")[0]}"
|
110
|
-
end
|
111
|
-
|
112
|
-
def self.binary_to_string(value)
|
113
|
-
# FIXME: sybase-ctlib uses separate sql method for binary columns.
|
114
|
-
value
|
115
|
-
end
|
116
|
-
end # class ColumnWithIdentity
|
117
|
-
|
118
|
-
# Sybase adapter
|
119
|
-
def initialize(connection, database, config = {}, logger = nil)
|
120
|
-
super(connection, logger)
|
121
|
-
context = connection.context
|
122
|
-
context.init(logger)
|
123
|
-
@config = config
|
124
|
-
@numconvert = config.has_key?(:numconvert) ? config[:numconvert] : true
|
125
|
-
@limit = @offset = 0
|
126
|
-
unless connection.sql_norow("USE #{database}")
|
127
|
-
raise "Cannot USE #{database}"
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def native_database_types
|
132
|
-
{
|
133
|
-
:primary_key => "numeric(9,0) IDENTITY PRIMARY KEY",
|
134
|
-
:string => { :name => "varchar", :limit => 255 },
|
135
|
-
:text => { :name => "text" },
|
136
|
-
:integer => { :name => "int" },
|
137
|
-
:float => { :name => "float", :limit => 8 },
|
138
|
-
:decimal => { :name => "decimal" },
|
139
|
-
:datetime => { :name => "datetime" },
|
140
|
-
:timestamp => { :name => "timestamp" },
|
141
|
-
:time => { :name => "time" },
|
142
|
-
:date => { :name => "datetime" },
|
143
|
-
:binary => { :name => "image"},
|
144
|
-
:boolean => { :name => "bit" }
|
145
|
-
}
|
146
|
-
end
|
147
|
-
|
148
|
-
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
149
|
-
return super unless type.to_s == 'integer'
|
150
|
-
if !limit.nil? && limit < 4
|
151
|
-
'smallint'
|
152
|
-
else
|
153
|
-
'integer'
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def adapter_name
|
158
|
-
'Sybase'
|
159
|
-
end
|
160
|
-
|
161
|
-
def active?
|
162
|
-
!(@connection.connection.nil? || @connection.connection_dead?)
|
163
|
-
end
|
164
|
-
|
165
|
-
def disconnect!
|
166
|
-
@connection.close rescue nil
|
167
|
-
end
|
168
|
-
|
169
|
-
def reconnect!
|
170
|
-
raise "Sybase Connection Adapter does not yet support reconnect!"
|
171
|
-
# disconnect!
|
172
|
-
# connect! # Not yet implemented
|
173
|
-
end
|
174
|
-
|
175
|
-
def table_alias_length
|
176
|
-
30
|
177
|
-
end
|
178
|
-
|
179
|
-
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
180
|
-
begin
|
181
|
-
table_name = get_table_name(sql)
|
182
|
-
col = get_identity_column(table_name)
|
183
|
-
ii_enabled = false
|
184
|
-
|
185
|
-
if col != nil
|
186
|
-
if query_contains_identity_column(sql, col)
|
187
|
-
begin
|
188
|
-
enable_identity_insert(table_name, true)
|
189
|
-
ii_enabled = true
|
190
|
-
rescue Exception => e
|
191
|
-
raise ActiveRecordError, "IDENTITY_INSERT could not be turned ON"
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
log(sql, name) do
|
197
|
-
execute(sql, name)
|
198
|
-
ident = select_one("SELECT @@IDENTITY AS last_id")["last_id"]
|
199
|
-
id_value || ident
|
200
|
-
end
|
201
|
-
ensure
|
202
|
-
if ii_enabled
|
203
|
-
begin
|
204
|
-
enable_identity_insert(table_name, false)
|
205
|
-
rescue Exception => e
|
206
|
-
raise ActiveRecordError, "IDENTITY_INSERT could not be turned OFF"
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def execute(sql, name = nil)
|
213
|
-
raw_execute(sql, name)
|
214
|
-
@connection.results[0].row_count
|
215
|
-
end
|
216
|
-
|
217
|
-
def begin_db_transaction() raw_execute "BEGIN TRAN" end
|
218
|
-
def commit_db_transaction() raw_execute "COMMIT TRAN" end
|
219
|
-
def rollback_db_transaction() raw_execute "ROLLBACK TRAN" end
|
220
|
-
|
221
|
-
def current_database
|
222
|
-
select_one("select DB_NAME() as name")["name"]
|
223
|
-
end
|
224
|
-
|
225
|
-
def tables(name = nil)
|
226
|
-
select("select name from sysobjects where type='U'", name).map { |row| row['name'] }
|
227
|
-
end
|
228
|
-
|
229
|
-
def indexes(table_name, name = nil)
|
230
|
-
select("exec sp_helpindex #{table_name}", name).map do |index|
|
231
|
-
unique = index["index_description"] =~ /unique/
|
232
|
-
primary = index["index_description"] =~ /^clustered/
|
233
|
-
if !primary
|
234
|
-
cols = index["index_keys"].split(", ").each { |col| col.strip! }
|
235
|
-
IndexDefinition.new(table_name, index["index_name"], unique, cols)
|
236
|
-
end
|
237
|
-
end.compact
|
238
|
-
end
|
239
|
-
|
240
|
-
def columns(table_name, name = nil)
|
241
|
-
sql = <<SQLTEXT
|
242
|
-
SELECT col.name AS name, type.name AS type, col.prec, col.scale,
|
243
|
-
col.length, col.status, obj.sysstat2, def.text
|
244
|
-
FROM sysobjects obj, syscolumns col, systypes type, syscomments def
|
245
|
-
WHERE obj.id = col.id AND col.usertype = type.usertype AND col.cdefault *= def.id
|
246
|
-
AND obj.type = 'U' AND obj.name = '#{table_name}' ORDER BY col.colid
|
247
|
-
SQLTEXT
|
248
|
-
@logger.debug "Get Column Info for table '#{table_name}'" if @logger
|
249
|
-
@connection.set_rowcount(0)
|
250
|
-
@connection.sql(sql)
|
251
|
-
|
252
|
-
raise "SQL Command for table_structure for #{table_name} failed\nMessage: #{@connection.context.message}" if @connection.context.failed?
|
253
|
-
return nil if @connection.cmd_fail?
|
254
|
-
|
255
|
-
@connection.top_row_result.rows.map do |row|
|
256
|
-
name, type, prec, scale, length, status, sysstat2, default = row
|
257
|
-
name.sub!(/_$/o, '')
|
258
|
-
type = normalize_type(type, prec, scale, length)
|
259
|
-
default_value = nil
|
260
|
-
if default =~ /DEFAULT\s+(.+)/o
|
261
|
-
default_value = $1.strip
|
262
|
-
default_value = default_value[1...-1] if default_value =~ /^['"]/o
|
263
|
-
end
|
264
|
-
nullable = (status & 8) == 8
|
265
|
-
identity = status >= 128
|
266
|
-
primary = (sysstat2 & 8) == 8
|
267
|
-
ColumnWithIdentity.new(name, default_value, type, nullable, identity, primary)
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
def quoted_true
|
272
|
-
"1"
|
273
|
-
end
|
274
|
-
|
275
|
-
def quoted_false
|
276
|
-
"0"
|
277
|
-
end
|
278
|
-
|
279
|
-
def quote(value, column = nil)
|
280
|
-
return value.quoted_id if value.respond_to?(:quoted_id)
|
281
|
-
|
282
|
-
case value
|
283
|
-
when String
|
284
|
-
if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
285
|
-
"#{quote_string(column.class.string_to_binary(value))}"
|
286
|
-
elsif @numconvert && force_numeric?(column) && value =~ /^[+-]?[0-9]+$/o
|
287
|
-
value
|
288
|
-
else
|
289
|
-
"'#{quote_string(value)}'"
|
290
|
-
end
|
291
|
-
when NilClass then (column && column.type == :boolean) ? '0' : "NULL"
|
292
|
-
when TrueClass then '1'
|
293
|
-
when FalseClass then '0'
|
294
|
-
when Float, Fixnum, Bignum then force_numeric?(column) ? value.to_s : "'#{value.to_s}'"
|
295
|
-
when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
296
|
-
else super
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
# True if column is explicitly declared non-numeric, or
|
301
|
-
# if column is nil (not specified).
|
302
|
-
def force_numeric?(column)
|
303
|
-
(column.nil? || [:integer, :float, :decimal].include?(column.type))
|
304
|
-
end
|
305
|
-
|
306
|
-
def quote_string(s)
|
307
|
-
s.gsub(/'/, "''") # ' (for ruby-mode)
|
308
|
-
end
|
309
|
-
|
310
|
-
def quote_column_name(name)
|
311
|
-
# If column name is close to max length, skip the quotes, since they
|
312
|
-
# seem to count as part of the length.
|
313
|
-
((name.to_s.length + 2) <= table_alias_length) ? "[#{name}]" : name.to_s
|
314
|
-
end
|
315
|
-
|
316
|
-
def add_limit_offset!(sql, options) # :nodoc:
|
317
|
-
@limit = options[:limit]
|
318
|
-
@offset = options[:offset]
|
319
|
-
if use_temp_table?
|
320
|
-
# Use temp table to hack offset with Sybase
|
321
|
-
sql.sub!(/ FROM /i, ' INTO #artemp FROM ')
|
322
|
-
elsif zero_limit?
|
323
|
-
# "SET ROWCOUNT 0" turns off limits, so we have
|
324
|
-
# to use a cheap trick.
|
325
|
-
if sql =~ /WHERE/i
|
326
|
-
sql.sub!(/WHERE/i, 'WHERE 1 = 2 AND ')
|
327
|
-
elsif sql =~ /ORDER\s+BY/i
|
328
|
-
sql.sub!(/ORDER\s+BY/i, 'WHERE 1 = 2 ORDER BY')
|
329
|
-
else
|
330
|
-
sql << 'WHERE 1 = 2'
|
331
|
-
end
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
def add_lock!(sql, options) #:nodoc:
|
336
|
-
@logger.info "Warning: Sybase :lock option '#{options[:lock].inspect}' not supported" if @logger && options.has_key?(:lock)
|
337
|
-
sql
|
338
|
-
end
|
339
|
-
|
340
|
-
def supports_migrations? #:nodoc:
|
341
|
-
true
|
342
|
-
end
|
343
|
-
|
344
|
-
def rename_table(name, new_name)
|
345
|
-
execute "EXEC sp_rename '#{name}', '#{new_name}'"
|
346
|
-
end
|
347
|
-
|
348
|
-
def rename_column(table, column, new_column_name)
|
349
|
-
execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
|
350
|
-
end
|
351
|
-
|
352
|
-
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
353
|
-
begin
|
354
|
-
execute "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
|
355
|
-
rescue StatementInvalid => e
|
356
|
-
# Swallow exception and reset context if no-op.
|
357
|
-
raise e unless e.message =~ /no columns to drop, add or modify/
|
358
|
-
@connection.context.reset
|
359
|
-
end
|
360
|
-
|
361
|
-
if options.has_key?(:default)
|
362
|
-
remove_default_constraint(table_name, column_name)
|
363
|
-
execute "ALTER TABLE #{table_name} REPLACE #{column_name} DEFAULT #{quote options[:default]}"
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
def remove_column(table_name, column_name)
|
368
|
-
remove_default_constraint(table_name, column_name)
|
369
|
-
execute "ALTER TABLE #{table_name} DROP #{column_name}"
|
370
|
-
end
|
371
|
-
|
372
|
-
def remove_default_constraint(table_name, column_name)
|
373
|
-
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"
|
374
|
-
select(sql).each do |constraint|
|
375
|
-
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
def remove_index(table_name, options = {})
|
380
|
-
execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
|
381
|
-
end
|
382
|
-
|
383
|
-
def add_column_options!(sql, options) #:nodoc:
|
384
|
-
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
|
385
|
-
|
386
|
-
if check_null_for_column?(options[:column], sql)
|
387
|
-
sql << (options[:null] == false ? " NOT NULL" : " NULL")
|
388
|
-
end
|
389
|
-
sql
|
390
|
-
end
|
391
|
-
|
392
|
-
def enable_identity_insert(table_name, enable = true)
|
393
|
-
if has_identity_column(table_name)
|
394
|
-
execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
private
|
399
|
-
def check_null_for_column?(col, sql)
|
400
|
-
# Sybase columns are NOT NULL by default, so explicitly set NULL
|
401
|
-
# if :null option is omitted. Disallow NULLs for boolean.
|
402
|
-
type = col.nil? ? "" : col[:type]
|
403
|
-
|
404
|
-
# Ignore :null if a primary key
|
405
|
-
return false if type =~ /PRIMARY KEY/i
|
406
|
-
|
407
|
-
# Ignore :null if a :boolean or BIT column
|
408
|
-
if (sql =~ /\s+bit(\s+DEFAULT)?/i) || type == :boolean
|
409
|
-
# If no default clause found on a boolean column, add one.
|
410
|
-
sql << " DEFAULT 0" if $1.nil?
|
411
|
-
return false
|
412
|
-
end
|
413
|
-
true
|
414
|
-
end
|
415
|
-
|
416
|
-
# Return the last value of the identity global value.
|
417
|
-
def last_insert_id
|
418
|
-
@connection.sql("SELECT @@IDENTITY")
|
419
|
-
unless @connection.cmd_fail?
|
420
|
-
id = @connection.top_row_result.rows.first.first
|
421
|
-
if id
|
422
|
-
id = id.to_i
|
423
|
-
id = nil if id == 0
|
424
|
-
end
|
425
|
-
else
|
426
|
-
id = nil
|
427
|
-
end
|
428
|
-
id
|
429
|
-
end
|
430
|
-
|
431
|
-
def affected_rows(name = nil)
|
432
|
-
@connection.sql("SELECT @@ROWCOUNT")
|
433
|
-
unless @connection.cmd_fail?
|
434
|
-
count = @connection.top_row_result.rows.first.first
|
435
|
-
count = count.to_i if count
|
436
|
-
else
|
437
|
-
0
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
# If limit is not set at all, we can ignore offset;
|
442
|
-
# if limit *is* set but offset is zero, use normal select
|
443
|
-
# with simple SET ROWCOUNT. Thus, only use the temp table
|
444
|
-
# if limit is set and offset > 0.
|
445
|
-
def use_temp_table?
|
446
|
-
!@limit.nil? && !@offset.nil? && @offset > 0
|
447
|
-
end
|
448
|
-
|
449
|
-
def zero_limit?
|
450
|
-
!@limit.nil? && @limit == 0
|
451
|
-
end
|
452
|
-
|
453
|
-
def raw_execute(sql, name = nil)
|
454
|
-
log(sql, name) do
|
455
|
-
@connection.context.reset
|
456
|
-
@logger.debug "Setting row count to (#{@limit})" if @logger && @limit
|
457
|
-
@connection.set_rowcount(@limit || 0)
|
458
|
-
if sql =~ /^\s*SELECT/i
|
459
|
-
@connection.sql(sql)
|
460
|
-
else
|
461
|
-
@connection.sql_norow(sql)
|
462
|
-
end
|
463
|
-
@limit = @offset = nil
|
464
|
-
if @connection.cmd_fail? or @connection.context.failed?
|
465
|
-
raise "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}"
|
466
|
-
end
|
467
|
-
end
|
468
|
-
end
|
469
|
-
|
470
|
-
# Select limit number of rows starting at optional offset.
|
471
|
-
def select(sql, name = nil)
|
472
|
-
if !use_temp_table?
|
473
|
-
execute(sql, name)
|
474
|
-
else
|
475
|
-
log(sql, name) do
|
476
|
-
# Select into a temp table and prune results
|
477
|
-
@logger.debug "Selecting #{@limit + (@offset || 0)} or fewer rows into #artemp" if @logger
|
478
|
-
@connection.context.reset
|
479
|
-
@connection.set_rowcount(@limit + (@offset || 0))
|
480
|
-
@connection.sql_norow(sql) # Select into temp table
|
481
|
-
@logger.debug "Deleting #{@offset || 0} or fewer rows from #artemp" if @logger
|
482
|
-
@connection.set_rowcount(@offset || 0)
|
483
|
-
@connection.sql_norow("delete from #artemp") # Delete leading rows
|
484
|
-
@connection.set_rowcount(0)
|
485
|
-
@connection.sql("select * from #artemp") # Return the rest
|
486
|
-
end
|
487
|
-
end
|
488
|
-
|
489
|
-
raise StatementInvalid, "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}" if @connection.context.failed? or @connection.cmd_fail?
|
490
|
-
|
491
|
-
rows = []
|
492
|
-
results = @connection.top_row_result
|
493
|
-
if results && results.rows.length > 0
|
494
|
-
fields = results.columns.map { |column| column.sub(/_$/, '') }
|
495
|
-
results.rows.each do |row|
|
496
|
-
hashed_row = {}
|
497
|
-
row.zip(fields) { |cell, column| hashed_row[column] = cell }
|
498
|
-
rows << hashed_row
|
499
|
-
end
|
500
|
-
end
|
501
|
-
@connection.sql_norow("drop table #artemp") if use_temp_table?
|
502
|
-
@limit = @offset = nil
|
503
|
-
rows
|
504
|
-
end
|
505
|
-
|
506
|
-
def get_table_name(sql)
|
507
|
-
if sql =~ /^\s*insert\s+into\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
|
508
|
-
$1
|
509
|
-
elsif sql =~ /from\s+([^\(\s]+)\s*/i
|
510
|
-
$1
|
511
|
-
else
|
512
|
-
nil
|
513
|
-
end
|
514
|
-
end
|
515
|
-
|
516
|
-
def has_identity_column(table_name)
|
517
|
-
!get_identity_column(table_name).nil?
|
518
|
-
end
|
519
|
-
|
520
|
-
def get_identity_column(table_name)
|
521
|
-
@id_columns ||= {}
|
522
|
-
if !@id_columns.has_key?(table_name)
|
523
|
-
@logger.debug "Looking up identity column for table '#{table_name}'" if @logger
|
524
|
-
col = columns(table_name).detect { |col| col.identity }
|
525
|
-
@id_columns[table_name] = col.nil? ? nil : col.name
|
526
|
-
end
|
527
|
-
@id_columns[table_name]
|
528
|
-
end
|
529
|
-
|
530
|
-
def query_contains_identity_column(sql, col)
|
531
|
-
sql =~ /\[#{col}\]/
|
532
|
-
end
|
533
|
-
|
534
|
-
# Resolve all user-defined types (udt) to their fundamental types.
|
535
|
-
def resolve_type(field_type)
|
536
|
-
(@udts ||= {})[field_type] ||= select_one("sp_help #{field_type}")["Storage_type"].strip
|
537
|
-
end
|
538
|
-
|
539
|
-
def normalize_type(field_type, prec, scale, length)
|
540
|
-
has_scale = (!scale.nil? && scale > 0)
|
541
|
-
type = if field_type =~ /numeric/i and !has_scale
|
542
|
-
'int'
|
543
|
-
elsif field_type =~ /money/i
|
544
|
-
'numeric'
|
545
|
-
else
|
546
|
-
resolve_type(field_type.strip)
|
547
|
-
end
|
548
|
-
|
549
|
-
spec = if prec
|
550
|
-
has_scale ? "(#{prec},#{scale})" : "(#{prec})"
|
551
|
-
elsif length && !(type =~ /date|time|text/)
|
552
|
-
"(#{length})"
|
553
|
-
else
|
554
|
-
''
|
555
|
-
end
|
556
|
-
"#{type}#{spec}"
|
557
|
-
end
|
558
|
-
end # class SybaseAdapter
|
559
|
-
|
560
|
-
class SybaseAdapterContext < SybSQLContext
|
561
|
-
DEADLOCK = 1205
|
562
|
-
attr_reader :message
|
563
|
-
|
564
|
-
def init(logger = nil)
|
565
|
-
@deadlocked = false
|
566
|
-
@failed = false
|
567
|
-
@logger = logger
|
568
|
-
@message = nil
|
569
|
-
end
|
570
|
-
|
571
|
-
def srvmsgCB(con, msg)
|
572
|
-
# Do not log change of context messages.
|
573
|
-
if msg['severity'] == 10 or msg['severity'] == 0
|
574
|
-
return true
|
575
|
-
end
|
576
|
-
|
577
|
-
if msg['msgnumber'] == DEADLOCK
|
578
|
-
@deadlocked = true
|
579
|
-
else
|
580
|
-
@logger.info "SQL Command failed!" if @logger
|
581
|
-
@failed = true
|
582
|
-
end
|
583
|
-
|
584
|
-
if @logger
|
585
|
-
@logger.error "** SybSQLContext Server Message: **"
|
586
|
-
@logger.error " Message number #{msg['msgnumber']} Severity #{msg['severity']} State #{msg['state']} Line #{msg['line']}"
|
587
|
-
@logger.error " Server #{msg['srvname']}"
|
588
|
-
@logger.error " Procedure #{msg['proc']}"
|
589
|
-
@logger.error " Message String: #{msg['text']}"
|
590
|
-
end
|
591
|
-
|
592
|
-
@message = msg['text']
|
593
|
-
|
594
|
-
true
|
595
|
-
end
|
596
|
-
|
597
|
-
def deadlocked?
|
598
|
-
@deadlocked
|
599
|
-
end
|
600
|
-
|
601
|
-
def failed?
|
602
|
-
@failed
|
603
|
-
end
|
604
|
-
|
605
|
-
def reset
|
606
|
-
@deadlocked = false
|
607
|
-
@failed = false
|
608
|
-
@message = nil
|
609
|
-
end
|
610
|
-
|
611
|
-
def cltmsgCB(con, msg)
|
612
|
-
return true unless ( msg.kind_of?(Hash) )
|
613
|
-
unless ( msg[ "severity" ] ) then
|
614
|
-
return true
|
615
|
-
end
|
616
|
-
|
617
|
-
if @logger
|
618
|
-
@logger.error "** SybSQLContext Client-Message: **"
|
619
|
-
@logger.error " Message number: LAYER=#{msg[ 'layer' ]} ORIGIN=#{msg[ 'origin' ]} SEVERITY=#{msg[ 'severity' ]} NUMBER=#{msg[ 'number' ]}"
|
620
|
-
@logger.error " Message String: #{msg['msgstring']}"
|
621
|
-
@logger.error " OS Error: #{msg['osstring']}"
|
622
|
-
|
623
|
-
@message = msg['msgstring']
|
624
|
-
end
|
625
|
-
|
626
|
-
@failed = true
|
627
|
-
|
628
|
-
# Not retry , CS_CV_RETRY_FAIL( probability TimeOut )
|
629
|
-
if( msg[ 'severity' ] == "RETRY_FAIL" ) then
|
630
|
-
@timeout_p = true
|
631
|
-
return false
|
632
|
-
end
|
633
|
-
|
634
|
-
return true
|
635
|
-
end
|
636
|
-
end # class SybaseAdapterContext
|
637
|
-
|
638
|
-
end # module ConnectionAdapters
|
639
|
-
end # module ActiveRecord
|
640
|
-
|
641
|
-
|
642
|
-
# Allow identity inserts for fixtures.
|
643
|
-
require "active_record/fixtures"
|
644
|
-
class Fixtures
|
645
|
-
alias :original_insert_fixtures :insert_fixtures
|
646
|
-
|
647
|
-
def insert_fixtures
|
648
|
-
if @connection.instance_of?(ActiveRecord::ConnectionAdapters::SybaseAdapter)
|
649
|
-
values.each do |fixture|
|
650
|
-
@connection.enable_identity_insert(table_name, true)
|
651
|
-
@connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
|
652
|
-
@connection.enable_identity_insert(table_name, false)
|
653
|
-
end
|
654
|
-
else
|
655
|
-
original_insert_fixtures
|
656
|
-
end
|
657
|
-
end
|
658
|
-
end
|
659
|
-
|
660
|
-
rescue LoadError => cannot_require_sybase
|
661
|
-
# Couldn't load sybase adapter
|
662
|
-
end
|