Class: ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
- Inherits:
-
AbstractAdapter
- Object
- AbstractAdapter
- ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
- Defined in:
- lib/active_record/connection_adapters/oracle_enhanced_adapter.rb
Overview
This is an Oracle/OCI adapter for the ActiveRecord persistence framework. It relies upon the OCI8 driver, which works with Oracle 8i and above. Most recent development has been on Debian Linux against a 10g database, ActiveRecord 1.12.1 and OCI8 0.1.13. See: rubyforge.org/projects/ruby-oci8/
Usage notes:
-
Key generation assumes a “$table_name_seq” sequence is available for all tables; the sequence name can be changed using ActiveRecord::Base.set_sequence_name. When using Migrations, these sequences are created automatically.
-
Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently some hacks are employed to map data back to Date or Time in Ruby. If the column_name ends in _time it’s created as a Ruby Time. Else if the hours/minutes/seconds are 0, I make it a Ruby Date. Else it’s a Ruby Time. This is a bit nasty - but if you use Duck Typing you’ll probably not care very much. In 9i and up it’s tempting to map DATE to Date and TIMESTAMP to Time, but too many databases use DATE for both. Timezones and sub-second precision on timestamps are not supported.
-
Default values that are functions (such as “SYSDATE”) are not supported. This is a restriction of the way ActiveRecord supports default values.
-
Support for Oracle8 is limited by Rails’ use of ANSI join syntax, which is supported in Oracle9i and later. You will need to use #finder_sql for has_and_belongs_to_many associations to run against Oracle8.
Required parameters:
-
:username
-
:password
-
:database
Direct Known Subclasses
Constant Summary collapse
- @@emulate_booleans =
true
- @@emulate_dates =
false
- @@emulate_dates_by_column_name =
RSI: set to true if columns with DATE in their name should be emulated as date
false
- @@emulate_integers_by_column_name =
RSI: set to true if NUMBER columns with ID at the end of their name should be emulated as integers
false
- @@emulate_booleans_from_strings =
RSI: set to true if CHAR(1), VARCHAR2(1) columns or VARCHAR2 columns with FLAG or YN at the end of their name should be emulated as booleans
false
- @@string_to_date_format =
RSI: use to set NLS specific date formats which will be used when assigning string to :date and :datetime columns
@@string_to_time_format = nil
- @@default_sequence_start_value =
RSI: default sequence start with value
10000
Instance Attribute Summary collapse
-
#auto_retry ⇒ Object
Returns the value of attribute auto_retry.
Class Method Summary collapse
- .boolean_to_string(bool) ⇒ Object
- .is_boolean_column?(name, field_type, table_name = nil) ⇒ Boolean
- .is_date_column?(name, table_name = nil) ⇒ Boolean
- .is_integer_column?(name, table_name = nil) ⇒ Boolean
-
.valid_table_name?(name) ⇒ Boolean
unescaped table name should start with letter and contain letters, digits, _, $ or # can be prefixed with schema name.
Instance Method Summary collapse
-
#active? ⇒ Boolean
Returns true if the connection is active.
-
#adapter_name ⇒ Object
:nodoc:.
- #add_column(table_name, column_name, type, options = {}) ⇒ Object
-
#add_column_options!(sql, options) ⇒ Object
:nodoc:.
-
#add_comment(table_name, column_name, comment) ⇒ Object
RSI: table and column comments.
-
#add_limit_offset!(sql, options) ⇒ Object
:nodoc:.
-
#add_order_by_for_association_limiting!(sql, options) ⇒ Object
ORDER BY clause for the passed order option.
- #add_table_comment(table_name, comment) ⇒ Object
-
#begin_db_transaction ⇒ Object
:nodoc:.
-
#change_column(table_name, column_name, type, options = {}) ⇒ Object
:nodoc:.
-
#change_column_default(table_name, column_name, default) ⇒ Object
:nodoc:.
- #change_column_null(table_name, column_name, null, default = nil) ⇒ Object
- #clear_types_for_columns ⇒ Object
- #column_comment(table_name, column_name) ⇒ Object
-
#columns(table_name, name = nil) ⇒ Object
:nodoc:.
-
#commit_db_transaction ⇒ Object
:nodoc:.
-
#create_table(name, options = {}, &block) ⇒ Object
:nodoc:.
-
#current_database ⇒ Object
SCHEMA STATEMENTS ========================================.
-
#default_sequence_name(table, column) ⇒ Object
:nodoc:.
-
#disconnect! ⇒ Object
Disconnects from the database.
-
#distinct(columns, order_by) ⇒ Object
SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
-
#drop_table(name, options = {}) ⇒ Object
:nodoc:.
-
#execute(sql, name = nil) ⇒ Object
DATABASE STATEMENTS ======================================.
- #get_type_for_column(table_name, column_name) ⇒ Object
-
#ignore_table_columns(table_name, *args) ⇒ Object
RSI: set ignored columns for table.
- #ignored_table_columns(table_name) ⇒ Object
-
#indexes(table_name, name = nil) ⇒ Object
:nodoc:.
-
#insert_fixture(fixture, table_name) ⇒ Object
Inserts the given fixture into the table.
-
#is_date_column?(name, table_name = nil) ⇒ Boolean
RSI: instance method uses at first check if column type defined at class level.
-
#lob_order_by_expression(klass, order) ⇒ Object
RSI: change LOB column for ORDER BY clause just first 100 characters are taken for ordering.
-
#native_database_types ⇒ Object
:nodoc:.
-
#next_sequence_value(sequence_name) ⇒ Object
Returns the next sequence value from a sequence generator.
-
#pk_and_sequence_for(table_name) ⇒ Object
Find a table’s primary key and sequence.
-
#prefetch_primary_key?(table_name = nil) ⇒ Boolean
Returns true for Oracle adapter (since Oracle requires primary key values to be pre-fetched before insert).
-
#quote(value, column = nil) ⇒ Object
:nodoc:.
-
#quote_column_name(name) ⇒ Object
camelCase column names need to be quoted; not that anyone using Oracle would really do this, but handling this case means we pass the test…
-
#quote_date_with_to_date(value) ⇒ Object
RSI: should support that composite_primary_keys gem will pass date as string.
-
#quote_string(s) ⇒ Object
:nodoc:.
-
#quote_table_name(name) ⇒ Object
abstract_adapter calls quote_column_name from quote_table_name, so prevent that.
- #quote_timestamp_with_to_timestamp(value) ⇒ Object
- #quoted_false ⇒ Object
- #quoted_true ⇒ Object
- #raw_connection ⇒ Object
-
#reconnect! ⇒ Object
Reconnects to the database.
-
#remove_column(table_name, column_name) ⇒ Object
:nodoc:.
-
#remove_index(table_name, options = {}) ⇒ Object
:nodoc:.
-
#rename_column(table_name, column_name, new_column_name) ⇒ Object
:nodoc:.
-
#rename_table(name, new_name) ⇒ Object
:nodoc:.
-
#rollback_db_transaction ⇒ Object
:nodoc:.
-
#select_rows(sql, name = nil) ⇒ Object
Returns an array of arrays containing the field values.
-
#set_type_for_columns(table_name, column_type, *args) ⇒ Object
RSI: set explicit type for specified table columns.
-
#structure_drop ⇒ Object
:nodoc:.
-
#structure_dump ⇒ Object
:nodoc:.
-
#supports_migrations? ⇒ Boolean
:nodoc:.
- #table_alias_length ⇒ Object
- #table_comment(table_name) ⇒ Object
-
#tables(name = nil) ⇒ Object
RSI: changed select from user_tables to all_tables - much faster in large data dictionaries.
-
#write_lobs(table_name, klass, attributes) ⇒ Object
Writes LOB values from attributes, as indicated by the LOB columns of klass.
Instance Attribute Details
#auto_retry ⇒ Object
Returns the value of attribute auto_retry.
418 419 420 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 418 def auto_retry @auto_retry end |
Class Method Details
.boolean_to_string(bool) ⇒ Object
287 288 289 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 287 def self.boolean_to_string(bool) bool ? "Y" : "N" end |
.is_boolean_column?(name, field_type, table_name = nil) ⇒ Boolean
283 284 285 286 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 283 def self.is_boolean_column?(name, field_type, table_name = nil) return true if ["CHAR(1)","VARCHAR2(1)"].include?(field_type) field_type =~ /^VARCHAR2/ && (name =~ /_flag$/i || name =~ /_yn$/i) end |
.is_date_column?(name, table_name = nil) ⇒ Boolean
257 258 259 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 257 def self.is_date_column?(name, table_name = nil) name =~ /(^|_)date(_|$)/i end |
.is_integer_column?(name, table_name = nil) ⇒ Boolean
275 276 277 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 275 def self.is_integer_column?(name, table_name = nil) name =~ /(^|_)id$/i end |
.valid_table_name?(name) ⇒ Boolean
unescaped table name should start with letter and contain letters, digits, _, $ or # can be prefixed with schema name
349 350 351 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 349 def self.valid_table_name?(name) name.to_s =~ /^([A-Z_0-9]+\.)?[A-Z][A-Z_0-9\$#]*$/i ? true : false end |
Instance Method Details
#active? ⇒ Boolean
Returns true if the connection is active.
429 430 431 432 433 434 435 436 437 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 429 def active? # Pings the connection to check if it's still good. Note that an # #active? method is also available, but that simply returns the # last known state, which isn't good enough if the connection has # gone stale since the last use. @connection.ping rescue OracleEnhancedConnectionException false end |
#adapter_name ⇒ Object
:nodoc:
295 296 297 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 295 def adapter_name #:nodoc: 'OracleEnhanced' end |
#add_column(table_name, column_name, type, options = {}) ⇒ Object
730 731 732 733 734 735 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 730 def add_column(table_name, column_name, type, = {}) add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, [:limit], [:precision], [:scale])}" [:type] = type (add_column_sql, ) execute(add_column_sql) end |
#add_column_options!(sql, options) ⇒ Object
:nodoc:
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 868 def (sql, ) #:nodoc: type = [:type] || ((column = [:column]) && column.type) type = type && type.to_sym # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly if () if type == :text sql << " DEFAULT #{quote([:default])}" else # from abstract adapter sql << " DEFAULT #{quote([:default], [:column])}" end end # must explicitly add NULL or NOT NULL to allow change_column to work on migrations if [:null] == false sql << " NOT NULL" elsif [:null] == true sql << " NULL" unless type == :primary_key end end |
#add_comment(table_name, column_name, comment) ⇒ Object
RSI: table and column comments
775 776 777 778 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 775 def add_comment(table_name, column_name, comment) return if comment.blank? execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{column_name} IS '#{comment}'" end |
#add_limit_offset!(sql, options) ⇒ Object
:nodoc:
483 484 485 486 487 488 489 490 491 492 493 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 483 def add_limit_offset!(sql, ) #:nodoc: # RSI: added to_i for limit and offset to protect from SQL injection offset = ([:offset] || 0).to_i if limit = [:limit] limit = limit.to_i sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}" elsif offset > 0 sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}" end end |
#add_order_by_for_association_limiting!(sql, options) ⇒ Object
ORDER BY clause for the passed order option.
Uses column aliases as defined by #distinct.
914 915 916 917 918 919 920 921 922 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 914 def add_order_by_for_association_limiting!(sql, ) return sql if [:order].blank? order = [:order].split(',').collect { |s| s.strip }.reject(&:blank?) order.map! {|s| $1 if s =~ / (.*)/} order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ') sql << " ORDER BY #{order}" end |
#add_table_comment(table_name, comment) ⇒ Object
780 781 782 783 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 780 def add_table_comment(table_name, comment) return if comment.blank? execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS '#{comment}'" end |
#begin_db_transaction ⇒ Object
:nodoc:
467 468 469 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 467 def begin_db_transaction #:nodoc: @connection.autocommit = false end |
#change_column(table_name, column_name, type, options = {}) ⇒ Object
:nodoc:
751 752 753 754 755 756 757 758 759 760 761 762 763 764 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 751 def change_column(table_name, column_name, type, = {}) #:nodoc: column = column_for(table_name, column_name) # remove :null option if its value is the same as current column definition # otherwise Oracle will raise error if .has_key?(:null) && [:null] == column.null [:null] = nil end change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} #{type_to_sql(type, [:limit], [:precision], [:scale])}" [:type] = type (change_column_sql, ) execute(change_column_sql) end |
#change_column_default(table_name, column_name, default) ⇒ Object
:nodoc:
737 738 739 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 737 def change_column_default(table_name, column_name, default) #:nodoc: execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}" end |
#change_column_null(table_name, column_name, null, default = nil) ⇒ Object
741 742 743 744 745 746 747 748 749 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 741 def change_column_null(table_name, column_name, null, default = nil) column = column_for(table_name, column_name) unless null || default.nil? execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") end change_column table_name, column_name, column.sql_type, :null => null end |
#clear_types_for_columns ⇒ Object
617 618 619 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 617 def clear_types_for_columns @table_column_type = nil end |
#column_comment(table_name, column_name) ⇒ Object
794 795 796 797 798 799 800 801 802 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 794 def column_comment(table_name, column_name) (owner, table_name) = @connection.describe(table_name) select_value <<-SQL SELECT comments FROM all_col_comments WHERE owner = '#{owner}' AND table_name = '#{table_name}' AND column_name = '#{column_name.upcase}' SQL end |
#columns(table_name, name = nil) ⇒ Object
:nodoc:
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 621 def columns(table_name, name = nil) #:nodoc: # RSI: get ignored_columns by original table name ignored_columns = ignored_table_columns(table_name) (owner, desc_table_name) = @connection.describe(table_name) table_cols = <<-SQL select column_name as name, data_type as sql_type, data_default, nullable, decode(data_type, 'NUMBER', data_precision, 'FLOAT', data_precision, 'VARCHAR2', data_length, 'CHAR', data_length, null) as limit, decode(data_type, 'NUMBER', data_scale, null) as scale from all_tab_columns where owner = '#{owner}' and table_name = '#{desc_table_name}' order by column_id SQL # RSI: added deletion of ignored columns select_all(table_cols, name).delete_if do |row| ignored_columns && ignored_columns.include?(row['name'].downcase) end.map do |row| limit, scale = row['limit'], row['scale'] if limit || scale row['sql_type'] << "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")") end # clean up odd default spacing from Oracle if row['data_default'] row['data_default'].sub!(/^(.*?)\s*$/, '\1') row['data_default'].sub!(/^'(.*)'$/, '\1') row['data_default'] = nil if row['data_default'] =~ /^(null|empty_[bc]lob\(\))$/i end OracleEnhancedColumn.new(oracle_downcase(row['name']), row['data_default'], row['sql_type'], row['nullable'] == 'Y', # RSI: pass table name for table specific column definitions table_name, # RSI: pass column type if specified in class definition get_type_for_column(table_name, oracle_downcase(row['name']))) end end |
#commit_db_transaction ⇒ Object
:nodoc:
471 472 473 474 475 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 471 def commit_db_transaction #:nodoc: @connection.commit ensure @connection.autocommit = true end |
#create_table(name, options = {}, &block) ⇒ Object
:nodoc:
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 672 def create_table(name, = {}, &block) #:nodoc: create_sequence = [:id] != false column_comments = {} super(name, ) do |t| # store that primary key was defined in create_table block unless create_sequence class <<t attr_accessor :create_sequence def primary_key(*args) self.create_sequence = true super(*args) end end end # store column comments class <<t attr_accessor :column_comments def column(name, type, = {}) if [:comment] self.column_comments ||= {} self.column_comments[name] = [:comment] end super(name, type, ) end end result = block.call(t) create_sequence = create_sequence || t.create_sequence column_comments = t.column_comments if t.column_comments end seq_name = [:sequence_name] || quote_table_name("#{name}_seq") seq_start_value = [:sequence_start_value] || default_sequence_start_value execute "CREATE SEQUENCE #{seq_name} START WITH #{seq_start_value}" if create_sequence add_table_comment name, [:comment] column_comments.each do |column_name, comment| add_comment name, column_name, comment end end |
#current_database ⇒ Object
SCHEMA STATEMENTS ========================================
see: abstract/schema_statements.rb
553 554 555 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 553 def current_database #:nodoc: select_one("select sys_context('userenv','db_name') db from dual")["db"] end |
#default_sequence_name(table, column) ⇒ Object
:nodoc:
501 502 503 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 501 def default_sequence_name(table, column) #:nodoc: quote_table_name("#{table}_seq") end |
#disconnect! ⇒ Object
Disconnects from the database.
447 448 449 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 447 def disconnect! @connection.logoff rescue nil end |
#distinct(columns, order_by) ⇒ Object
SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT queries. However, with those columns included in the SELECT DISTINCT list, you won’t actually get a distinct list of the column you want (presuming the column has duplicates with multiple values for the ordered-by columns. So we use the FIRST_VALUE function to get a single (first) value for each column, effectively making every row the same.
distinct("posts.id", "posts.created_at desc")
898 899 900 901 902 903 904 905 906 907 908 909 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 898 def distinct(columns, order_by) return "DISTINCT #{columns}" if order_by.blank? # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT order_columns = order_by.split(',').map { |s| s.strip }.reject(&:blank?) order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i| "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__" end sql = "DISTINCT #{columns}, " sql << order_columns * ", " end |
#drop_table(name, options = {}) ⇒ Object
:nodoc:
720 721 722 723 724 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 720 def drop_table(name, = {}) #:nodoc: super(name) seq_name = [:sequence_name] || quote_table_name("#{name}_seq") execute "DROP SEQUENCE #{seq_name}" rescue nil end |
#execute(sql, name = nil) ⇒ Object
DATABASE STATEMENTS ======================================
see: abstract/database_statements.rb
456 457 458 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 456 def execute(sql, name = nil) #:nodoc: log(sql, name) { @connection.exec sql } end |
#get_type_for_column(table_name, column_name) ⇒ Object
613 614 615 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 613 def get_type_for_column(table_name, column_name) @table_column_type && @table_column_type[table_name] && @table_column_type[table_name][column_name.to_s.downcase] end |
#ignore_table_columns(table_name, *args) ⇒ Object
RSI: set ignored columns for table
592 593 594 595 596 597 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 592 def ignore_table_columns(table_name, *args) @ignore_table_columns ||= {} @ignore_table_columns[table_name] ||= [] @ignore_table_columns[table_name] += args.map{|a| a.to_s.downcase} @ignore_table_columns[table_name].uniq! end |
#ignored_table_columns(table_name) ⇒ Object
599 600 601 602 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 599 def ignored_table_columns(table_name) @ignore_table_columns ||= {} @ignore_table_columns[table_name] end |
#indexes(table_name, name = nil) ⇒ Object
:nodoc:
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 562 def indexes(table_name, name = nil) #:nodoc: (owner, table_name) = @connection.describe(table_name) result = select_all(<<-SQL, name) SELECT lower(i.index_name) as index_name, i.uniqueness, lower(c.column_name) as column_name FROM all_indexes i, all_ind_columns c WHERE i.table_name = '#{table_name}' AND i.owner = '#{owner}' AND i.table_owner = '#{owner}' AND c.index_name = i.index_name AND c.index_owner = i.owner AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc WHERE uc.index_name = i.index_name AND uc.owner = i.owner AND uc.constraint_type = 'P') ORDER BY i.index_name, c.column_position SQL current_index = nil indexes = [] result.each do |row| if current_index != row['index_name'] indexes << IndexDefinition.new(table_name.downcase, row['index_name'], row['uniqueness'] == "UNIQUE", []) current_index = row['index_name'] end indexes.last.columns << row['column_name'] end indexes end |
#insert_fixture(fixture, table_name) ⇒ Object
Inserts the given fixture into the table. Overridden to properly handle lobs.
507 508 509 510 511 512 513 514 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 507 def insert_fixture(fixture, table_name) super klass = fixture.class_name.constantize rescue nil if klass.respond_to?(:ancestors) && klass.ancestors.include?(ActiveRecord::Base) write_lobs(table_name, klass, fixture) end end |
#is_date_column?(name, table_name = nil) ⇒ Boolean
RSI: instance method uses at first check if column type defined at class level
261 262 263 264 265 266 267 268 269 270 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 261 def is_date_column?(name, table_name = nil) case get_type_for_column(table_name, name) when nil self.class.is_date_column?(name, table_name) when :date true else false end end |
#lob_order_by_expression(klass, order) ⇒ Object
RSI: change LOB column for ORDER BY clause just first 100 characters are taken for ordering
534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 534 def lob_order_by_expression(klass, order) return order if order.nil? changed = false new_order = order.to_s.strip.split(/, */).map do |order_by_col| column_name, asc_desc = order_by_col.split(/ +/) if column = klass.columns.detect { |col| col.name == column_name && col.sql_type =~ /LOB$/i} changed = true "DBMS_LOB.SUBSTR(#{column_name},100,1) #{asc_desc}" else order_by_col end end.join(', ') changed ? new_order : order end |
#native_database_types ⇒ Object
:nodoc:
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 303 def native_database_types #:nodoc: { :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY", :string => { :name => "VARCHAR2", :limit => 255 }, :text => { :name => "CLOB" }, :integer => { :name => "NUMBER", :limit => 38 }, :float => { :name => "NUMBER" }, :decimal => { :name => "DECIMAL" }, :datetime => { :name => "DATE" }, # RSI: changed to native TIMESTAMP type # :timestamp => { :name => "DATE" }, :timestamp => { :name => "TIMESTAMP" }, :time => { :name => "DATE" }, :date => { :name => "DATE" }, :binary => { :name => "BLOB" }, # RSI: if emulate_booleans_from_strings then store booleans in VARCHAR2 :boolean => emulate_booleans_from_strings ? { :name => "VARCHAR2", :limit => 1 } : { :name => "NUMBER", :limit => 1 } } end |
#next_sequence_value(sequence_name) ⇒ Object
Returns the next sequence value from a sequence generator. Not generally called directly; used by ActiveRecord to get the next primary key value when inserting a new database record (see #prefetch_primary_key?).
463 464 465 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 463 def next_sequence_value(sequence_name) select_one("select #{sequence_name}.nextval id from dual")['id'] end |
#pk_and_sequence_for(table_name) ⇒ Object
Find a table’s primary key and sequence. Note: Only primary key is implemented - sequence will be nil.
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 806 def pk_and_sequence_for(table_name) (owner, table_name) = @connection.describe(table_name) # RSI: changed select from all_constraints to user_constraints - much faster in large data dictionaries pks = select_values(<<-SQL, 'Primary Key') select cc.column_name from user_constraints c, user_cons_columns cc where c.owner = '#{owner}' and c.table_name = '#{table_name}' and c.constraint_type = 'P' and cc.owner = c.owner and cc.constraint_name = c.constraint_name SQL # only support single column keys pks.size == 1 ? [oracle_downcase(pks.first), nil] : nil end |
#prefetch_primary_key?(table_name = nil) ⇒ Boolean
Returns true for Oracle adapter (since Oracle requires primary key values to be pre-fetched before insert). See also #next_sequence_value.
497 498 499 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 497 def prefetch_primary_key?(table_name = nil) true end |
#quote(value, column = nil) ⇒ Object
:nodoc:
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 366 def quote(value, column = nil) #:nodoc: if value && column case column.type when :text, :binary %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()} # RSI: TIMESTAMP support when :timestamp (value) # RSI: NLS_DATE_FORMAT independent DATE support when :date, :time, :datetime quote_date_with_to_date(value) else super end elsif value.acts_like?(:date) quote_date_with_to_date(value) elsif value.acts_like?(:time) value.to_i == value.to_f ? quote_date_with_to_date(value) : (value) else super end end |
#quote_column_name(name) ⇒ Object
camelCase column names need to be quoted; not that anyone using Oracle would really do this, but handling this case means we pass the test…
342 343 344 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 342 def quote_column_name(name) #:nodoc: name.to_s =~ /[A-Z]/ ? "\"#{name}\"" : quote_oracle_reserved_words(name) end |
#quote_date_with_to_date(value) ⇒ Object
RSI: should support that composite_primary_keys gem will pass date as string
400 401 402 403 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 400 def quote_date_with_to_date(value) value = value.to_s(:db) if value.acts_like?(:date) || value.acts_like?(:time) "TO_DATE('#{value}','YYYY-MM-DD HH24:MI:SS')" end |
#quote_string(s) ⇒ Object
:nodoc:
362 363 364 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 362 def quote_string(s) #:nodoc: s.gsub(/'/, "''") end |
#quote_table_name(name) ⇒ Object
abstract_adapter calls quote_column_name from quote_table_name, so prevent that
354 355 356 357 358 359 360 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 354 def quote_table_name(name) if self.class.valid_table_name?(name) name else "\"#{name}\"" end end |
#quote_timestamp_with_to_timestamp(value) ⇒ Object
405 406 407 408 409 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 405 def (value) # add up to 9 digits of fractional seconds to inserted time value = "#{value.to_s(:db)}.#{("%.6f"%value.to_f).split('.')[1]}" if value.acts_like?(:time) "TO_TIMESTAMP('#{value}','YYYY-MM-DD HH24:MI:SS.FF6')" end |
#quoted_false ⇒ Object
394 395 396 397 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 394 def quoted_false return "'#{self.class.boolean_to_string(false)}'" if emulate_booleans_from_strings "0" end |
#quoted_true ⇒ Object
389 390 391 392 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 389 def quoted_true return "'#{self.class.boolean_to_string(true)}'" if emulate_booleans_from_strings "1" end |
#raw_connection ⇒ Object
424 425 426 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 424 def raw_connection @connection.raw_connection end |
#reconnect! ⇒ Object
Reconnects to the database.
440 441 442 443 444 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 440 def reconnect! @connection.reset! rescue OracleEnhancedConnectionException => e @logger.warn "#{adapter_name} automatic reconnection failed: #{e.}" end |
#remove_column(table_name, column_name) ⇒ Object
:nodoc:
770 771 772 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 770 def remove_column(table_name, column_name) #:nodoc: execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}" end |
#remove_index(table_name, options = {}) ⇒ Object
:nodoc:
726 727 728 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 726 def remove_index(table_name, = {}) #:nodoc: execute "DROP INDEX #{index_name(table_name, )}" end |
#rename_column(table_name, column_name, new_column_name) ⇒ Object
:nodoc:
766 767 768 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 766 def rename_column(table_name, column_name, new_column_name) #:nodoc: execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}" end |
#rename_table(name, new_name) ⇒ Object
:nodoc:
715 716 717 718 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 715 def rename_table(name, new_name) #:nodoc: execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}" execute "RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil end |
#rollback_db_transaction ⇒ Object
:nodoc:
477 478 479 480 481 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 477 def rollback_db_transaction #:nodoc: @connection.rollback ensure @connection.autocommit = true end |
#select_rows(sql, name = nil) ⇒ Object
Returns an array of arrays containing the field values. Order is the same as that returned by #columns.
330 331 332 333 334 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 330 def select_rows(sql, name = nil) # last parameter indicates to return also column list result, columns = select(sql, name, true) result.map{ |v| columns.map{|c| v[c]} } end |
#set_type_for_columns(table_name, column_type, *args) ⇒ Object
RSI: set explicit type for specified table columns
605 606 607 608 609 610 611 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 605 def set_type_for_columns(table_name, column_type, *args) @table_column_type ||= {} @table_column_type[table_name] ||= {} args.each do |col| @table_column_type[table_name][col.to_s.downcase] = column_type end end |
#structure_drop ⇒ Object
:nodoc:
857 858 859 860 861 862 863 864 865 866 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 857 def structure_drop #:nodoc: s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq| drop << "drop sequence #{seq.to_a.first.last};\n\n" end # RSI: changed select from user_tables to all_tables - much faster in large data dictionaries select_all("select table_name from all_tables where owner = sys_context('userenv','session_user')").inject(s) do |drop, table| drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n" end end |
#structure_dump ⇒ Object
:nodoc:
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 824 def structure_dump #:nodoc: s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq| structure << "create sequence #{seq.to_a.first.last};\n\n" end # RSI: changed select from user_tables to all_tables - much faster in large data dictionaries select_all("select table_name from all_tables where owner = sys_context('userenv','session_user')").inject(s) do |structure, table| ddl = "create table #{table.to_a.first.last} (\n " cols = select_all(%Q{ select column_name, data_type, data_length, char_used, char_length, data_precision, data_scale, data_default, nullable from user_tab_columns where table_name = '#{table.to_a.first.last}' order by column_id }).map do |row| col = "#{row['column_name'].downcase} #{row['data_type'].downcase}" if row['data_type'] =='NUMBER' and !row['data_precision'].nil? col << "(#{row['data_precision'].to_i}" col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil? col << ')' elsif row['data_type'].include?('CHAR') length = row['char_used'] == 'C' ? row['char_length'].to_i : row['data_length'].to_i col << "(#{length})" end col << " default #{row['data_default']}" if !row['data_default'].nil? col << ' not null' if row['nullable'] == 'N' col end ddl << cols.join(",\n ") ddl << ");\n\n" structure << ddl end end |
#supports_migrations? ⇒ Boolean
:nodoc:
299 300 301 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 299 def supports_migrations? #:nodoc: true end |
#table_alias_length ⇒ Object
324 325 326 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 324 def table_alias_length 30 end |
#table_comment(table_name) ⇒ Object
785 786 787 788 789 790 791 792 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 785 def table_comment(table_name) (owner, table_name) = @connection.describe(table_name) select_value <<-SQL SELECT comments FROM all_tab_comments WHERE owner = '#{owner}' AND table_name = '#{table_name}' SQL end |
#tables(name = nil) ⇒ Object
RSI: changed select from user_tables to all_tables - much faster in large data dictionaries
558 559 560 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 558 def tables(name = nil) #:nodoc: select_all("select lower(table_name) name from all_tables where owner = sys_context('userenv','session_user')").map {|t| t['name']} end |
#write_lobs(table_name, klass, attributes) ⇒ Object
Writes LOB values from attributes, as indicated by the LOB columns of klass.
517 518 519 520 521 522 523 524 525 526 527 528 529 530 |
# File 'lib/active_record/connection_adapters/oracle_enhanced_adapter.rb', line 517 def write_lobs(table_name, klass, attributes) id = quote(attributes[klass.primary_key]) klass.columns.select { |col| col.sql_type =~ /LOB$/i }.each do |col| value = attributes[col.name] # RSI: changed sequence of next two lines - should check if value is nil before converting to yaml next if value.nil? || (value == '') value = value.to_yaml if col.text? && klass.serialized_attributes[col.name] uncached do lob = select_one("SELECT #{col.name} FROM #{table_name} WHERE #{klass.primary_key} = #{id} FOR UPDATE", 'Writable Large Object')[col.name] @connection.write_lob(lob, value, col.type == :binary) end end end |