activerecord-jdbc-adapter-ficoh 1.3.21-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +462 -0
  4. data/.yardopts +4 -0
  5. data/Appraisals +36 -0
  6. data/CONTRIBUTING.md +49 -0
  7. data/Gemfile +68 -0
  8. data/History.md +1191 -0
  9. data/LICENSE.txt +25 -0
  10. data/README.md +277 -0
  11. data/RUNNING_TESTS.md +88 -0
  12. data/Rakefile +298 -0
  13. data/Rakefile.jdbc +20 -0
  14. data/activerecord-jdbc-adapter.gemspec +63 -0
  15. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  16. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  25. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  31. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  32. data/lib/activerecord-jdbc-adapter.rb +1 -0
  33. data/lib/arel/visitors/compat.rb +64 -0
  34. data/lib/arel/visitors/db2.rb +137 -0
  35. data/lib/arel/visitors/derby.rb +112 -0
  36. data/lib/arel/visitors/firebird.rb +79 -0
  37. data/lib/arel/visitors/h2.rb +25 -0
  38. data/lib/arel/visitors/hsqldb.rb +32 -0
  39. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  40. data/lib/arel/visitors/sql_server.rb +225 -0
  41. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  42. data/lib/arjdbc.rb +22 -0
  43. data/lib/arjdbc/db2.rb +4 -0
  44. data/lib/arjdbc/db2/adapter.rb +802 -0
  45. data/lib/arjdbc/db2/as400.rb +137 -0
  46. data/lib/arjdbc/db2/column.rb +177 -0
  47. data/lib/arjdbc/db2/connection_methods.rb +45 -0
  48. data/lib/arjdbc/derby.rb +3 -0
  49. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  50. data/lib/arjdbc/derby/adapter.rb +567 -0
  51. data/lib/arjdbc/derby/connection_methods.rb +16 -0
  52. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  53. data/lib/arjdbc/discover.rb +104 -0
  54. data/lib/arjdbc/firebird.rb +4 -0
  55. data/lib/arjdbc/firebird/adapter.rb +468 -0
  56. data/lib/arjdbc/firebird/connection_methods.rb +20 -0
  57. data/lib/arjdbc/h2.rb +3 -0
  58. data/lib/arjdbc/h2/adapter.rb +335 -0
  59. data/lib/arjdbc/h2/connection_methods.rb +22 -0
  60. data/lib/arjdbc/hsqldb.rb +3 -0
  61. data/lib/arjdbc/hsqldb/adapter.rb +304 -0
  62. data/lib/arjdbc/hsqldb/connection_methods.rb +23 -0
  63. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  64. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  65. data/lib/arjdbc/informix.rb +5 -0
  66. data/lib/arjdbc/informix/adapter.rb +160 -0
  67. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  68. data/lib/arjdbc/jdbc.rb +62 -0
  69. data/lib/arjdbc/jdbc/adapter.rb +997 -0
  70. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  71. data/lib/arjdbc/jdbc/arel_support.rb +149 -0
  72. data/lib/arjdbc/jdbc/base_ext.rb +34 -0
  73. data/lib/arjdbc/jdbc/callbacks.rb +52 -0
  74. data/lib/arjdbc/jdbc/column.rb +83 -0
  75. data/lib/arjdbc/jdbc/connection.rb +26 -0
  76. data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
  77. data/lib/arjdbc/jdbc/driver.rb +44 -0
  78. data/lib/arjdbc/jdbc/error.rb +75 -0
  79. data/lib/arjdbc/jdbc/extension.rb +69 -0
  80. data/lib/arjdbc/jdbc/java.rb +13 -0
  81. data/lib/arjdbc/jdbc/type_cast.rb +154 -0
  82. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  83. data/lib/arjdbc/mssql.rb +7 -0
  84. data/lib/arjdbc/mssql/adapter.rb +822 -0
  85. data/lib/arjdbc/mssql/column.rb +207 -0
  86. data/lib/arjdbc/mssql/connection_methods.rb +72 -0
  87. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  88. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  89. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  90. data/lib/arjdbc/mssql/types.rb +343 -0
  91. data/lib/arjdbc/mssql/utils.rb +82 -0
  92. data/lib/arjdbc/mysql.rb +3 -0
  93. data/lib/arjdbc/mysql/adapter.rb +998 -0
  94. data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
  95. data/lib/arjdbc/mysql/column.rb +167 -0
  96. data/lib/arjdbc/mysql/connection_methods.rb +137 -0
  97. data/lib/arjdbc/mysql/explain_support.rb +82 -0
  98. data/lib/arjdbc/mysql/schema_creation.rb +58 -0
  99. data/lib/arjdbc/oracle.rb +4 -0
  100. data/lib/arjdbc/oracle/adapter.rb +968 -0
  101. data/lib/arjdbc/oracle/column.rb +136 -0
  102. data/lib/arjdbc/oracle/connection_methods.rb +21 -0
  103. data/lib/arjdbc/postgresql.rb +3 -0
  104. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  105. data/lib/arjdbc/postgresql/adapter.rb +1498 -0
  106. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  107. data/lib/arjdbc/postgresql/base/oid.rb +412 -0
  108. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
  109. data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
  110. data/lib/arjdbc/postgresql/column.rb +640 -0
  111. data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
  112. data/lib/arjdbc/postgresql/explain_support.rb +53 -0
  113. data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
  114. data/lib/arjdbc/postgresql/oid_types.rb +265 -0
  115. data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
  116. data/lib/arjdbc/railtie.rb +11 -0
  117. data/lib/arjdbc/sqlite3.rb +3 -0
  118. data/lib/arjdbc/sqlite3/adapter.rb +654 -0
  119. data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
  120. data/lib/arjdbc/sqlite3/explain_support.rb +29 -0
  121. data/lib/arjdbc/sybase.rb +2 -0
  122. data/lib/arjdbc/sybase/adapter.rb +47 -0
  123. data/lib/arjdbc/tasks.rb +13 -0
  124. data/lib/arjdbc/tasks/database_tasks.rb +66 -0
  125. data/lib/arjdbc/tasks/databases.rake +91 -0
  126. data/lib/arjdbc/tasks/databases3.rake +239 -0
  127. data/lib/arjdbc/tasks/databases4.rake +39 -0
  128. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  129. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  130. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  131. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  132. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  133. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  134. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  135. data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
  136. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  137. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  138. data/lib/arjdbc/util/table_copier.rb +108 -0
  139. data/lib/arjdbc/version.rb +8 -0
  140. data/lib/generators/jdbc/USAGE +9 -0
  141. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  142. data/pom.xml +285 -0
  143. data/rails_generators/jdbc_generator.rb +15 -0
  144. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  145. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  146. data/rakelib/01-tomcat.rake +51 -0
  147. data/rakelib/02-test.rake +151 -0
  148. data/rakelib/bundler_ext.rb +11 -0
  149. data/rakelib/db.rake +58 -0
  150. data/rakelib/rails.rake +77 -0
  151. data/src/java/arjdbc/ArJdbcModule.java +288 -0
  152. data/src/java/arjdbc/db2/DB2Module.java +77 -0
  153. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
  154. data/src/java/arjdbc/derby/DerbyModule.java +180 -0
  155. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
  156. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  157. data/src/java/arjdbc/h2/H2Module.java +50 -0
  158. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
  159. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
  160. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -0
  161. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  162. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  163. data/src/java/arjdbc/jdbc/ConnectionFactory.java +77 -0
  164. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  165. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  166. data/src/java/arjdbc/jdbc/DriverWrapper.java +128 -0
  167. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
  168. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
  169. data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
  170. data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
  171. data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
  172. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
  173. data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
  174. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
  175. data/src/java/arjdbc/oracle/OracleModule.java +81 -0
  176. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
  177. data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
  178. data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
  179. data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
  180. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
  181. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
  182. data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
  183. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
  184. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  185. data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
  186. data/src/java/arjdbc/util/NumberUtils.java +50 -0
  187. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  188. data/src/java/arjdbc/util/QuotingUtils.java +139 -0
  189. data/src/java/arjdbc/util/StringCache.java +60 -0
  190. data/src/java/arjdbc/util/StringHelper.java +155 -0
  191. metadata +288 -0
