activerecord 1.14.4 → 1.15.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 +400 -1
- data/README +2 -2
- data/RUNNING_UNIT_TESTS +21 -3
- data/Rakefile +55 -10
- data/lib/active_record.rb +10 -4
- data/lib/active_record/acts/list.rb +15 -4
- data/lib/active_record/acts/nested_set.rb +11 -12
- data/lib/active_record/acts/tree.rb +13 -14
- data/lib/active_record/aggregations.rb +46 -22
- data/lib/active_record/associations.rb +213 -162
- data/lib/active_record/associations/association_collection.rb +45 -15
- data/lib/active_record/associations/association_proxy.rb +32 -13
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
- data/lib/active_record/associations/has_many_association.rb +37 -17
- data/lib/active_record/associations/has_many_through_association.rb +120 -30
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +282 -203
- data/lib/active_record/calculations.rb +95 -54
- data/lib/active_record/callbacks.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
- data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
- data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
- data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
- data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
- data/lib/active_record/deprecated_associations.rb +24 -10
- data/lib/active_record/deprecated_finders.rb +4 -1
- data/lib/active_record/fixtures.rb +37 -23
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +8 -5
- data/lib/active_record/observer.rb +73 -34
- data/lib/active_record/reflection.rb +21 -7
- data/lib/active_record/schema_dumper.rb +33 -5
- data/lib/active_record/timestamp.rb +23 -34
- data/lib/active_record/transactions.rb +37 -30
- data/lib/active_record/validations.rb +46 -30
- data/lib/active_record/vendor/mysql.rb +20 -5
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record/wrappings.rb +1 -2
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +5 -1
- data/test/abstract_unit.rb +18 -8
- data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
- data/test/adapter_test.rb +9 -7
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +29 -0
- data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
- data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
- data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
- data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
- data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
- data/test/associations_test.rb +339 -45
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +321 -67
- data/test/calculations_test.rb +48 -10
- data/test/callbacks_test.rb +13 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +18 -17
- data/test/connections/native_firebird/connection.rb +19 -17
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +18 -15
- data/test/connections/native_openbase/connection.rb +14 -15
- data/test/connections/native_oracle/connection.rb +16 -12
- data/test/connections/native_postgresql/connection.rb +16 -17
- data/test/connections/native_sqlite/connection.rb +3 -6
- data/test/connections/native_sqlite3/connection.rb +3 -6
- data/test/connections/native_sqlserver/connection.rb +16 -17
- data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
- data/test/connections/native_sybase/connection.rb +16 -17
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/defaults_test.rb +52 -10
- data/test/deprecated_associations_test.rb +151 -107
- data/test/deprecated_finder_test.rb +83 -66
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +118 -11
- data/test/fixtures/accounts.yml +6 -1
- data/test/fixtures/author.rb +27 -4
- data/test/fixtures/categorizations.yml +8 -2
- data/test/fixtures/category.rb +1 -2
- data/test/fixtures/comments.yml +0 -6
- data/test/fixtures/companies.yml +6 -1
- data/test/fixtures/company.rb +23 -1
- data/test/fixtures/company_in_module.rb +8 -10
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/customers.yml +9 -0
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +9 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
- data/test/fixtures/db_definitions/firebird.sql +13 -1
- data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
- data/test/fixtures/db_definitions/frontbase.sql +262 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
- data/test/fixtures/db_definitions/mysql.sql +23 -14
- data/test/fixtures/db_definitions/openbase.sql +13 -1
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +29 -2
- data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
- data/test/fixtures/db_definitions/postgresql.sql +13 -3
- data/test/fixtures/db_definitions/schema.rb +29 -1
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +12 -3
- data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlserver.sql +35 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
- data/test/fixtures/db_definitions/sybase.sql +13 -4
- data/test/fixtures/developer.rb +12 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/funny_jokes.yml +3 -7
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixin.rb +15 -0
- data/test/fixtures/mixins.yml +38 -0
- data/test/fixtures/post.rb +3 -2
- data/test/fixtures/project.rb +3 -1
- data/test/fixtures/topic.rb +6 -1
- data/test/fixtures/topics.yml +4 -4
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +45 -0
- data/test/inheritance_test.rb +67 -6
- data/test/lifecycle_test.rb +40 -19
- data/test/locking_test.rb +170 -26
- data/test/method_scoping_test.rb +2 -2
- data/test/migration_test.rb +387 -110
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +14 -2
- data/test/mixin_test.rb +56 -18
- data/test/modules_test.rb +8 -2
- data/test/multiple_db_test.rb +2 -2
- data/test/pk_test.rb +1 -0
- data/test/reflection_test.rb +8 -2
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +40 -4
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +19 -16
- data/test/transactions_test.rb +86 -72
- data/test/validations_test.rb +126 -56
- data/test/xml_serialization_test.rb +125 -0
- metadata +45 -11
- data/lib/active_record/locking.rb +0 -79
@@ -1,11 +1,20 @@
|
|
1
1
|
# sybase_adaptor.rb
|
2
|
-
# Author: John Sheets
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
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
7
|
# 17 Mar 2006: Added support for migrations; fixed issues with :boolean columns.
|
8
|
-
#
|
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().
|
9
18
|
|
10
19
|
require 'active_record/connection_adapters/abstract_adapter'
|
11
20
|
|
@@ -35,7 +44,7 @@ module ActiveRecord
|
|
35
44
|
|
36
45
|
ConnectionAdapters::SybaseAdapter.new(
|
37
46
|
SybSQL.new({'S' => host, 'U' => username, 'P' => password},
|
38
|
-
ConnectionAdapters::SybaseAdapterContext), database, logger)
|
47
|
+
ConnectionAdapters::SybaseAdapterContext), database, config, logger)
|
39
48
|
end
|
40
49
|
end # class Base
|
41
50
|
|
@@ -48,7 +57,7 @@ module ActiveRecord
|
|
48
57
|
#
|
49
58
|
# * <tt>:host</tt> -- The name of the database server. No default, must be provided.
|
50
59
|
# * <tt>:database</tt> -- The name of the database. No default, must be provided.
|
51
|
-
# * <tt>:username</tt> -- Defaults to sa.
|
60
|
+
# * <tt>:username</tt> -- Defaults to "sa".
|
52
61
|
# * <tt>:password</tt> -- Defaults to empty string.
|
53
62
|
#
|
54
63
|
# Usage Notes:
|
@@ -75,7 +84,7 @@ module ActiveRecord
|
|
75
84
|
# 2> go
|
76
85
|
class SybaseAdapter < AbstractAdapter # :nodoc:
|
77
86
|
class ColumnWithIdentity < Column
|
78
|
-
attr_reader :identity
|
87
|
+
attr_reader :identity
|
79
88
|
|
80
89
|
def initialize(name, default, sql_type = nil, nullable = nil, identity = nil, primary = nil)
|
81
90
|
super(name, default, sql_type, nullable)
|
@@ -84,14 +93,15 @@ module ActiveRecord
|
|
84
93
|
|
85
94
|
def simplified_type(field_type)
|
86
95
|
case field_type
|
87
|
-
when /int|bigint|smallint|tinyint/i
|
88
|
-
when /float|double|
|
89
|
-
when /
|
90
|
-
when /
|
91
|
-
when /
|
92
|
-
when /
|
93
|
-
when /
|
94
|
-
|
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
|
95
105
|
end
|
96
106
|
end
|
97
107
|
|
@@ -106,10 +116,12 @@ module ActiveRecord
|
|
106
116
|
end # class ColumnWithIdentity
|
107
117
|
|
108
118
|
# Sybase adapter
|
109
|
-
def initialize(connection, database, logger = nil)
|
119
|
+
def initialize(connection, database, config = {}, logger = nil)
|
110
120
|
super(connection, logger)
|
111
121
|
context = connection.context
|
112
122
|
context.init(logger)
|
123
|
+
@config = config
|
124
|
+
@numconvert = config.has_key?(:numconvert) ? config[:numconvert] : true
|
113
125
|
@limit = @offset = 0
|
114
126
|
unless connection.sql_norow("USE #{database}")
|
115
127
|
raise "Cannot USE #{database}"
|
@@ -123,6 +135,7 @@ module ActiveRecord
|
|
123
135
|
:text => { :name => "text" },
|
124
136
|
:integer => { :name => "int" },
|
125
137
|
:float => { :name => "float", :limit => 8 },
|
138
|
+
:decimal => { :name => "decimal" },
|
126
139
|
:datetime => { :name => "datetime" },
|
127
140
|
:timestamp => { :name => "timestamp" },
|
128
141
|
:time => { :name => "time" },
|
@@ -132,6 +145,15 @@ module ActiveRecord
|
|
132
145
|
}
|
133
146
|
end
|
134
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
|
+
|
135
157
|
def adapter_name
|
136
158
|
'Sybase'
|
137
159
|
end
|
@@ -154,29 +176,6 @@ module ActiveRecord
|
|
154
176
|
30
|
155
177
|
end
|
156
178
|
|
157
|
-
# Check for a limit statement and parse out the limit and
|
158
|
-
# offset if specified. Remove the limit from the sql statement
|
159
|
-
# and call select.
|
160
|
-
def select_all(sql, name = nil)
|
161
|
-
select(sql, name)
|
162
|
-
end
|
163
|
-
|
164
|
-
# Remove limit clause from statement. This will almost always
|
165
|
-
# contain LIMIT 1 from the caller. set the rowcount to 1 before
|
166
|
-
# calling select.
|
167
|
-
def select_one(sql, name = nil)
|
168
|
-
result = select(sql, name)
|
169
|
-
result.nil? ? nil : result.first
|
170
|
-
end
|
171
|
-
|
172
|
-
def columns(table_name, name = nil)
|
173
|
-
table_structure(table_name).inject([]) do |columns, column|
|
174
|
-
name, default, type, nullable, identity, primary = column
|
175
|
-
columns << ColumnWithIdentity.new(name, default, type, nullable, identity, primary)
|
176
|
-
columns
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
179
|
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
181
180
|
begin
|
182
181
|
table_name = get_table_name(sql)
|
@@ -186,7 +185,7 @@ module ActiveRecord
|
|
186
185
|
if col != nil
|
187
186
|
if query_contains_identity_column(sql, col)
|
188
187
|
begin
|
189
|
-
|
188
|
+
enable_identity_insert(table_name, true)
|
190
189
|
ii_enabled = true
|
191
190
|
rescue Exception => e
|
192
191
|
raise ActiveRecordError, "IDENTITY_INSERT could not be turned ON"
|
@@ -202,7 +201,7 @@ module ActiveRecord
|
|
202
201
|
ensure
|
203
202
|
if ii_enabled
|
204
203
|
begin
|
205
|
-
|
204
|
+
enable_identity_insert(table_name, false)
|
206
205
|
rescue Exception => e
|
207
206
|
raise ActiveRecordError, "IDENTITY_INSERT could not be turned OFF"
|
208
207
|
end
|
@@ -211,45 +210,62 @@ module ActiveRecord
|
|
211
210
|
end
|
212
211
|
|
213
212
|
def execute(sql, name = nil)
|
214
|
-
|
215
|
-
@connection.context.reset
|
216
|
-
@connection.set_rowcount(@limit || 0)
|
217
|
-
@limit = @offset = nil
|
218
|
-
@connection.sql_norow(sql)
|
219
|
-
if @connection.cmd_fail? or @connection.context.failed?
|
220
|
-
raise "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}"
|
221
|
-
end
|
222
|
-
end
|
223
|
-
# Return rows affected
|
213
|
+
raw_execute(sql, name)
|
224
214
|
@connection.results[0].row_count
|
225
215
|
end
|
226
216
|
|
227
|
-
|
228
|
-
|
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
|
229
220
|
|
230
|
-
def
|
231
|
-
|
232
|
-
|
221
|
+
def current_database
|
222
|
+
select_one("select DB_NAME() as name")["name"]
|
223
|
+
end
|
233
224
|
|
234
225
|
def tables(name = nil)
|
235
|
-
|
236
|
-
select("select name from sysobjects where type='U'", name).each do |row|
|
237
|
-
tables << row['name']
|
238
|
-
end
|
239
|
-
tables
|
226
|
+
select("select name from sysobjects where type='U'", name).map { |row| row['name'] }
|
240
227
|
end
|
241
228
|
|
242
229
|
def indexes(table_name, name = nil)
|
243
|
-
|
244
|
-
select("exec sp_helpindex #{table_name}", name).each do |index|
|
230
|
+
select("exec sp_helpindex #{table_name}", name).map do |index|
|
245
231
|
unique = index["index_description"] =~ /unique/
|
246
232
|
primary = index["index_description"] =~ /^clustered/
|
247
233
|
if !primary
|
248
234
|
cols = index["index_keys"].split(", ").each { |col| col.strip! }
|
249
|
-
|
235
|
+
IndexDefinition.new(table_name, index["index_name"], unique, cols)
|
250
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)
|
251
268
|
end
|
252
|
-
indexes
|
253
269
|
end
|
254
270
|
|
255
271
|
def quoted_true
|
@@ -261,11 +277,13 @@ module ActiveRecord
|
|
261
277
|
end
|
262
278
|
|
263
279
|
def quote(value, column = nil)
|
280
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
281
|
+
|
264
282
|
case value
|
265
283
|
when String
|
266
284
|
if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
267
285
|
"#{quote_string(column.class.string_to_binary(value))}"
|
268
|
-
elsif value =~ /^[+-]?[0-9]+$/o
|
286
|
+
elsif @numconvert && force_numeric?(column) && value =~ /^[+-]?[0-9]+$/o
|
269
287
|
value
|
270
288
|
else
|
271
289
|
"'#{quote_string(value)}'"
|
@@ -273,39 +291,16 @@ module ActiveRecord
|
|
273
291
|
when NilClass then (column && column.type == :boolean) ? '0' : "NULL"
|
274
292
|
when TrueClass then '1'
|
275
293
|
when FalseClass then '0'
|
276
|
-
when Float, Fixnum, Bignum then value.to_s
|
277
|
-
when Date then "'#{value.to_s}'"
|
294
|
+
when Float, Fixnum, Bignum then force_numeric?(column) ? value.to_s : "'#{value.to_s}'"
|
278
295
|
when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
279
|
-
else
|
296
|
+
else super
|
280
297
|
end
|
281
298
|
end
|
282
299
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
when String then value =~ /^[ty]/o ? 1 : 0
|
288
|
-
when true then 1
|
289
|
-
when false then 0
|
290
|
-
else value.to_i
|
291
|
-
end
|
292
|
-
when :integer then value.to_i
|
293
|
-
when :float then value.to_f
|
294
|
-
when :text, :string, :enum
|
295
|
-
case value
|
296
|
-
when String, Symbol, Fixnum, Float, Bignum, TrueClass, FalseClass
|
297
|
-
"'#{quote_string(value.to_s)}'"
|
298
|
-
else
|
299
|
-
"'#{quote_string(value.to_yaml)}'"
|
300
|
-
end
|
301
|
-
when :date, :datetime, :time
|
302
|
-
case value
|
303
|
-
when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
304
|
-
when Date then "'#{value.to_s}'"
|
305
|
-
else "'#{quote_string(value)}'"
|
306
|
-
end
|
307
|
-
else "'#{quote_string(value.to_yaml)}'"
|
308
|
-
end
|
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))
|
309
304
|
end
|
310
305
|
|
311
306
|
def quote_string(s)
|
@@ -313,13 +308,15 @@ module ActiveRecord
|
|
313
308
|
end
|
314
309
|
|
315
310
|
def quote_column_name(name)
|
316
|
-
|
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
|
317
314
|
end
|
318
315
|
|
319
316
|
def add_limit_offset!(sql, options) # :nodoc:
|
320
317
|
@limit = options[:limit]
|
321
318
|
@offset = options[:offset]
|
322
|
-
if
|
319
|
+
if use_temp_table?
|
323
320
|
# Use temp table to hack offset with Sybase
|
324
321
|
sql.sub!(/ FROM /i, ' INTO #artemp FROM ')
|
325
322
|
elsif zero_limit?
|
@@ -335,6 +332,11 @@ module ActiveRecord
|
|
335
332
|
end
|
336
333
|
end
|
337
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
|
+
|
338
340
|
def supports_migrations? #:nodoc:
|
339
341
|
true
|
340
342
|
end
|
@@ -348,12 +350,18 @@ module ActiveRecord
|
|
348
350
|
end
|
349
351
|
|
350
352
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
351
|
-
|
352
|
-
|
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)
|
353
362
|
remove_default_constraint(table_name, column_name)
|
354
|
-
|
363
|
+
execute "ALTER TABLE #{table_name} REPLACE #{column_name} DEFAULT #{quote options[:default]}"
|
355
364
|
end
|
356
|
-
sql_commands.each { |c| execute(c) }
|
357
365
|
end
|
358
366
|
|
359
367
|
def remove_column(table_name, column_name)
|
@@ -362,10 +370,10 @@ module ActiveRecord
|
|
362
370
|
end
|
363
371
|
|
364
372
|
def remove_default_constraint(table_name, column_name)
|
365
|
-
|
366
|
-
|
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|
|
367
375
|
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
|
368
|
-
|
376
|
+
end
|
369
377
|
end
|
370
378
|
|
371
379
|
def remove_index(table_name, options = {})
|
@@ -373,7 +381,7 @@ module ActiveRecord
|
|
373
381
|
end
|
374
382
|
|
375
383
|
def add_column_options!(sql, options) #:nodoc:
|
376
|
-
sql << " DEFAULT #{quote(options[:default], options[:column])}"
|
384
|
+
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
|
377
385
|
|
378
386
|
if check_null_for_column?(options[:column], sql)
|
379
387
|
sql << (options[:null] == false ? " NOT NULL" : " NULL")
|
@@ -381,6 +389,12 @@ module ActiveRecord
|
|
381
389
|
sql
|
382
390
|
end
|
383
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
|
+
|
384
398
|
private
|
385
399
|
def check_null_for_column?(col, sql)
|
386
400
|
# Sybase columns are NOT NULL by default, so explicitly set NULL
|
@@ -424,34 +438,44 @@ module ActiveRecord
|
|
424
438
|
end
|
425
439
|
end
|
426
440
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
has_offset = !@offset.nil? && @offset > 0
|
434
|
-
!has_limit || !has_offset
|
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
|
435
447
|
end
|
436
448
|
|
437
449
|
def zero_limit?
|
438
450
|
!@limit.nil? && @limit == 0
|
439
451
|
end
|
440
452
|
|
441
|
-
|
442
|
-
def select(sql, name = nil)
|
443
|
-
@connection.context.reset
|
453
|
+
def raw_execute(sql, name = nil)
|
444
454
|
log(sql, name) do
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
# Run a normal select
|
450
|
-
@connection.set_rowcount(@limit || 0)
|
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
|
451
459
|
@connection.sql(sql)
|
452
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
|
453
476
|
# Select into a temp table and prune results
|
454
477
|
@logger.debug "Selecting #{@limit + (@offset || 0)} or fewer rows into #artemp" if @logger
|
478
|
+
@connection.context.reset
|
455
479
|
@connection.set_rowcount(@limit + (@offset || 0))
|
456
480
|
@connection.sql_norow(sql) # Select into temp table
|
457
481
|
@logger.debug "Deleting #{@offset || 0} or fewer rows from #artemp" if @logger
|
@@ -462,29 +486,21 @@ module ActiveRecord
|
|
462
486
|
end
|
463
487
|
end
|
464
488
|
|
489
|
+
raise StatementInvalid, "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}" if @connection.context.failed? or @connection.cmd_fail?
|
490
|
+
|
465
491
|
rows = []
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
results
|
470
|
-
|
471
|
-
fields =
|
472
|
-
|
473
|
-
hashed_row = {}
|
474
|
-
row.zip(fields) { |cell, column| hashed_row[column] = cell }
|
475
|
-
rows << hashed_row
|
476
|
-
end
|
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
|
477
499
|
end
|
478
500
|
end
|
479
|
-
@connection.sql_norow("drop table #artemp") if
|
501
|
+
@connection.sql_norow("drop table #artemp") if use_temp_table?
|
480
502
|
@limit = @offset = nil
|
481
|
-
|
482
|
-
end
|
483
|
-
|
484
|
-
def enable_identity_insert(table_name, enable = true)
|
485
|
-
if has_identity_column(table_name)
|
486
|
-
"SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
|
487
|
-
end
|
503
|
+
rows
|
488
504
|
end
|
489
505
|
|
490
506
|
def get_table_name(sql)
|
@@ -502,80 +518,42 @@ module ActiveRecord
|
|
502
518
|
end
|
503
519
|
|
504
520
|
def get_identity_column(table_name)
|
505
|
-
@
|
506
|
-
|
507
|
-
|
508
|
-
|
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
|
509
526
|
end
|
510
|
-
|
511
|
-
return nil
|
527
|
+
@id_columns[table_name]
|
512
528
|
end
|
513
529
|
|
514
530
|
def query_contains_identity_column(sql, col)
|
515
531
|
sql =~ /\[#{col}\]/
|
516
532
|
end
|
517
533
|
|
518
|
-
#
|
519
|
-
def
|
520
|
-
|
521
|
-
end
|
522
|
-
|
523
|
-
def table_structure(table_name)
|
524
|
-
sql = <<SQLTEXT
|
525
|
-
SELECT col.name AS name, type.name AS type, col.prec, col.scale, col.length,
|
526
|
-
col.status, obj.sysstat2, def.text
|
527
|
-
FROM sysobjects obj, syscolumns col, systypes type, syscomments def
|
528
|
-
WHERE obj.id = col.id AND col.usertype = type.usertype AND col.cdefault *= def.id
|
529
|
-
AND obj.type = 'U' AND obj.name = '#{table_name}' ORDER BY col.colid
|
530
|
-
SQLTEXT
|
531
|
-
log(sql, "Get Column Info ") do
|
532
|
-
@connection.set_rowcount(0)
|
533
|
-
@connection.sql(sql)
|
534
|
-
end
|
535
|
-
if @connection.context.failed?
|
536
|
-
raise "SQL Command for table_structure for #{table_name} failed\nMessage: #{@connection.context.message}"
|
537
|
-
elsif !@connection.cmd_fail?
|
538
|
-
columns = []
|
539
|
-
results = @connection.top_row_result
|
540
|
-
results.rows.each do |row|
|
541
|
-
name, type, prec, scale, length, status, sysstat2, default = row
|
542
|
-
type = normalize_type(type, prec, scale, length)
|
543
|
-
default_value = nil
|
544
|
-
name.sub!(/_$/o, '')
|
545
|
-
if default =~ /DEFAULT\s+(.+)/o
|
546
|
-
default_value = $1.strip
|
547
|
-
default_value = default_value[1...-1] if default_value =~ /^['"]/o
|
548
|
-
end
|
549
|
-
nullable = (status & 8) == 8
|
550
|
-
identity = status >= 128
|
551
|
-
primary = (sysstat2 & 8) == 8
|
552
|
-
|
553
|
-
columns << [name, default_value, type, nullable, identity, primary]
|
554
|
-
end
|
555
|
-
columns
|
556
|
-
else
|
557
|
-
nil
|
558
|
-
end
|
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
|
559
537
|
end
|
560
538
|
|
561
539
|
def normalize_type(field_type, prec, scale, length)
|
562
|
-
|
563
|
-
|
540
|
+
has_scale = (!scale.nil? && scale > 0)
|
541
|
+
type = if field_type =~ /numeric/i and !has_scale
|
542
|
+
'int'
|
564
543
|
elsif field_type =~ /money/i
|
565
|
-
|
544
|
+
'numeric'
|
566
545
|
else
|
567
|
-
|
568
|
-
end
|
569
|
-
size = ''
|
570
|
-
if prec
|
571
|
-
size = "(#{prec})"
|
572
|
-
elsif length
|
573
|
-
size = "(#{length})"
|
546
|
+
resolve_type(field_type.strip)
|
574
547
|
end
|
575
|
-
return type + size
|
576
|
-
end
|
577
548
|
|
578
|
-
|
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}"
|
579
557
|
end
|
580
558
|
end # class SybaseAdapter
|
581
559
|
|
@@ -667,18 +645,18 @@ class Fixtures
|
|
667
645
|
alias :original_insert_fixtures :insert_fixtures
|
668
646
|
|
669
647
|
def insert_fixtures
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
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
|
674
656
|
end
|
675
657
|
end
|
676
|
-
|
677
|
-
def allow_identity_inserts(table_name, enable)
|
678
|
-
@connection.execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" rescue nil
|
679
|
-
end
|
680
658
|
end
|
681
659
|
|
682
660
|
rescue LoadError => cannot_require_sybase
|
683
661
|
# Couldn't load sybase adapter
|
684
|
-
end
|
662
|
+
end
|