activerecord_authorails 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3043 -0
- data/README +360 -0
- data/RUNNING_UNIT_TESTS +64 -0
- data/Rakefile +226 -0
- data/examples/associations.png +0 -0
- data/examples/associations.rb +87 -0
- data/examples/shared_setup.rb +15 -0
- data/examples/validation.rb +85 -0
- data/install.rb +30 -0
- data/lib/active_record.rb +85 -0
- data/lib/active_record/acts/list.rb +244 -0
- data/lib/active_record/acts/nested_set.rb +211 -0
- data/lib/active_record/acts/tree.rb +89 -0
- data/lib/active_record/aggregations.rb +191 -0
- data/lib/active_record/associations.rb +1637 -0
- data/lib/active_record/associations/association_collection.rb +190 -0
- data/lib/active_record/associations/association_proxy.rb +158 -0
- data/lib/active_record/associations/belongs_to_association.rb +56 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
- data/lib/active_record/associations/has_many_association.rb +210 -0
- data/lib/active_record/associations/has_many_through_association.rb +247 -0
- data/lib/active_record/associations/has_one_association.rb +80 -0
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +2164 -0
- data/lib/active_record/calculations.rb +270 -0
- data/lib/active_record/callbacks.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
- data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
- data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
- data/lib/active_record/deprecated_associations.rb +104 -0
- data/lib/active_record/deprecated_finders.rb +44 -0
- data/lib/active_record/fixtures.rb +628 -0
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +394 -0
- data/lib/active_record/observer.rb +178 -0
- data/lib/active_record/query_cache.rb +64 -0
- data/lib/active_record/reflection.rb +222 -0
- data/lib/active_record/schema.rb +58 -0
- data/lib/active_record/schema_dumper.rb +149 -0
- data/lib/active_record/timestamp.rb +51 -0
- data/lib/active_record/transactions.rb +136 -0
- data/lib/active_record/validations.rb +843 -0
- data/lib/active_record/vendor/db2.rb +362 -0
- data/lib/active_record/vendor/mysql.rb +1214 -0
- data/lib/active_record/vendor/simple.rb +693 -0
- data/lib/active_record/version.rb +9 -0
- data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
- data/lib/active_record/wrappings.rb +58 -0
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +59 -0
- data/test/abstract_unit.rb +77 -0
- data/test/active_schema_test_mysql.rb +31 -0
- data/test/adapter_test.rb +87 -0
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +95 -0
- data/test/all.sh +8 -0
- data/test/ar_schema_test.rb +33 -0
- data/test/association_inheritance_reload.rb +14 -0
- data/test/associations/callbacks_test.rb +126 -0
- data/test/associations/cascaded_eager_loading_test.rb +138 -0
- data/test/associations/eager_test.rb +393 -0
- data/test/associations/extension_test.rb +42 -0
- data/test/associations/join_model_test.rb +497 -0
- data/test/associations_test.rb +1809 -0
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +1586 -0
- data/test/binary_test.rb +37 -0
- data/test/calculations_test.rb +219 -0
- data/test/callbacks_test.rb +377 -0
- data/test/class_inheritable_attributes_test.rb +32 -0
- data/test/column_alias_test.rb +17 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +25 -0
- data/test/connections/native_firebird/connection.rb +26 -0
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +24 -0
- data/test/connections/native_openbase/connection.rb +21 -0
- data/test/connections/native_oracle/connection.rb +27 -0
- data/test/connections/native_postgresql/connection.rb +23 -0
- data/test/connections/native_sqlite/connection.rb +34 -0
- data/test/connections/native_sqlite3/connection.rb +34 -0
- data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
- data/test/connections/native_sqlserver/connection.rb +23 -0
- data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
- data/test/connections/native_sybase/connection.rb +23 -0
- data/test/copy_table_sqlite.rb +64 -0
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/default_test_firebird.rb +16 -0
- data/test/defaults_test.rb +60 -0
- data/test/deprecated_associations_test.rb +396 -0
- data/test/deprecated_finder_test.rb +151 -0
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +504 -0
- data/test/fixtures/accounts.yml +28 -0
- data/test/fixtures/author.rb +99 -0
- data/test/fixtures/author_favorites.yml +4 -0
- data/test/fixtures/authors.yml +7 -0
- data/test/fixtures/auto_id.rb +4 -0
- data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
- data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
- data/test/fixtures/bad_fixtures/blank_line +3 -0
- data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
- data/test/fixtures/bad_fixtures/missing_value +1 -0
- data/test/fixtures/binary.rb +2 -0
- data/test/fixtures/categories.yml +14 -0
- data/test/fixtures/categories/special_categories.yml +9 -0
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
- data/test/fixtures/categories_ordered.yml +7 -0
- data/test/fixtures/categories_posts.yml +23 -0
- data/test/fixtures/categorization.rb +5 -0
- data/test/fixtures/categorizations.yml +17 -0
- data/test/fixtures/category.rb +20 -0
- data/test/fixtures/column_name.rb +3 -0
- data/test/fixtures/comment.rb +23 -0
- data/test/fixtures/comments.yml +59 -0
- data/test/fixtures/companies.yml +55 -0
- data/test/fixtures/company.rb +107 -0
- data/test/fixtures/company_in_module.rb +59 -0
- data/test/fixtures/computer.rb +3 -0
- data/test/fixtures/computers.yml +4 -0
- data/test/fixtures/course.rb +3 -0
- data/test/fixtures/courses.yml +7 -0
- data/test/fixtures/customer.rb +55 -0
- data/test/fixtures/customers.yml +17 -0
- data/test/fixtures/db_definitions/db2.drop.sql +32 -0
- data/test/fixtures/db_definitions/db2.sql +231 -0
- data/test/fixtures/db_definitions/db22.drop.sql +2 -0
- data/test/fixtures/db_definitions/db22.sql +5 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
- data/test/fixtures/db_definitions/firebird.sql +304 -0
- data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
- data/test/fixtures/db_definitions/firebird2.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
- data/test/fixtures/db_definitions/frontbase.sql +268 -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 +32 -0
- data/test/fixtures/db_definitions/mysql.sql +234 -0
- data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/mysql2.sql +5 -0
- data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase.sql +302 -0
- data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase2.sql +7 -0
- data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
- data/test/fixtures/db_definitions/oracle.sql +325 -0
- data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle2.sql +6 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
- data/test/fixtures/db_definitions/postgresql.sql +263 -0
- data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/postgresql2.sql +5 -0
- data/test/fixtures/db_definitions/schema.rb +60 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
- data/test/fixtures/db_definitions/sqlite.sql +215 -0
- data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlite2.sql +5 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
- data/test/fixtures/db_definitions/sqlserver.sql +243 -0
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
- data/test/fixtures/db_definitions/sybase.sql +218 -0
- data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
- data/test/fixtures/db_definitions/sybase2.sql +5 -0
- data/test/fixtures/default.rb +2 -0
- data/test/fixtures/developer.rb +52 -0
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects.yml +17 -0
- data/test/fixtures/developers_projects/david_action_controller +3 -0
- data/test/fixtures/developers_projects/david_active_record +3 -0
- data/test/fixtures/developers_projects/jamis_active_record +2 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/entrant.rb +3 -0
- data/test/fixtures/entrants.yml +14 -0
- data/test/fixtures/fk_test_has_fk.yml +3 -0
- data/test/fixtures/fk_test_has_pk.yml +2 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/funny_jokes.yml +10 -0
- data/test/fixtures/joke.rb +6 -0
- data/test/fixtures/keyboard.rb +3 -0
- data/test/fixtures/legacy_thing.rb +3 -0
- data/test/fixtures/legacy_things.yml +3 -0
- data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
- data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -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/mixed_case_monkey.rb +3 -0
- data/test/fixtures/mixed_case_monkeys.yml +6 -0
- data/test/fixtures/mixin.rb +63 -0
- data/test/fixtures/mixins.yml +127 -0
- data/test/fixtures/movie.rb +5 -0
- data/test/fixtures/movies.yml +7 -0
- data/test/fixtures/naked/csv/accounts.csv +1 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/order.rb +4 -0
- data/test/fixtures/people.yml +3 -0
- data/test/fixtures/person.rb +4 -0
- data/test/fixtures/post.rb +58 -0
- data/test/fixtures/posts.yml +48 -0
- data/test/fixtures/project.rb +27 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/reader.rb +4 -0
- data/test/fixtures/readers.yml +4 -0
- data/test/fixtures/reply.rb +37 -0
- data/test/fixtures/subject.rb +4 -0
- data/test/fixtures/subscriber.rb +6 -0
- data/test/fixtures/subscribers/first +2 -0
- data/test/fixtures/subscribers/second +2 -0
- data/test/fixtures/tag.rb +7 -0
- data/test/fixtures/tagging.rb +6 -0
- data/test/fixtures/taggings.yml +18 -0
- data/test/fixtures/tags.yml +7 -0
- data/test/fixtures/task.rb +3 -0
- data/test/fixtures/tasks.yml +7 -0
- data/test/fixtures/topic.rb +25 -0
- data/test/fixtures/topics.yml +22 -0
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +401 -0
- data/test/inheritance_test.rb +205 -0
- data/test/lifecycle_test.rb +137 -0
- data/test/locking_test.rb +190 -0
- data/test/method_scoping_test.rb +416 -0
- data/test/migration_test.rb +768 -0
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +196 -0
- data/test/mixin_test.rb +550 -0
- data/test/modules_test.rb +34 -0
- data/test/multiple_db_test.rb +60 -0
- data/test/pk_test.rb +104 -0
- data/test/readonly_test.rb +107 -0
- data/test/reflection_test.rb +159 -0
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +96 -0
- data/test/schema_test_postgresql.rb +64 -0
- data/test/synonym_test_oracle.rb +17 -0
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +48 -0
- data/test/transactions_test.rb +230 -0
- data/test/unconnected_test.rb +32 -0
- data/test/validations_test.rb +1097 -0
- data/test/xml_serialization_test.rb +125 -0
- metadata +365 -0
@@ -0,0 +1,689 @@
|
|
1
|
+
# oracle_adapter.rb -- ActiveRecord adapter for Oracle 8i, 9i, 10g
|
2
|
+
#
|
3
|
+
# Original author: Graham Jenkins
|
4
|
+
#
|
5
|
+
# Current maintainer: Michael Schoen <schoenm@earthlink.net>
|
6
|
+
#
|
7
|
+
#########################################################################
|
8
|
+
#
|
9
|
+
# Implementation notes:
|
10
|
+
# 1. Redefines (safely) a method in ActiveRecord to make it possible to
|
11
|
+
# implement an autonumbering solution for Oracle.
|
12
|
+
# 2. The OCI8 driver is patched to properly handle values for LONG and
|
13
|
+
# TIMESTAMP columns. The driver-author has indicated that a future
|
14
|
+
# release of the driver will obviate this patch.
|
15
|
+
# 3. LOB support is implemented through an after_save callback.
|
16
|
+
# 4. Oracle does not offer native LIMIT and OFFSET options; this
|
17
|
+
# functionality is mimiced through the use of nested selects.
|
18
|
+
# See http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
|
19
|
+
#
|
20
|
+
# Do what you want with this code, at your own peril, but if any
|
21
|
+
# significant portion of my code remains then please acknowledge my
|
22
|
+
# contribution.
|
23
|
+
# portions Copyright 2005 Graham Jenkins
|
24
|
+
|
25
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
26
|
+
require 'delegate'
|
27
|
+
|
28
|
+
begin
|
29
|
+
require_library_or_gem 'oci8' unless self.class.const_defined? :OCI8
|
30
|
+
|
31
|
+
module ActiveRecord
|
32
|
+
class Base
|
33
|
+
def self.oracle_connection(config) #:nodoc:
|
34
|
+
# Use OCI8AutoRecover instead of normal OCI8 driver.
|
35
|
+
ConnectionAdapters::OracleAdapter.new OCI8AutoRecover.new(config), logger
|
36
|
+
end
|
37
|
+
|
38
|
+
# for backwards-compatibility
|
39
|
+
def self.oci_connection(config) #:nodoc:
|
40
|
+
config[:database] = config[:host]
|
41
|
+
self.oracle_connection(config)
|
42
|
+
end
|
43
|
+
|
44
|
+
# After setting large objects to empty, select the OCI8::LOB
|
45
|
+
# and write back the data.
|
46
|
+
after_save :write_lobs
|
47
|
+
def write_lobs() #:nodoc:
|
48
|
+
if connection.is_a?(ConnectionAdapters::OracleAdapter)
|
49
|
+
self.class.columns.select { |c| c.sql_type =~ /LOB$/i }.each { |c|
|
50
|
+
value = self[c.name]
|
51
|
+
value = value.to_yaml if unserializable_attribute?(c.name, c)
|
52
|
+
next if value.nil? || (value == '')
|
53
|
+
lob = connection.select_one(
|
54
|
+
"SELECT #{c.name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = #{quote_value(id)}",
|
55
|
+
'Writable Large Object')[c.name]
|
56
|
+
lob.write value
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private :write_lobs
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
module ConnectionAdapters #:nodoc:
|
66
|
+
class OracleColumn < Column #:nodoc:
|
67
|
+
|
68
|
+
def type_cast(value)
|
69
|
+
return guess_date_or_time(value) if type == :datetime && OracleAdapter.emulate_dates
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def simplified_type(field_type)
|
75
|
+
return :boolean if OracleAdapter.emulate_booleans && field_type == 'NUMBER(1)'
|
76
|
+
case field_type
|
77
|
+
when /date|time/i then :datetime
|
78
|
+
else super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def guess_date_or_time(value)
|
83
|
+
(value.hour == 0 and value.min == 0 and value.sec == 0) ?
|
84
|
+
Date.new(value.year, value.month, value.day) : value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# This is an Oracle/OCI adapter for the ActiveRecord persistence
|
90
|
+
# framework. It relies upon the OCI8 driver, which works with Oracle 8i
|
91
|
+
# and above. Most recent development has been on Debian Linux against
|
92
|
+
# a 10g database, ActiveRecord 1.12.1 and OCI8 0.1.13.
|
93
|
+
# See: http://rubyforge.org/projects/ruby-oci8/
|
94
|
+
#
|
95
|
+
# Usage notes:
|
96
|
+
# * Key generation assumes a "${table_name}_seq" sequence is available
|
97
|
+
# for all tables; the sequence name can be changed using
|
98
|
+
# ActiveRecord::Base.set_sequence_name. When using Migrations, these
|
99
|
+
# sequences are created automatically.
|
100
|
+
# * Oracle uses DATE or TIMESTAMP datatypes for both dates and times.
|
101
|
+
# Consequently some hacks are employed to map data back to Date or Time
|
102
|
+
# in Ruby. If the column_name ends in _time it's created as a Ruby Time.
|
103
|
+
# Else if the hours/minutes/seconds are 0, I make it a Ruby Date. Else
|
104
|
+
# it's a Ruby Time. This is a bit nasty - but if you use Duck Typing
|
105
|
+
# you'll probably not care very much. In 9i and up it's tempting to
|
106
|
+
# map DATE to Date and TIMESTAMP to Time, but too many databases use
|
107
|
+
# DATE for both. Timezones and sub-second precision on timestamps are
|
108
|
+
# not supported.
|
109
|
+
# * Default values that are functions (such as "SYSDATE") are not
|
110
|
+
# supported. This is a restriction of the way ActiveRecord supports
|
111
|
+
# default values.
|
112
|
+
# * Support for Oracle8 is limited by Rails' use of ANSI join syntax, which
|
113
|
+
# is supported in Oracle9i and later. You will need to use #finder_sql for
|
114
|
+
# has_and_belongs_to_many associations to run against Oracle8.
|
115
|
+
#
|
116
|
+
# Required parameters:
|
117
|
+
#
|
118
|
+
# * <tt>:username</tt>
|
119
|
+
# * <tt>:password</tt>
|
120
|
+
# * <tt>:database</tt>
|
121
|
+
class OracleAdapter < AbstractAdapter
|
122
|
+
|
123
|
+
@@emulate_booleans = true
|
124
|
+
cattr_accessor :emulate_booleans
|
125
|
+
|
126
|
+
@@emulate_dates = false
|
127
|
+
cattr_accessor :emulate_dates
|
128
|
+
|
129
|
+
def adapter_name #:nodoc:
|
130
|
+
'Oracle'
|
131
|
+
end
|
132
|
+
|
133
|
+
def supports_migrations? #:nodoc:
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
137
|
+
def native_database_types #:nodoc:
|
138
|
+
{
|
139
|
+
:primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
|
140
|
+
:string => { :name => "VARCHAR2", :limit => 255 },
|
141
|
+
:text => { :name => "CLOB" },
|
142
|
+
:integer => { :name => "NUMBER", :limit => 38 },
|
143
|
+
:float => { :name => "NUMBER" },
|
144
|
+
:decimal => { :name => "DECIMAL" },
|
145
|
+
:datetime => { :name => "DATE" },
|
146
|
+
:timestamp => { :name => "DATE" },
|
147
|
+
:time => { :name => "DATE" },
|
148
|
+
:date => { :name => "DATE" },
|
149
|
+
:binary => { :name => "BLOB" },
|
150
|
+
:boolean => { :name => "NUMBER", :limit => 1 }
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
def table_alias_length
|
155
|
+
30
|
156
|
+
end
|
157
|
+
|
158
|
+
# QUOTING ==================================================
|
159
|
+
#
|
160
|
+
# see: abstract/quoting.rb
|
161
|
+
|
162
|
+
# camelCase column names need to be quoted; not that anyone using Oracle
|
163
|
+
# would really do this, but handling this case means we pass the test...
|
164
|
+
def quote_column_name(name) #:nodoc:
|
165
|
+
name =~ /[A-Z]/ ? "\"#{name}\"" : name
|
166
|
+
end
|
167
|
+
|
168
|
+
def quote_string(s) #:nodoc:
|
169
|
+
s.gsub(/'/, "''")
|
170
|
+
end
|
171
|
+
|
172
|
+
def quote(value, column = nil) #:nodoc:
|
173
|
+
if column && [:text, :binary].include?(column.type)
|
174
|
+
%Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
|
175
|
+
else
|
176
|
+
super
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def quoted_true
|
181
|
+
"1"
|
182
|
+
end
|
183
|
+
|
184
|
+
def quoted_false
|
185
|
+
"0"
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
# CONNECTION MANAGEMENT ====================================
|
190
|
+
#
|
191
|
+
|
192
|
+
# Returns true if the connection is active.
|
193
|
+
def active?
|
194
|
+
# Pings the connection to check if it's still good. Note that an
|
195
|
+
# #active? method is also available, but that simply returns the
|
196
|
+
# last known state, which isn't good enough if the connection has
|
197
|
+
# gone stale since the last use.
|
198
|
+
@connection.ping
|
199
|
+
rescue OCIException
|
200
|
+
false
|
201
|
+
end
|
202
|
+
|
203
|
+
# Reconnects to the database.
|
204
|
+
def reconnect!
|
205
|
+
@connection.reset!
|
206
|
+
rescue OCIException => e
|
207
|
+
@logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}"
|
208
|
+
end
|
209
|
+
|
210
|
+
# Disconnects from the database.
|
211
|
+
def disconnect!
|
212
|
+
@connection.logoff rescue nil
|
213
|
+
@connection.active = false
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# DATABASE STATEMENTS ======================================
|
218
|
+
#
|
219
|
+
# see: abstract/database_statements.rb
|
220
|
+
|
221
|
+
def execute(sql, name = nil) #:nodoc:
|
222
|
+
log(sql, name) { @connection.exec sql }
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns the next sequence value from a sequence generator. Not generally
|
226
|
+
# called directly; used by ActiveRecord to get the next primary key value
|
227
|
+
# when inserting a new database record (see #prefetch_primary_key?).
|
228
|
+
def next_sequence_value(sequence_name)
|
229
|
+
id = 0
|
230
|
+
@connection.exec("select #{sequence_name}.nextval id from dual") { |r| id = r[0].to_i }
|
231
|
+
id
|
232
|
+
end
|
233
|
+
|
234
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
235
|
+
execute(sql, name)
|
236
|
+
id_value
|
237
|
+
end
|
238
|
+
|
239
|
+
def begin_db_transaction #:nodoc:
|
240
|
+
@connection.autocommit = false
|
241
|
+
end
|
242
|
+
|
243
|
+
def commit_db_transaction #:nodoc:
|
244
|
+
@connection.commit
|
245
|
+
ensure
|
246
|
+
@connection.autocommit = true
|
247
|
+
end
|
248
|
+
|
249
|
+
def rollback_db_transaction #:nodoc:
|
250
|
+
@connection.rollback
|
251
|
+
ensure
|
252
|
+
@connection.autocommit = true
|
253
|
+
end
|
254
|
+
|
255
|
+
def add_limit_offset!(sql, options) #:nodoc:
|
256
|
+
offset = options[:offset] || 0
|
257
|
+
|
258
|
+
if limit = options[:limit]
|
259
|
+
sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
|
260
|
+
elsif offset > 0
|
261
|
+
sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Returns true for Oracle adapter (since Oracle requires primary key
|
266
|
+
# values to be pre-fetched before insert). See also #next_sequence_value.
|
267
|
+
def prefetch_primary_key?(table_name = nil)
|
268
|
+
true
|
269
|
+
end
|
270
|
+
|
271
|
+
def default_sequence_name(table, column) #:nodoc:
|
272
|
+
"#{table}_seq"
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
# SCHEMA STATEMENTS ========================================
|
277
|
+
#
|
278
|
+
# see: abstract/schema_statements.rb
|
279
|
+
|
280
|
+
def current_database #:nodoc:
|
281
|
+
select_one("select sys_context('userenv','db_name') db from dual")["db"]
|
282
|
+
end
|
283
|
+
|
284
|
+
def tables(name = nil) #:nodoc:
|
285
|
+
select_all("select lower(table_name) from user_tables").inject([]) do | tabs, t |
|
286
|
+
tabs << t.to_a.first.last
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def indexes(table_name, name = nil) #:nodoc:
|
291
|
+
result = select_all(<<-SQL, name)
|
292
|
+
SELECT lower(i.index_name) as index_name, i.uniqueness, lower(c.column_name) as column_name
|
293
|
+
FROM user_indexes i, user_ind_columns c
|
294
|
+
WHERE i.table_name = '#{table_name.to_s.upcase}'
|
295
|
+
AND c.index_name = i.index_name
|
296
|
+
AND i.index_name NOT IN (SELECT uc.index_name FROM user_constraints uc WHERE uc.constraint_type = 'P')
|
297
|
+
ORDER BY i.index_name, c.column_position
|
298
|
+
SQL
|
299
|
+
|
300
|
+
current_index = nil
|
301
|
+
indexes = []
|
302
|
+
|
303
|
+
result.each do |row|
|
304
|
+
if current_index != row['index_name']
|
305
|
+
indexes << IndexDefinition.new(table_name, row['index_name'], row['uniqueness'] == "UNIQUE", [])
|
306
|
+
current_index = row['index_name']
|
307
|
+
end
|
308
|
+
|
309
|
+
indexes.last.columns << row['column_name']
|
310
|
+
end
|
311
|
+
|
312
|
+
indexes
|
313
|
+
end
|
314
|
+
|
315
|
+
def columns(table_name, name = nil) #:nodoc:
|
316
|
+
(owner, table_name) = @connection.describe(table_name)
|
317
|
+
|
318
|
+
table_cols = <<-SQL
|
319
|
+
select column_name as name, data_type as sql_type, data_default, nullable,
|
320
|
+
decode(data_type, 'NUMBER', data_precision,
|
321
|
+
'FLOAT', data_precision,
|
322
|
+
'VARCHAR2', data_length,
|
323
|
+
null) as limit,
|
324
|
+
decode(data_type, 'NUMBER', data_scale, null) as scale
|
325
|
+
from all_tab_columns
|
326
|
+
where owner = '#{owner}'
|
327
|
+
and table_name = '#{table_name}'
|
328
|
+
order by column_id
|
329
|
+
SQL
|
330
|
+
|
331
|
+
select_all(table_cols, name).map do |row|
|
332
|
+
limit, scale = row['limit'], row['scale']
|
333
|
+
if limit || scale
|
334
|
+
row['sql_type'] << "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
|
335
|
+
end
|
336
|
+
|
337
|
+
# clean up odd default spacing from Oracle
|
338
|
+
if row['data_default']
|
339
|
+
row['data_default'].sub!(/^(.*?)\s*$/, '\1')
|
340
|
+
row['data_default'].sub!(/^'(.*)'$/, '\1')
|
341
|
+
row['data_default'] = nil if row['data_default'] =~ /^null$/i
|
342
|
+
end
|
343
|
+
|
344
|
+
OracleColumn.new(oracle_downcase(row['name']),
|
345
|
+
row['data_default'],
|
346
|
+
row['sql_type'],
|
347
|
+
row['nullable'] == 'Y')
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def create_table(name, options = {}) #:nodoc:
|
352
|
+
super(name, options)
|
353
|
+
seq_name = options[:sequence_name] || "#{name}_seq"
|
354
|
+
execute "CREATE SEQUENCE #{seq_name} START WITH 10000" unless options[:id] == false
|
355
|
+
end
|
356
|
+
|
357
|
+
def rename_table(name, new_name) #:nodoc:
|
358
|
+
execute "RENAME #{name} TO #{new_name}"
|
359
|
+
execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
|
360
|
+
end
|
361
|
+
|
362
|
+
def drop_table(name, options = {}) #:nodoc:
|
363
|
+
super(name)
|
364
|
+
seq_name = options[:sequence_name] || "#{name}_seq"
|
365
|
+
execute "DROP SEQUENCE #{seq_name}" rescue nil
|
366
|
+
end
|
367
|
+
|
368
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
369
|
+
execute "DROP INDEX #{index_name(table_name, options)}"
|
370
|
+
end
|
371
|
+
|
372
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
373
|
+
execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
|
374
|
+
end
|
375
|
+
|
376
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
377
|
+
change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
378
|
+
add_column_options!(change_column_sql, options)
|
379
|
+
execute(change_column_sql)
|
380
|
+
end
|
381
|
+
|
382
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
383
|
+
execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
|
384
|
+
end
|
385
|
+
|
386
|
+
def remove_column(table_name, column_name) #:nodoc:
|
387
|
+
execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
|
388
|
+
end
|
389
|
+
|
390
|
+
# Find a table's primary key and sequence.
|
391
|
+
# *Note*: Only primary key is implemented - sequence will be nil.
|
392
|
+
def pk_and_sequence_for(table_name)
|
393
|
+
(owner, table_name) = @connection.describe(table_name)
|
394
|
+
|
395
|
+
pks = select_values(<<-SQL, 'Primary Key')
|
396
|
+
select cc.column_name
|
397
|
+
from all_constraints c, all_cons_columns cc
|
398
|
+
where c.owner = '#{owner}'
|
399
|
+
and c.table_name = '#{table_name}'
|
400
|
+
and c.constraint_type = 'P'
|
401
|
+
and cc.owner = c.owner
|
402
|
+
and cc.constraint_name = c.constraint_name
|
403
|
+
SQL
|
404
|
+
|
405
|
+
# only support single column keys
|
406
|
+
pks.size == 1 ? [oracle_downcase(pks.first), nil] : nil
|
407
|
+
end
|
408
|
+
|
409
|
+
def structure_dump #:nodoc:
|
410
|
+
s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
|
411
|
+
structure << "create sequence #{seq.to_a.first.last};\n\n"
|
412
|
+
end
|
413
|
+
|
414
|
+
select_all("select table_name from user_tables").inject(s) do |structure, table|
|
415
|
+
ddl = "create table #{table.to_a.first.last} (\n "
|
416
|
+
cols = select_all(%Q{
|
417
|
+
select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
|
418
|
+
from user_tab_columns
|
419
|
+
where table_name = '#{table.to_a.first.last}'
|
420
|
+
order by column_id
|
421
|
+
}).map do |row|
|
422
|
+
col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
|
423
|
+
if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
|
424
|
+
col << "(#{row['data_precision'].to_i}"
|
425
|
+
col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
|
426
|
+
col << ')'
|
427
|
+
elsif row['data_type'].include?('CHAR')
|
428
|
+
col << "(#{row['data_length'].to_i})"
|
429
|
+
end
|
430
|
+
col << " default #{row['data_default']}" if !row['data_default'].nil?
|
431
|
+
col << ' not null' if row['nullable'] == 'N'
|
432
|
+
col
|
433
|
+
end
|
434
|
+
ddl << cols.join(",\n ")
|
435
|
+
ddl << ");\n\n"
|
436
|
+
structure << ddl
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def structure_drop #:nodoc:
|
441
|
+
s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
|
442
|
+
drop << "drop sequence #{seq.to_a.first.last};\n\n"
|
443
|
+
end
|
444
|
+
|
445
|
+
select_all("select table_name from user_tables").inject(s) do |drop, table|
|
446
|
+
drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
451
|
+
#
|
452
|
+
# Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
|
453
|
+
# queries. However, with those columns included in the SELECT DISTINCT list, you
|
454
|
+
# won't actually get a distinct list of the column you want (presuming the column
|
455
|
+
# has duplicates with multiple values for the ordered-by columns. So we use the
|
456
|
+
# FIRST_VALUE function to get a single (first) value for each column, effectively
|
457
|
+
# making every row the same.
|
458
|
+
#
|
459
|
+
# distinct("posts.id", "posts.created_at desc")
|
460
|
+
def distinct(columns, order_by)
|
461
|
+
return "DISTINCT #{columns}" if order_by.blank?
|
462
|
+
|
463
|
+
# construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
|
464
|
+
# FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
|
465
|
+
order_columns = order_by.split(',').map { |s| s.strip }.reject(&:blank?)
|
466
|
+
order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
|
467
|
+
"FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
|
468
|
+
end
|
469
|
+
sql = "DISTINCT #{columns}, "
|
470
|
+
sql << order_columns * ", "
|
471
|
+
end
|
472
|
+
|
473
|
+
# ORDER BY clause for the passed order option.
|
474
|
+
#
|
475
|
+
# Uses column aliases as defined by #distinct.
|
476
|
+
def add_order_by_for_association_limiting!(sql, options)
|
477
|
+
return sql if options[:order].blank?
|
478
|
+
|
479
|
+
order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
|
480
|
+
order.map! {|s| $1 if s =~ / (.*)/}
|
481
|
+
order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
|
482
|
+
|
483
|
+
sql << "ORDER BY #{order}"
|
484
|
+
end
|
485
|
+
|
486
|
+
private
|
487
|
+
|
488
|
+
def select(sql, name = nil)
|
489
|
+
cursor = execute(sql, name)
|
490
|
+
cols = cursor.get_col_names.map { |x| oracle_downcase(x) }
|
491
|
+
rows = []
|
492
|
+
|
493
|
+
while row = cursor.fetch
|
494
|
+
hash = Hash.new
|
495
|
+
|
496
|
+
cols.each_with_index do |col, i|
|
497
|
+
hash[col] =
|
498
|
+
case row[i]
|
499
|
+
when OCI8::LOB
|
500
|
+
name == 'Writable Large Object' ? row[i]: row[i].read
|
501
|
+
when OraDate
|
502
|
+
(row[i].hour == 0 and row[i].minute == 0 and row[i].second == 0) ?
|
503
|
+
row[i].to_date : row[i].to_time
|
504
|
+
else row[i]
|
505
|
+
end unless col == 'raw_rnum_'
|
506
|
+
end
|
507
|
+
|
508
|
+
rows << hash
|
509
|
+
end
|
510
|
+
|
511
|
+
rows
|
512
|
+
ensure
|
513
|
+
cursor.close if cursor
|
514
|
+
end
|
515
|
+
|
516
|
+
# Oracle column names by default are case-insensitive, but treated as upcase;
|
517
|
+
# for neatness, we'll downcase within Rails. EXCEPT that folks CAN quote
|
518
|
+
# their column names when creating Oracle tables, which makes then case-sensitive.
|
519
|
+
# I don't know anybody who does this, but we'll handle the theoretical case of a
|
520
|
+
# camelCase column name. I imagine other dbs handle this different, since there's a
|
521
|
+
# unit test that's currently failing test_oci.
|
522
|
+
def oracle_downcase(column_name)
|
523
|
+
column_name =~ /[a-z]/ ? column_name : column_name.downcase
|
524
|
+
end
|
525
|
+
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
|
531
|
+
class OCI8 #:nodoc:
|
532
|
+
|
533
|
+
# This OCI8 patch may not longer be required with the upcoming
|
534
|
+
# release of version 0.2.
|
535
|
+
class Cursor #:nodoc:
|
536
|
+
alias :define_a_column_pre_ar :define_a_column
|
537
|
+
def define_a_column(i)
|
538
|
+
case do_ocicall(@ctx) { @parms[i - 1].attrGet(OCI_ATTR_DATA_TYPE) }
|
539
|
+
when 8 : @stmt.defineByPos(i, String, 65535) # Read LONG values
|
540
|
+
when 187 : @stmt.defineByPos(i, OraDate) # Read TIMESTAMP values
|
541
|
+
when 108
|
542
|
+
if @parms[i - 1].attrGet(OCI_ATTR_TYPE_NAME) == 'XMLTYPE'
|
543
|
+
@stmt.defineByPos(i, String, 65535)
|
544
|
+
else
|
545
|
+
raise 'unsupported datatype'
|
546
|
+
end
|
547
|
+
else define_a_column_pre_ar i
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
# missing constant from oci8 < 0.1.14
|
553
|
+
OCI_PTYPE_UNK = 0 unless defined?(OCI_PTYPE_UNK)
|
554
|
+
|
555
|
+
# Uses the describeAny OCI call to find the target owner and table_name
|
556
|
+
# indicated by +name+, parsing through synonynms as necessary. Returns
|
557
|
+
# an array of [owner, table_name].
|
558
|
+
def describe(name)
|
559
|
+
@desc ||= @@env.alloc(OCIDescribe)
|
560
|
+
@desc.attrSet(OCI_ATTR_DESC_PUBLIC, -1) if VERSION >= '0.1.14'
|
561
|
+
@desc.describeAny(@svc, name.to_s, OCI_PTYPE_UNK) rescue raise %Q{"DESC #{name}" failed; does it exist?}
|
562
|
+
info = @desc.attrGet(OCI_ATTR_PARAM)
|
563
|
+
|
564
|
+
case info.attrGet(OCI_ATTR_PTYPE)
|
565
|
+
when OCI_PTYPE_TABLE, OCI_PTYPE_VIEW
|
566
|
+
owner = info.attrGet(OCI_ATTR_OBJ_SCHEMA)
|
567
|
+
table_name = info.attrGet(OCI_ATTR_OBJ_NAME)
|
568
|
+
[owner, table_name]
|
569
|
+
when OCI_PTYPE_SYN
|
570
|
+
schema = info.attrGet(OCI_ATTR_SCHEMA_NAME)
|
571
|
+
name = info.attrGet(OCI_ATTR_NAME)
|
572
|
+
describe(schema + '.' + name)
|
573
|
+
else raise %Q{"DESC #{name}" failed; not a table or view.}
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
end
|
578
|
+
|
579
|
+
|
580
|
+
# The OracleConnectionFactory factors out the code necessary to connect and
|
581
|
+
# configure an Oracle/OCI connection.
|
582
|
+
class OracleConnectionFactory #:nodoc:
|
583
|
+
def new_connection(username, password, database, async, prefetch_rows, cursor_sharing)
|
584
|
+
conn = OCI8.new username, password, database
|
585
|
+
conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
|
586
|
+
conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} rescue nil
|
587
|
+
conn.autocommit = true
|
588
|
+
conn.non_blocking = true if async
|
589
|
+
conn.prefetch_rows = prefetch_rows
|
590
|
+
conn.exec "alter session set cursor_sharing = #{cursor_sharing}" rescue nil
|
591
|
+
conn
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
|
596
|
+
# The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
|
597
|
+
# reset functionality. If a call to #exec fails, and autocommit is turned on
|
598
|
+
# (ie., we're not in the middle of a longer transaction), it will
|
599
|
+
# automatically reconnect and try again. If autocommit is turned off,
|
600
|
+
# this would be dangerous (as the earlier part of the implied transaction
|
601
|
+
# may have failed silently if the connection died) -- so instead the
|
602
|
+
# connection is marked as dead, to be reconnected on it's next use.
|
603
|
+
class OCI8AutoRecover < DelegateClass(OCI8) #:nodoc:
|
604
|
+
attr_accessor :active
|
605
|
+
alias :active? :active
|
606
|
+
|
607
|
+
cattr_accessor :auto_retry
|
608
|
+
class << self
|
609
|
+
alias :auto_retry? :auto_retry
|
610
|
+
end
|
611
|
+
@@auto_retry = false
|
612
|
+
|
613
|
+
def initialize(config, factory = OracleConnectionFactory.new)
|
614
|
+
@active = true
|
615
|
+
@username, @password, @database, = config[:username], config[:password], config[:database]
|
616
|
+
@async = config[:allow_concurrency]
|
617
|
+
@prefetch_rows = config[:prefetch_rows] || 100
|
618
|
+
@cursor_sharing = config[:cursor_sharing] || 'similar'
|
619
|
+
@factory = factory
|
620
|
+
@connection = @factory.new_connection @username, @password, @database, @async, @prefetch_rows, @cursor_sharing
|
621
|
+
super @connection
|
622
|
+
end
|
623
|
+
|
624
|
+
# Checks connection, returns true if active. Note that ping actively
|
625
|
+
# checks the connection, while #active? simply returns the last
|
626
|
+
# known state.
|
627
|
+
def ping
|
628
|
+
@connection.exec("select 1 from dual") { |r| nil }
|
629
|
+
@active = true
|
630
|
+
rescue
|
631
|
+
@active = false
|
632
|
+
raise
|
633
|
+
end
|
634
|
+
|
635
|
+
# Resets connection, by logging off and creating a new connection.
|
636
|
+
def reset!
|
637
|
+
logoff rescue nil
|
638
|
+
begin
|
639
|
+
@connection = @factory.new_connection @username, @password, @database, @async, @prefetch_rows, @cursor_sharing
|
640
|
+
__setobj__ @connection
|
641
|
+
@active = true
|
642
|
+
rescue
|
643
|
+
@active = false
|
644
|
+
raise
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
# ORA-00028: your session has been killed
|
649
|
+
# ORA-01012: not logged on
|
650
|
+
# ORA-03113: end-of-file on communication channel
|
651
|
+
# ORA-03114: not connected to ORACLE
|
652
|
+
LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
|
653
|
+
|
654
|
+
# Adds auto-recovery functionality.
|
655
|
+
#
|
656
|
+
# See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11
|
657
|
+
def exec(sql, *bindvars, &block)
|
658
|
+
should_retry = self.class.auto_retry? && autocommit?
|
659
|
+
|
660
|
+
begin
|
661
|
+
@connection.exec(sql, *bindvars, &block)
|
662
|
+
rescue OCIException => e
|
663
|
+
raise unless LOST_CONNECTION_ERROR_CODES.include?(e.code)
|
664
|
+
@active = false
|
665
|
+
raise unless should_retry
|
666
|
+
should_retry = false
|
667
|
+
reset! rescue nil
|
668
|
+
retry
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
end
|
673
|
+
|
674
|
+
rescue LoadError
|
675
|
+
# OCI8 driver is unavailable.
|
676
|
+
module ActiveRecord # :nodoc:
|
677
|
+
class Base
|
678
|
+
@@oracle_error_message = "Oracle/OCI libraries could not be loaded: #{$!.to_s}"
|
679
|
+
def self.oracle_connection(config) # :nodoc:
|
680
|
+
# Set up a reasonable error message
|
681
|
+
raise LoadError, @@oracle_error_message
|
682
|
+
end
|
683
|
+
def self.oci_connection(config) # :nodoc:
|
684
|
+
# Set up a reasonable error message
|
685
|
+
raise LoadError, @@oracle_error_message
|
686
|
+
end
|
687
|
+
end
|
688
|
+
end
|
689
|
+
end
|