PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/benchmarks/rails/substruct/lib/activerecord-jdbc-adapter-0.9.2/lib/jdbc_adapter/jdbc_oracle.rb

http://github.com/acangiano/ruby-benchmark-suite
Ruby | 377 lines | 289 code | 56 blank | 32 comment | 34 complexity | 456c462e7bd006439805ef47fb9e8a44 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, CC-BY-SA-3.0, LGPL-2.0, ISC, LGPL-2.1, GPL-2.0
  1. module ::JdbcSpec
  2. module ActiveRecordExtensions
  3. def oracle_connection(config)
  4. config[:port] ||= 1521
  5. config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database]}"
  6. config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
  7. jdbc_connection(config)
  8. end
  9. end
  10. module Oracle
  11. def self.extended(mod)
  12. unless @lob_callback_added
  13. ActiveRecord::Base.class_eval do
  14. def after_save_with_oracle_lob
  15. self.class.columns.select { |c| c.sql_type =~ /LOB\(|LOB$/i }.each do |c|
  16. value = self[c.name]
  17. value = value.to_yaml if unserializable_attribute?(c.name, c)
  18. next if value.nil? || (value == '')
  19. connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
  20. end
  21. end
  22. end
  23. ActiveRecord::Base.after_save :after_save_with_oracle_lob
  24. @lob_callback_added = true
  25. end
  26. mod.class_eval do
  27. alias_chained_method :insert, :query_dirty, :jdbc_oracle_insert
  28. end
  29. end
  30. def self.adapter_matcher(name, *)
  31. name =~ /oracle/i ? self : false
  32. end
  33. def self.column_selector
  34. [/oracle/i, lambda {|cfg,col| col.extend(::JdbcSpec::Oracle::Column)}]
  35. end
  36. module Column
  37. def type_cast(value)
  38. return nil if value.nil?
  39. case type
  40. when :datetime then JdbcSpec::Oracle::Column.string_to_time(value, self.class)
  41. else
  42. super
  43. end
  44. end
  45. def type_cast_code(var_name)
  46. case type
  47. when :datetime then "JdbcSpec::Oracle::Column.string_to_time(#{var_name}, self.class)"
  48. else
  49. super
  50. end
  51. end
  52. def self.string_to_time(string, klass)
  53. time = klass.string_to_time(string)
  54. guess_date_or_time(time)
  55. end
  56. def self.guess_date_or_time(value)
  57. (value.hour == 0 && value.min == 0 && value.sec == 0) ?
  58. new_date(value.year, value.month, value.day) : value
  59. end
  60. private
  61. def simplified_type(field_type)
  62. case field_type
  63. when /^number\(1\)$/i then :boolean
  64. when /char/i then :string
  65. when /float|double/i then :float
  66. when /int/i then :integer
  67. when /num|dec|real/i then @scale == 0 ? :integer : :decimal
  68. when /date|time/i then :datetime
  69. when /clob/i then :text
  70. when /blob/i then :binary
  71. end
  72. end
  73. # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
  74. def default_value(value)
  75. return nil unless value
  76. # Not sure why we need this for Oracle?
  77. value = value.strip
  78. return nil if value == "null"
  79. # sysdate default should be treated like a null value
  80. return nil if value.downcase == "sysdate"
  81. # jdbc returns column default strings with actual single quotes around the value.
  82. return $1 if value =~ /^'(.*)'$/
  83. value
  84. end
  85. end
  86. def adapter_name
  87. 'oracle'
  88. end
  89. def table_alias_length
  90. 30
  91. end
  92. def default_sequence_name(table, column = nil) #:nodoc:
  93. "#{table}_seq"
  94. end
  95. def create_table(name, options = {}) #:nodoc:
  96. super(name, options)
  97. seq_name = options[:sequence_name] || "#{name}_seq"
  98. raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
  99. execute "CREATE SEQUENCE #{seq_name} START WITH 10000" unless options[:id] == false
  100. end
  101. def rename_table(name, new_name) #:nodoc:
  102. execute "RENAME #{name} TO #{new_name}"
  103. execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
  104. end
  105. def drop_table(name, options = {}) #:nodoc:
  106. super(name)
  107. seq_name = options[:sequence_name] || "#{name}_seq"
  108. execute "DROP SEQUENCE #{seq_name}" rescue nil
  109. end
  110. def recreate_database(name)
  111. tables.each{ |table| drop_table(table) }
  112. end
  113. def drop_database(name)
  114. recreate_database(name)
  115. end
  116. def jdbc_oracle_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
  117. if id_value || pk.nil? # Pre-assigned id or table without a primary key
  118. execute sql, name
  119. else # Assume the sql contains a bind-variable for the id
  120. # Extract the table from the insert sql. Yuck.
  121. table = sql.split(" ", 4)[2].gsub('"', '')
  122. sequence_name ||= default_sequence_name(table)
  123. id_value = select_one("select #{sequence_name}.nextval id from dual")['id'].to_i
  124. log(sql, name) do
  125. @connection.execute_id_insert(sql,id_value)
  126. end
  127. end
  128. id_value
  129. end
  130. def indexes(table, name = nil)
  131. @connection.indexes(table, name, @connection.connection.meta_data.user_name)
  132. end
  133. def _execute(sql, name = nil)
  134. case sql.strip
  135. when /\A\(?\s*(select|show)/i then
  136. @connection.execute_query(sql)
  137. else
  138. @connection.execute_update(sql)
  139. end
  140. end
  141. def modify_types(tp)
  142. tp[:primary_key] = "NUMBER(38) NOT NULL PRIMARY KEY"
  143. tp[:integer] = { :name => "NUMBER", :limit => 38 }
  144. tp[:datetime] = { :name => "DATE" }
  145. tp[:timestamp] = { :name => "DATE" }
  146. tp[:time] = { :name => "DATE" }
  147. tp[:date] = { :name => "DATE" }
  148. tp
  149. end
  150. def add_limit_offset!(sql, options) #:nodoc:
  151. offset = options[:offset] || 0
  152. if limit = options[:limit]
  153. sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
  154. elsif offset > 0
  155. sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
  156. end
  157. end
  158. def current_database #:nodoc:
  159. select_one("select sys_context('userenv','db_name') db from dual")["db"]
  160. end
  161. def remove_index(table_name, options = {}) #:nodoc:
  162. execute "DROP INDEX #{index_name(table_name, options)}"
  163. end
  164. def change_column_default(table_name, column_name, default) #:nodoc:
  165. execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
  166. end
  167. def add_column_options!(sql, options) #:nodoc:
  168. # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
  169. if options_include_default?(options) && (column = options[:column]) && column.type == :text
  170. sql << " DEFAULT #{quote(options.delete(:default))}"
  171. end
  172. super
  173. end
  174. def change_column(table_name, column_name, type, options = {}) #:nodoc:
  175. change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
  176. add_column_options!(change_column_sql, options)
  177. execute(change_column_sql)
  178. end
  179. def rename_column(table_name, column_name, new_column_name) #:nodoc:
  180. execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
  181. end
  182. def remove_column(table_name, column_name) #:nodoc:
  183. execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
  184. end
  185. def structure_dump #:nodoc:
  186. s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
  187. structure << "create sequence #{seq.to_a.first.last};\n\n"
  188. end
  189. select_all("select table_name from user_tables").inject(s) do |structure, table|
  190. ddl = "create table #{table.to_a.first.last} (\n "
  191. cols = select_all(%Q{
  192. select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
  193. from user_tab_columns
  194. where table_name = '#{table.to_a.first.last}'
  195. order by column_id
  196. }).map do |row|
  197. row = row.inject({}) do |h,args|
  198. h[args[0].downcase] = args[1]
  199. h
  200. end
  201. col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
  202. if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
  203. col << "(#{row['data_precision'].to_i}"
  204. col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
  205. col << ')'
  206. elsif row['data_type'].include?('CHAR')
  207. col << "(#{row['data_length'].to_i})"
  208. end
  209. col << " default #{row['data_default']}" if !row['data_default'].nil?
  210. col << ' not null' if row['nullable'] == 'N'
  211. col
  212. end
  213. ddl << cols.join(",\n ")
  214. ddl << ");\n\n"
  215. structure << ddl
  216. end
  217. end
  218. def structure_drop #:nodoc:
  219. s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
  220. drop << "drop sequence #{seq.to_a.first.last};\n\n"
  221. end
  222. select_all("select table_name from user_tables").inject(s) do |drop, table|
  223. drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
  224. end
  225. end
  226. # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
  227. #
  228. # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
  229. # queries. However, with those columns included in the SELECT DISTINCT list, you
  230. # won't actually get a distinct list of the column you want (presuming the column
  231. # has duplicates with multiple values for the ordered-by columns. So we use the
  232. # FIRST_VALUE function to get a single (first) value for each column, effectively
  233. # making every row the same.
  234. #
  235. # distinct("posts.id", "posts.created_at desc")
  236. def distinct(columns, order_by)
  237. return "DISTINCT #{columns}" if order_by.blank?
  238. # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
  239. # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
  240. order_columns = order_by.split(',').map { |s| s.strip }.reject(&:blank?)
  241. order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
  242. "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
  243. end
  244. sql = "DISTINCT #{columns}, "
  245. sql << order_columns * ", "
  246. end
  247. # ORDER BY clause for the passed order option.
  248. #
  249. # Uses column aliases as defined by #distinct.
  250. def add_order_by_for_association_limiting!(sql, options)
  251. return sql if options[:order].blank?
  252. order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
  253. order.map! {|s| $1 if s =~ / (.*)/}
  254. order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
  255. sql << "ORDER BY #{order}"
  256. end
  257. def tables
  258. @connection.tables(nil, oracle_schema)
  259. end
  260. def columns(table_name, name=nil)
  261. @connection.columns_internal(table_name, name, oracle_schema)
  262. end
  263. # QUOTING ==================================================
  264. #
  265. # see: abstract/quoting.rb
  266. # Camelcase column names need to be quoted.
  267. # Nonquoted identifiers can contain only alphanumeric characters from your
  268. # database character set and the underscore (_), dollar sign ($), and pound sign (#).
  269. # Database links can also contain periods (.) and "at" signs (@).
  270. # Oracle strongly discourages you from using $ and # in nonquoted identifiers.
  271. # Source: http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/sql_elements008.htm
  272. def quote_column_name(name) #:nodoc:
  273. name.to_s =~ /^[a-z0-9_$#]+$/ ? name.to_s : "\"#{name}\""
  274. end
  275. def quote_string(string) #:nodoc:
  276. string.gsub(/'/, "''")
  277. end
  278. def quote(value, column = nil) #:nodoc:
  279. if column && [:text, :binary].include?(column.type)
  280. if /(.*?)\([0-9]+\)/ =~ column.sql_type
  281. %Q{empty_#{ $1.downcase }()}
  282. else
  283. %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
  284. end
  285. else
  286. if column.respond_to?(:primary) && column.primary
  287. return value.to_i.to_s
  288. end
  289. quoted = super
  290. if value.acts_like?(:date) || value.acts_like?(:time)
  291. quoted = "#{quoted_date(value)}"
  292. end
  293. quoted
  294. end
  295. end
  296. def quoted_date(value)
  297. %Q{TIMESTAMP'#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
  298. end
  299. def quoted_true #:nodoc:
  300. '1'
  301. end
  302. def quoted_false #:nodoc:
  303. '0'
  304. end
  305. private
  306. # In Oracle, schemas are created under your username:
  307. # http://www.oracle.com/technology/obe/2day_dba/schema/schema.htm
  308. def oracle_schema
  309. @config[:username].to_s if @config[:username]
  310. end
  311. def select(sql, name=nil)
  312. records = execute(sql,name)
  313. records.each do |col|
  314. col.delete('raw_rnum_')
  315. end
  316. records
  317. end
  318. end
  319. end