@@ -0,0 +1,82 @@
1
+ module ArJdbc
2
+ module MySQL
3
+ module ExplainSupport
4
+ def supports_explain?
5
+ true
6
+ end
7
+
8
+ def explain(arel, binds = [])
9
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
10
+ start = Time.now.to_f
11
+ result = exec_query(sql, "EXPLAIN", binds)
12
+ elapsed = Time.now.to_f - start
13
+ ExplainPrettyPrinter.new.pp result, elapsed
14
+ end
15
+
16
+ # @private
17
+ class ExplainPrettyPrinter
18
+ # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
19
+ # MySQL shell:
20
+ #
21
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
22
+ # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
23
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
24
+ # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
25
+ # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
26
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
27
+ # 2 rows in set (0.00 sec)
28
+ #
29
+ # This is an exercise in Ruby hyperrealism :).
30
+ def pp(result, elapsed)
31
+ widths = compute_column_widths(result)
32
+ separator = build_separator(widths)
33
+
34
+ pp = []
35
+
36
+ pp << separator
37
+ pp << build_cells(result.columns, widths)
38
+ pp << separator
39
+
40
+ result.rows.each do |row|
41
+ pp << build_cells(row, widths)
42
+ end
43
+
44
+ pp << separator
45
+ pp << build_footer(result.rows.length, elapsed)
46
+
47
+ pp.join(sep = "\n") << sep
48
+ end
49
+
50
+ private
51
+
52
+ def compute_column_widths(result)
53
+ [].tap do |widths|
54
+ result.columns.each_with_index do |column, i|
55
+ cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
56
+ widths << cells_in_column.map(&:length).max
57
+ end
58
+ end
59
+ end
60
+
61
+ def build_separator(widths)
62
+ padding = 1; "+#{widths.map { |w| '-' * (w + (padding * 2 )) }.join('+')}+"
63
+ end
64
+
65
+ def build_cells(items, widths)
66
+ cells = []
67
+ items.each_with_index do |item, i|
68
+ item = 'NULL' if item.nil?
69
+ justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
70
+ cells << item.to_s.send(justifier, widths[i])
71
+ end
72
+ "|#{cells.join(' | ')}|"
73
+ end
74
+
75
+ def build_footer(nrows, elapsed)
76
+ rows_label = nrows == 1 ? 'row' : 'rows'
77
+ "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,58 @@
1
+ module ArJdbc
2
+ module MySQL
3
+ # @private copied from native adapter 4.0/4.1
4
+ class SchemaCreation < ::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation
5
+
6
+ def visit_AddColumn(o)
7
+ add_column_position!(super, column_options(o))
8
+ end
9
+
10
+ private
11
+
12
+ def visit_DropForeignKey(name)
13
+ "DROP FOREIGN KEY #{name}"
14
+ end
15
+
16
+ def visit_TableDefinition(o)
17
+ name = o.name
18
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
19
+
20
+ statements = o.columns.map { |c| accept c }
21
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
22
+
23
+ create_sql << "(#{statements.join(', ')}) " if statements.present?
24
+ create_sql << "#{o.options}"
25
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
26
+ create_sql
27
+ end if AR42
28
+
29
+ def visit_ChangeColumnDefinition(o)
30
+ column = o.column
31
+ options = o.options
32
+ sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
33
+ change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
34
+ add_column_options!(change_column_sql, options.merge(:column => column))
35
+ add_column_position!(change_column_sql, options)
36
+ end
37
+
38
+ def add_column_position!(sql, options)
39
+ if options[:first]
40
+ sql << " FIRST"
41
+ elsif options[:after]
42
+ sql << " AFTER #{quote_column_name(options[:after])}"
43
+ end
44
+ sql
45
+ end
46
+
47
+ def index_in_create(table_name, column_name, options)
48
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
49
+ "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
50
+ end
51
+ end
52
+ end
53
+
54
+ def schema_creation
55
+ SchemaCreation.new self
56
+ end
57
+
58
+ end if ::ActiveRecord::ConnectionAdapters::AbstractAdapter.const_defined? :SchemaCreation
@@ -0,0 +1,4 @@
1
+ require 'arjdbc'
2
+ require 'arjdbc/oracle/adapter'
3
+ require 'arjdbc/oracle/connection_methods'
4
+ ArJdbc.warn_unsupported_adapter 'oracle', [4, 2] # warns on AR >= 4.2
@@ -0,0 +1,968 @@
1
+ # NOTE: file contains code adapted from **oracle-enhanced** adapter, license follows
2
+ =begin
3
+ Copyright (c) 2008-2011 Graham Jenkins, Michael Schoen, Raimonds Simanovskis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ =end
24
+
25
+ ArJdbc.load_java_part :Oracle
26
+
27
+ module ArJdbc
28
+ module Oracle
29
+
30
+ require 'arjdbc/oracle/column'
31
+
32
+ # @private
33
+ def self.extended(adapter); initialize!; end
34
+
35
+ # @private
36
+ @@_initialized = nil
37
+
38
+ # @private
39
+ def self.initialize!
40
+ return if @@_initialized; @@_initialized = true
41
+
42
+ require 'arjdbc/util/serialized_attributes'
43
+ Util::SerializedAttributes.setup /LOB\(|LOB$/i, 'after_save_with_oracle_lob'
44
+ end
45
+
46
+ JdbcConnection = ::ActiveRecord::ConnectionAdapters::OracleJdbcConnection
47
+
48
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
49
+ def self.jdbc_connection_class; JdbcConnection end
50
+
51
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
52
+ def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::OracleColumn end
53
+
54
+ # @private
55
+ @@update_lob_values = true
56
+
57
+ # Updating records with LOB values (binary/text columns) in a separate
58
+ # statement can be disabled using :
59
+ #
60
+ # ArJdbc::Oracle.update_lob_values = false
61
+ #
62
+ # @note This only applies when prepared statements are not used.
63
+ def self.update_lob_values?; @@update_lob_values; end
64
+ # @see #update_lob_values?
65
+ def self.update_lob_values=(update); @@update_lob_values = update; end
66
+
67
+ # @see #update_lob_values?
68
+ # @see ArJdbc::Util::SerializedAttributes#update_lob_columns
69
+ def update_lob_value?(value, column = nil)
70
+ Oracle.update_lob_values? && ! prepared_statements? && ! ( value.nil? || value == '' )
71
+ end
72
+
73
+ # @private
74
+ @@emulate_booleans = true
75
+
76
+ # Boolean emulation can be disabled using :
77
+ #
78
+ # ArJdbc::Oracle.emulate_booleans = false
79
+ #
80
+ # @see ActiveRecord::ConnectionAdapters::OracleAdapter#emulate_booleans
81
+ def self.emulate_booleans?; @@emulate_booleans; end
82
+ # @deprecated Use {#emulate_booleans?} instead.
83
+ def self.emulate_booleans; @@emulate_booleans; end
84
+ # @see #emulate_booleans?
85
+ def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
86
+
87
+ class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition
88
+ def raw(*args)
89
+ options = args.extract_options!
90
+ column(args[0], 'raw', options)
91
+ end unless AR42
92
+
93
+ def raw(name, options={})
94
+ column(name, :raw, options)
95
+ end if AR42
96
+
97
+ def xml(*args)
98
+ options = args.extract_options!
99
+ column(args[0], 'xml', options)
100
+ end unless AR42
101
+
102
+ def raw(name, options={})
103
+ column(name, :xml, options)
104
+ end if AR42
105
+
106
+ def aliased_types(name, fallback)
107
+ # NOTE: disable aliasing :timestamp as :datetime :
108
+ fallback # 'timestamp' == name ? :datetime : fallback
109
+ end if AR42
110
+ end
111
+
112
+ def table_definition(*args)
113
+ new_table_definition(TableDefinition, *args)
114
+ end
115
+
116
+ def create_table_definition(name, temporary, options, as = nil)
117
+ TableDefinition.new native_database_types, name, temporary, options, as
118
+ end if AR42
119
+
120
+ def self.arel_visitor_type(config = nil)
121
+ ::Arel::Visitors::Oracle
122
+ end
123
+
124
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#bind_substitution
125
+ # @private
126
+ class BindSubstitution < ::Arel::Visitors::Oracle
127
+ include ::Arel::Visitors::BindVisitor
128
+ end if defined? ::Arel::Visitors::BindVisitor
129
+
130
+ ADAPTER_NAME = 'Oracle'.freeze
131
+
132
+ def adapter_name
133
+ ADAPTER_NAME
134
+ end
135
+
136
+ def initialize_type_map(m)
137
+ super
138
+
139
+ m.register_type(%r(NUMBER)i) do |sql_type|
140
+ scale = extract_scale(sql_type)
141
+ precision = extract_precision(sql_type)
142
+ limit = extract_limit(sql_type)
143
+ if scale == 0
144
+ if Oracle.emulate_booleans? && limit == 1
145
+ ActiveRecord::Type::Boolean.new
146
+ else
147
+ ActiveRecord::Type::Integer.new(:precision => precision, :limit => limit)
148
+ end
149
+ else
150
+ ActiveRecord::Type::Decimal.new(:precision => precision, :scale => scale)
151
+ end
152
+ end
153
+
154
+ register_class_with_limit m, %r(date)i, ActiveRecord::Type::DateTime
155
+ register_class_with_limit m, %r(raw)i, RawType
156
+ register_class_with_limit m, %r(timestamp)i, TimestampType
157
+
158
+ m.register_type %r(xmltype)i, XmlType.new
159
+ end if AR42
160
+
161
+ def clear_cache!
162
+ super
163
+ reload_type_map
164
+ end if AR42
165
+
166
+ # @private
167
+ class RawType < ActiveRecord::Type::String
168
+ def type; :raw end
169
+ end if AR42
170
+
171
+ # @private
172
+ class TimestampType < ActiveRecord::Type::DateTime
173
+ def type; :timestamp end
174
+ end if AR42
175
+
176
+ # @private
177
+ class XmlType < ActiveRecord::Type::String
178
+ def type; :xml end
179
+
180
+ def type_cast_for_database(value)
181
+ return unless value
182
+ Data.new(super)
183
+ end
184
+
185
+ class Data
186
+ def initialize(value)
187
+ @value = value
188
+ end
189
+ def to_s; @value end
190
+ end
191
+ end if AR42
192
+
193
+ NATIVE_DATABASE_TYPES = {
194
+ :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
195
+ :string => { :name => "VARCHAR2", :limit => 255 },
196
+ :text => { :name => "CLOB" },
197
+ :integer => { :name => "NUMBER", :limit => 38 },
198
+ :float => { :name => "NUMBER" },
199
+ :decimal => { :name => "DECIMAL" },
200
+ :datetime => { :name => "DATE" },
201
+ :timestamp => { :name => "TIMESTAMP" },
202
+ :time => { :name => "DATE" },
203
+ :date => { :name => "DATE" },
204
+ :binary => { :name => "BLOB" },
205
+ :boolean => { :name => "NUMBER", :limit => 1 },
206
+ :raw => { :name => "RAW", :limit => 2000 },
207
+ :xml => { :name => 'XMLTYPE' }
208
+ }
209
+
210
+ def native_database_types
211
+ super.merge(NATIVE_DATABASE_TYPES)
212
+ end
213
+
214
+ def modify_types(types)
215
+ super(types)
216
+ NATIVE_DATABASE_TYPES.each do |key, value|
217
+ types[key] = value.dup
218
+ end
219
+ types
220
+ end
221
+
222
+ # Prevent ORA-01795 for in clauses with more than 1000
223
+ def in_clause_length
224
+ 1000
225
+ end
226
+ alias_method :ids_in_list_limit, :in_clause_length
227
+
228
+ # @private
229
+ IDENTIFIER_LENGTH = 30
230
+
231
+ # maximum length of Oracle identifiers is 30
232
+ def table_alias_length; IDENTIFIER_LENGTH; end
233
+ def table_name_length; IDENTIFIER_LENGTH; end
234
+ def index_name_length; IDENTIFIER_LENGTH; end
235
+ def column_name_length; IDENTIFIER_LENGTH; end
236
+ def sequence_name_length; IDENTIFIER_LENGTH end
237
+
238
+ # @private
239
+ # Will take all or first 26 characters of table name and append _seq suffix
240
+ def default_sequence_name(table_name, primary_key = nil)
241
+ len = IDENTIFIER_LENGTH - 4
242
+ table_name.to_s.gsub (/(^|\.)([\w$-]{1,#{len}})([\w$-]*)$/), '\1\2_seq'
243
+ end
244
+
245
+ # @private
246
+ def default_trigger_name(table_name)
247
+ "#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_pkt"
248
+ end
249
+
250
+ # @override
251
+ def create_table(name, options = {})
252
+ super(name, options)
253
+ unless options[:id] == false
254
+ seq_name = options[:sequence_name] || default_sequence_name(name)
255
+ start_value = options[:sequence_start_value] || 10000
256
+ raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
257
+ execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{start_value}"
258
+ end
259
+ end
260
+
261
+ # @override
262
+ def rename_table(name, new_name)
263
+ if new_name.to_s.length > table_name_length
264
+ raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
265
+ end
266
+ if "#{new_name}_seq".to_s.length > sequence_name_length
267
+ raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
268
+ end
269
+ execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
270
+ execute_immediate("RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}", '-4043') # IF EXISTS
271
+ end
272
+
273
+ # @override
274
+ def drop_table(name, options = {})
275
+ outcome = super(name)
276
+ return outcome if name == 'schema_migrations'
277
+ seq_name = options.key?(:sequence_name) ? # pass nil/false - no sequence
278
+ options[:sequence_name] : default_sequence_name(name)
279
+ return outcome unless seq_name
280
+ execute_immediate("DROP SEQUENCE #{quote_table_name(seq_name)}", '-2289') # IF EXISTS
281
+ end
282
+
283
+ # @private
284
+ def execute_immediate(execute, sqlcode)
285
+ execute "BEGIN EXECUTE IMMEDIATE '#{execute}';" <<
286
+ " EXCEPTION WHEN OTHERS THEN IF SQLCODE != #{sqlcode} THEN RAISE; END IF;" <<
287
+ " END;"
288
+ end
289
+ private :execute_immediate
290
+
291
+ # @override
292
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
293
+ case type.to_sym
294
+ when :binary
295
+ # { BLOB | BINARY LARGE OBJECT } [ ( length [{K |M |G }] ) ]
296
+ # although Oracle does not like limit (length) with BLOB (or CLOB) :
297
+ #
298
+ # CREATE TABLE binaries (data BLOB, short_data BLOB(1024));
299
+ # ORA-00907: missing right parenthesis *
300
+ #
301
+ # TODO do we need to worry about NORMAL vs. non IN-TABLE BLOBs ?!
302
+ # http://dba.stackexchange.com/questions/8770/improve-blob-writing-performance-in-oracle-11g
303
+ # - if the LOB is smaller than 3900 bytes it can be stored inside the
304
+ # table row; by default this is enabled,
305
+ # unless you specify DISABLE STORAGE IN ROW
306
+ # - normal LOB - stored in a separate segment, outside of table,
307
+ # you may even put it in another tablespace;
308
+ super(type, nil, nil, nil)
309
+ when :text
310
+ super(type, nil, nil, nil)
311
+ else
312
+ super
313
+ end
314
+ end
315
+
316
+ def indexes(table, name = nil)
317
+ @connection.indexes(table, name, schema_owner)
318
+ end
319
+
320
+ # @note Only used with (non-AREL) ActiveRecord **2.3**.
321
+ # @see Arel::Visitors::Oracle
322
+ def add_limit_offset!(sql, options)
323
+ offset = options[:offset] || 0
324
+ if limit = options[:limit]
325
+ sql.replace "SELECT * FROM " <<
326
+ "(select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset + limit})" <<
327
+ " WHERE raw_rnum_ > #{offset}"
328
+ elsif offset > 0
329
+ sql.replace "SELECT * FROM " <<
330
+ "(select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_)" <<
331
+ " WHERE raw_rnum_ > #{offset}"
332
+ end
333
+ end if ::ActiveRecord::VERSION::MAJOR < 3
334
+
335
+ def current_user
336
+ @current_user ||= execute("SELECT sys_context('userenv', 'session_user') su FROM dual").first['su']
337
+ end
338
+
339
+ def current_database
340
+ @current_database ||= execute("SELECT sys_context('userenv', 'db_name') db FROM dual").first['db']
341
+ end
342
+
343
+ def current_schema
344
+ execute("SELECT sys_context('userenv', 'current_schema') schema FROM dual").first['schema']
345
+ end
346
+
347
+ def current_schema=(schema_owner)
348
+ execute("ALTER SESSION SET current_schema=#{schema_owner}")
349
+ end
350
+
351
+ # @override
352
+ def release_savepoint(name = nil)
353
+ # no RELEASE SAVEPOINT statement in Oracle (JDBC driver throws "Unsupported feature")
354
+ end
355
+
356
+ # @override
357
+ def add_index(table_name, column_name, options = {})
358
+ index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, options)
359
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{index_options}"
360
+ if index_type == 'UNIQUE'
361
+ unless quoted_column_names =~ /\(.*\)/
362
+ execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
363
+ end
364
+ end
365
+ end if AR42
366
+
367
+ # @private
368
+ def add_index_options(table_name, column_name, options = {})
369
+ column_names = Array(column_name)
370
+ index_name = index_name(table_name, column: column_names)
371
+
372
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
373
+
374
+ index_type = options[:unique] ? "UNIQUE" : ""
375
+ index_name = options[:name].to_s if options.key?(:name)
376
+ tablespace = '' # tablespace_for(:index, options[:tablespace])
377
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
378
+ index_options = '' # index_options = options[:options]
379
+
380
+ if index_name.to_s.length > max_index_length
381
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
382
+ end
383
+ if index_name_exists?(table_name, index_name, false)
384
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
385
+ end
386
+
387
+ quoted_column_names = column_names.map { |e| quote_column_name(e, true) }.join(", ")
388
+ [ index_name, index_type, quoted_column_names, tablespace, index_options ]
389
+ end if AR42
390
+
391
+ # @override
392
+ def remove_index(table_name, options = {})
393
+ index_name = index_name(table_name, options)
394
+ unless index_name_exists?(table_name, index_name, true)
395
+ # sometimes options can be String or Array with column names
396
+ options = {} unless options.is_a?(Hash)
397
+ if options.has_key? :name
398
+ options_without_column = options.dup
399
+ options_without_column.delete :column
400
+ index_name_without_column = index_name(table_name, options_without_column)
401
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
402
+ end
403
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
404
+ end
405
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
406
+ execute "DROP INDEX #{quote_column_name(index_name)}"
407
+ end if AR42
408
+
409
+ # @private
410
+ def remove_index(table_name, options = {})
411
+ execute "DROP INDEX #{index_name(table_name, options)}"
412
+ end unless AR42
413
+
414
+ def change_column_default(table_name, column_name, default)
415
+ execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
416
+ end
417
+
418
+ # @override
419
+ def add_column_options!(sql, options)
420
+ # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
421
+ if options_include_default?(options) && (column = options[:column]) && column.type == :text
422
+ sql << " DEFAULT #{quote(options.delete(:default))}"
423
+ end
424
+ super
425
+ end
426
+
427
+ # @override
428
+ def change_column(table_name, column_name, type, options = {})
429
+ change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} " <<
430
+ "MODIFY #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}"
431
+ add_column_options!(change_column_sql, options)
432
+ execute(change_column_sql)
433
+ end
434
+
435
+ # @override
436
+ def rename_column(table_name, column_name, new_column_name)
437
+ execute "ALTER TABLE #{quote_table_name(table_name)} " <<
438
+ "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
439
+ end
440
+
441
+ if ActiveRecord::VERSION::MAJOR >= 4
442
+
443
+ # @override
444
+ def remove_column(table_name, column_name, type = nil, options = {})
445
+ do_remove_column(table_name, column_name)
446
+ end
447
+
448
+ else
449
+
450
+ # @override
451
+ def remove_column(table_name, *column_names)
452
+ for column_name in column_names.flatten
453
+ do_remove_column(table_name, column_name)
454
+ end
455
+ end
456
+ alias remove_columns remove_column
457
+
458
+ end
459
+
460
+ def do_remove_column(table_name, column_name)
461
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
462
+ end
463
+ private :do_remove_column
464
+
465
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
466
+ #
467
+ # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
468
+ # queries. However, with those columns included in the SELECT DISTINCT list, you
469
+ # won't actually get a distinct list of the column you want (presuming the column
470
+ # has duplicates with multiple values for the ordered-by columns. So we use the
471
+ # FIRST_VALUE function to get a single (first) value for each column, effectively
472
+ # making every row the same.
473
+ #
474
+ # distinct("posts.id", "posts.created_at desc")
475
+ #
476
+ # @override
477
+ def distinct(columns, order_by)
478
+ "DISTINCT #{columns_for_distinct(columns, order_by)}"
479
+ end
480
+
481
+ # @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
482
+ def columns_for_distinct(columns, orders)
483
+ return columns if orders.blank?
484
+ if orders.is_a?(Array) # AR 3.x vs 4.x
485
+ orders = orders.map { |column| column.is_a?(String) ? column : column.to_sql }
486
+ else
487
+ orders = extract_order_columns(orders)
488
+ end
489
+ # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
490
+ # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
491
+ order_columns = orders.map do |c, i|
492
+ "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
493
+ end
494
+ columns = [ columns ]; columns.flatten!
495
+ columns.push( *order_columns ).join(', ')
496
+ end
497
+
498
+ # ORDER BY clause for the passed order option.
499
+ #
500
+ # Uses column aliases as defined by {#distinct}.
501
+ def add_order_by_for_association_limiting!(sql, options)
502
+ return sql if options[:order].blank?
503
+
504
+ order_columns = extract_order_columns(options[:order]) do |columns|
505
+ columns.map! { |s| $1 if s =~ / (.*)/ }; columns
506
+ end
507
+ order = order_columns.map { |s, i| "alias_#{i}__ #{s}" } # @see {#distinct}
508
+
509
+ sql << "ORDER BY #{order.join(', ')}"
510
+ end
511
+
512
+ def extract_order_columns(order_by)
513
+ columns = order_by.split(',')
514
+ columns.map!(&:strip); columns.reject!(&:blank?)
515
+ columns = yield(columns) if block_given?
516
+ columns.zip( (0...columns.size).to_a )
517
+ end
518
+ private :extract_order_columns
519
+
520
+ def temporary_table?(table_name)
521
+ select_value("SELECT temporary FROM user_tables WHERE table_name = '#{table_name.upcase}'") == 'Y'
522
+ end
523
+
524
+ def tables
525
+ @connection.tables(nil, oracle_schema)
526
+ end
527
+
528
+ # NOTE: better to use current_schema instead of the configured one ?!
529
+ def columns(table_name, name = nil)
530
+ @connection.columns_internal(table_name.to_s, nil, oracle_schema)
531
+ end
532
+
533
+ def tablespace(table_name)
534
+ select_value "SELECT tablespace_name FROM user_tables WHERE table_name='#{table_name.to_s.upcase}'"
535
+ end
536
+
537
+ def charset
538
+ database_parameters['NLS_CHARACTERSET']
539
+ end
540
+
541
+ def collation
542
+ database_parameters['NLS_COMP']
543
+ end
544
+
545
+ def database_parameters
546
+ return @database_parameters unless ( @database_parameters ||= {} ).empty?
547
+ @connection.execute_query_raw("SELECT * FROM NLS_DATABASE_PARAMETERS") do
548
+ |name, value| @database_parameters[name] = value
549
+ end
550
+ @database_parameters
551
+ end
552
+
553
+ # QUOTING ==================================================
554
+
555
+ # @override
556
+ def quote_table_name(name)
557
+ name.to_s.split('.').map{ |n| n.split('@').map{ |m| quote_column_name(m) }.join('@') }.join('.')
558
+ end
559
+
560
+ # @private
561
+ LOWER_CASE_ONLY = /\A[a-z][a-z_0-9\$#]*\Z/
562
+
563
+ # @override
564
+ def quote_column_name(name, handle_expression = false)
565
+ # if only valid lowercase column characters in name
566
+ if ( name = name.to_s ) =~ LOWER_CASE_ONLY
567
+ # putting double-quotes around an identifier causes Oracle to treat the
568
+ # identifier as case sensitive (otherwise assumes case-insensitivity) !
569
+ # all upper case is an exception, where double-quotes are meaningless
570
+ "\"#{name.upcase}\"" # name.upcase
571
+ else
572
+ if handle_expression
573
+ name =~ /^[a-z][a-z_0-9\$#\-]*$/i ? "\"#{name}\"" : name
574
+ else
575
+ # remove double quotes which cannot be used inside quoted identifier
576
+ "\"#{name.gsub('"', '')}\""
577
+ end
578
+ end
579
+ end
580
+
581
+ def unquote_table_name(name)
582
+ name = name[1...-1] if name[0, 1] == '"'
583
+ name.upcase == name ? name.downcase : name
584
+ end
585
+
586
+ # @override
587
+ def quote(value, column = nil)
588
+ return value if sql_literal?(value)
589
+
590
+ column_type = column && column.type
591
+ if column_type == :text || column_type == :binary
592
+ return 'NULL' if value.nil? || value == ''
593
+ if update_lob_value?(value, column)
594
+ if /(.*?)\([0-9]+\)/ =~ ( sql_type = column.sql_type )
595
+ %Q{empty_#{ $1.downcase }()}
596
+ else
597
+ %Q{empty_#{ sql_type.respond_to?(:downcase) ? sql_type.downcase : 'blob' }()}
598
+ end
599
+ else
600
+ "'#{quote_string(value.to_s)}'"
601
+ end
602
+ elsif column_type == :xml
603
+ "XMLTYPE('#{quote_string(value)}')" # XMLTYPE ?
604
+ elsif column_type == :raw
605
+ quote_raw(value)
606
+ else
607
+ if column.respond_to?(:primary) && column.primary && column.klass != String
608
+ return value.to_i.to_s
609
+ end
610
+
611
+ if column_type == :datetime || column_type == :time
612
+ if value.acts_like?(:time)
613
+ %Q{TO_DATE('#{get_time(value).strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')}
614
+ else
615
+ value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
616
+ end
617
+ elsif ( like_date = value.acts_like?(:date) ) || column_type == :date
618
+ if value.acts_like?(:time) # value.respond_to?(:strftime)
619
+ %Q{DATE'#{get_time(value).strftime("%Y-%m-%d")}'}
620
+ elsif like_date
621
+ %Q{DATE'#{quoted_date(value)}'} # DATE 'YYYY-MM-DD'
622
+ else
623
+ value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
624
+ end
625
+ elsif ( like_time = value.acts_like?(:time) ) || column_type == :timestamp
626
+ if like_time
627
+ %Q{TIMESTAMP'#{quoted_date(value, true)}'} # TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF'
628
+ else
629
+ value.blank? ? 'NULL' : %Q{TIMESTAMP'#{value}'} # assume correctly formated TIMESTAMP (string)
630
+ end
631
+ else
632
+ super
633
+ end
634
+ end
635
+ end
636
+
637
+ # Quote date/time values for use in SQL input.
638
+ # Includes milliseconds if the value is a Time responding to usec.
639
+ # @override
640
+ def quoted_date(value, time = nil)
641
+ if time || ( time.nil? && value.acts_like?(:time) )
642
+ usec = value.respond_to?(:usec) && (value.usec / 10000.0).round # .428000 -> .43
643
+ return "#{get_time(value).to_s(:db)}.#{sprintf("%02d", usec)}" if usec
644
+ # value.strftime("%Y-%m-%d %H:%M:%S")
645
+ end
646
+ value.to_s(:db)
647
+ end
648
+
649
+ def quote_raw(value)
650
+ value = value.unpack('C*') if value.is_a?(String)
651
+ "'#{value.map { |x| "%02X" % x }.join}'"
652
+ end
653
+
654
+ # @override
655
+ def supports_migrations?; true end
656
+
657
+ # @override
658
+ def supports_primary_key?; true end
659
+
660
+ # @override
661
+ def supports_savepoints?; true end
662
+
663
+ # @override
664
+ def supports_explain?; true end
665
+
666
+ # @override
667
+ def supports_views?; true end
668
+
669
+ def truncate(table_name, name = nil)
670
+ execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
671
+ end
672
+
673
+ def explain(arel, binds = [])
674
+ sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
675
+ return if sql =~ /FROM all_/
676
+ exec_update(sql, 'EXPLAIN', binds)
677
+ select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", 'EXPLAIN').join("\n")
678
+ end
679
+
680
+ def select(sql, name = nil, binds = [])
681
+ result = super # AR::Result (4.0) or Array (<= 3.2)
682
+ result.columns.delete('raw_rnum_') if result.respond_to?(:columns)
683
+ result.each { |row| row.delete('raw_rnum_') } # Hash rows even for AR::Result
684
+ result
685
+ end
686
+
687
+ @@do_not_prefetch_primary_key = {}
688
+
689
+ # Returns true for Oracle adapter (since Oracle requires primary key
690
+ # values to be pre-fetched before insert).
691
+ # @see #next_sequence_value
692
+ # @override
693
+ def prefetch_primary_key?(table_name = nil)
694
+ return true if table_name.nil?
695
+ do_not_prefetch_hash = @@do_not_prefetch_primary_key
696
+ do_not_prefetch = do_not_prefetch_hash[ table_name = table_name.to_s ]
697
+ if do_not_prefetch.nil?
698
+ owner, desc_table_name, db_link = describe(table_name)
699
+ do_not_prefetch_hash[table_name] = do_not_prefetch =
700
+ ! has_primary_key?(table_name, owner, desc_table_name, db_link) ||
701
+ has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
702
+ end
703
+ ! do_not_prefetch
704
+ end
705
+
706
+ # used to clear prefetch primary key flag for all tables
707
+ # @private
708
+ def clear_prefetch_primary_key; @@do_not_prefetch_primary_key = {} end
709
+
710
+ # @private
711
+ def has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
712
+ ! pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
713
+ end
714
+
715
+ # @private check if table has primary key trigger with _pkt suffix
716
+ def has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
717
+ (owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name
718
+
719
+ trigger_name = default_trigger_name(table_name).upcase
720
+ pkt_sql = "SELECT trigger_name FROM all_triggers#{db_link} WHERE owner = '#{owner}'" <<
721
+ " AND trigger_name = '#{trigger_name}'" <<
722
+ " AND table_owner = '#{owner}'" <<
723
+ " AND table_name = '#{desc_table_name}'" <<
724
+ " AND status = 'ENABLED'"
725
+ select_value(pkt_sql, 'Primary Key Trigger') ? true : false
726
+ end
727
+
728
+ # use in set_sequence_name to avoid fetching primary key value from sequence
729
+ AUTOGENERATED_SEQUENCE_NAME = 'autogenerated'.freeze
730
+
731
+ # Returns the next sequence value from a sequence generator. Not generally
732
+ # called directly; used by ActiveRecord to get the next primary key value
733
+ # when inserting a new database record (see #prefetch_primary_key?).
734
+ def next_sequence_value(sequence_name)
735
+ # if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
736
+ return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
737
+ sequence_name = quote_table_name(sequence_name)
738
+ sql = "SELECT #{sequence_name}.NEXTVAL id FROM dual"
739
+ log(sql, 'SQL') { @connection.next_sequence_value(sequence_name) }
740
+ end
741
+
742
+ def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil)
743
+ (owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name
744
+
745
+ seqs = "SELECT us.sequence_name" <<
746
+ " FROM all_sequences#{db_link} us" <<
747
+ " WHERE us.sequence_owner = '#{owner}'" <<
748
+ " AND us.sequence_name = '#{desc_table_name}_SEQ'"
749
+ seqs = select_values(seqs, 'Sequence')
750
+
751
+ # changed back from user_constraints to all_constraints for consistency
752
+ pks = "SELECT cc.column_name" <<
753
+ " FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc" <<
754
+ " WHERE c.owner = '#{owner}'" <<
755
+ " AND c.table_name = '#{desc_table_name}'" <<
756
+ " AND c.constraint_type = 'P'" <<
757
+ " AND cc.owner = c.owner" <<
758
+ " AND cc.constraint_name = c.constraint_name"
759
+ pks = select_values(pks, 'Primary Key')
760
+
761
+ # only support single column keys
762
+ pks.size == 1 ? [oracle_downcase(pks.first), oracle_downcase(seqs.first)] : nil
763
+ end
764
+ private :pk_and_sequence_for
765
+
766
+ # Returns just a table's primary key
767
+ def primary_key(table_name)
768
+ pk_and_sequence = pk_and_sequence_for(table_name)
769
+ pk_and_sequence && pk_and_sequence.first
770
+ end
771
+
772
+ # @override
773
+ def supports_foreign_keys?; true end
774
+
775
+ # @private
776
+ def disable_referential_integrity
777
+ sql_constraints = "SELECT constraint_name, owner, table_name FROM user_constraints WHERE constraint_type = 'R' AND status = 'ENABLED'"
778
+ old_constraints = select_all(sql_constraints)
779
+ begin
780
+ old_constraints.each do |constraint|
781
+ execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
782
+ end
783
+ yield
784
+ ensure
785
+ old_constraints.reverse_each do |constraint|
786
+ execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
787
+ end
788
+ end
789
+ end
790
+
791
+ # @override (for AR <= 3.0)
792
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
793
+ # if PK is already pre-fetched from sequence or if there is no PK :
794
+ if id_value || pk.nil?
795
+ execute(sql, name)
796
+ return id_value
797
+ end
798
+
799
+ if pk && use_insert_returning? # true by default on AR <= 3.0
800
+ sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO ?"
801
+ exec_insert_returning(sql, name, nil, pk)
802
+ else
803
+ execute(sql, name)
804
+ end
805
+ end
806
+ protected :insert_sql
807
+
808
+ # @override
809
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
810
+ unless id_value || pk.nil?
811
+ if pk && use_insert_returning?
812
+ sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO ?"
813
+ end
814
+ end
815
+ [ sql, binds ]
816
+ end
817
+
818
+ # @override
819
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
820
+ # NOTE: ActiveRecord::Relation calls our {#next_sequence_value}
821
+ # (from its `insert`) and passes the returned id_value here ...
822
+ sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
823
+ if id_value
824
+ exec_update(sql, name, binds)
825
+ return id_value
826
+ else
827
+ value = exec_insert(sql, name, binds, pk, sequence_name)
828
+ id_value || last_inserted_id(value)
829
+ end
830
+ end
831
+
832
+ # @override
833
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
834
+ if pk && use_insert_returning?
835
+ if sql.is_a?(String) && sql.index('RETURNING')
836
+ return exec_insert_returning(sql, name, binds, pk)
837
+ end
838
+ end
839
+ super(sql, name, binds) # assume no generated id for table
840
+ end
841
+
842
+ def exec_insert_returning(sql, name, binds, pk = nil)
843
+ sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)
844
+ if prepared_statements?
845
+ log(sql, name, binds) { @connection.execute_insert_returning(sql, binds) }
846
+ else
847
+ log(sql, name) { @connection.execute_insert_returning(sql, nil) }
848
+ end
849
+ end
850
+ # private :exec_insert_returning
851
+
852
+ def next_id_value(sql, sequence_name = nil)
853
+ # Assume the SQL contains a bind-variable for the ID
854
+ sequence_name ||= begin
855
+ # Extract the table from the insert SQL. Yuck.
856
+ table = extract_table_ref_from_insert_sql(sql)
857
+ default_sequence_name(table)
858
+ end
859
+ next_sequence_value(sequence_name)
860
+ end
861
+ private :next_id_value
862
+
863
+ def use_insert_returning?
864
+ if @use_insert_returning.nil?
865
+ @use_insert_returning = false
866
+ end
867
+ @use_insert_returning
868
+ end
869
+
870
+ private
871
+
872
+ def _execute(sql, name = nil)
873
+ if self.class.select?(sql)
874
+ @connection.execute_query_raw(sql)
875
+ elsif self.class.insert?(sql)
876
+ @connection.execute_insert(sql)
877
+ else
878
+ @connection.execute_update(sql)
879
+ end
880
+ end
881
+
882
+ def extract_table_ref_from_insert_sql(sql)
883
+ table = sql.split(" ", 4)[2]
884
+ if idx = table.index('(')
885
+ table = table[0...idx] # INTO table(col1, col2) ...
886
+ end
887
+ unquote_table_name(table)
888
+ end
889
+
890
+ # In Oracle, schemas are usually created under your username :
891
+ # http://www.oracle.com/technology/obe/2day_dba/schema/schema.htm
892
+ #
893
+ # A schema is the set of objects (tables, views, indexes, etc) that belongs
894
+ # to an user, often used as another way to refer to an Oracle user account.
895
+ #
896
+ # But allow separate configuration as "schema:" anyway (see #53)
897
+ def oracle_schema
898
+ if @config[:schema]
899
+ @config[:schema].to_s
900
+ elsif @config[:username]
901
+ @config[:username].to_s
902
+ end
903
+ end
904
+
905
+ # default schema owner
906
+ def schema_owner(force = true)
907
+ unless defined? @schema_owner
908
+ username = config[:username] ? config[:username].to_s : nil
909
+ username = jdbc_connection.meta_data.user_name if force && username.nil?
910
+ @schema_owner = username.nil? ? nil : username.upcase
911
+ end
912
+ @schema_owner
913
+ end
914
+
915
+ # do not force reading schema_owner as we're read on our own ...
916
+ def describe(table_name, owner = schema_owner(false))
917
+ @connection.describe(table_name, owner)
918
+ end
919
+
920
+ def oracle_downcase(column_name)
921
+ return nil if column_name.nil?
922
+ column_name =~ /[a-z]/ ? column_name : column_name.downcase
923
+ end
924
+
925
+ end
926
+ end
927
+
928
+ require 'arjdbc/util/quoted_cache'
929
+
930
+ module ActiveRecord::ConnectionAdapters
931
+ class OracleColumn < JdbcColumn
932
+ include ::ArJdbc::Oracle::ColumnMethods
933
+ # def returning_id?; @returning_id ||= nil end
934
+ # def returning_id!; @returning_id = true end
935
+ end
936
+
937
+ remove_const(:OracleAdapter) if const_defined?(:OracleAdapter)
938
+ class OracleAdapter < JdbcAdapter
939
+ include ::ArJdbc::Oracle
940
+ include ::ArJdbc::Util::QuotedCache
941
+
942
+ # By default, the OracleAdapter will consider all columns of type
943
+ # <tt>NUMBER(1)</tt> as boolean. If you wish to disable this :
944
+ #
945
+ # ActiveRecord::ConnectionAdapters::OracleAdapter.emulate_booleans = false
946
+ #
947
+ def self.emulate_booleans?; ::ArJdbc::Oracle.emulate_booleans?; end
948
+ def self.emulate_booleans; ::ArJdbc::Oracle.emulate_booleans?; end # oracle-enhanced
949
+ def self.emulate_booleans=(emulate); ::ArJdbc::Oracle.emulate_booleans = emulate; end
950
+
951
+ def initialize(*args)
952
+ ::ArJdbc::Oracle.initialize!
953
+ super # configure_connection happens in super
954
+
955
+ @use_insert_returning = config.key?(:insert_returning) ?
956
+ self.class.type_cast_config_to_boolean(config[:insert_returning]) : nil
957
+ end
958
+
959
+ # @private Temporary until ArJdbc::Oracle::Column is changed.
960
+ Column = OracleColumn
961
+ end
962
+ end
963
+
964
+ #module ArJdbc
965
+ # module Oracle
966
+ # Column = ::ActiveRecord::ConnectionAdapters::OracleColumn
967
+ # end
968
+ #end