activerecord-oracle_enhanced-adapter 1.5.6 → 1.6.0

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/History.md +107 -0
  4. data/README.md +271 -174
  5. data/VERSION +1 -1
  6. data/activerecord-oracle_enhanced-adapter.gemspec +26 -22
  7. data/lib/active_record/connection_adapters/{oracle_enhanced_column.rb → oracle_enhanced/column.rb} +14 -63
  8. data/lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb +65 -0
  9. data/lib/active_record/connection_adapters/{oracle_enhanced_connection.rb → oracle_enhanced/connection.rb} +2 -2
  10. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +347 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +257 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dirty.rb +40 -0
  13. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_creation.rb → oracle_enhanced/schema_creation.rb} +17 -16
  14. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +95 -0
  15. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_dumper.rb → oracle_enhanced/schema_dumper.rb} +4 -32
  16. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +546 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +65 -0
  18. data/lib/active_record/connection_adapters/{oracle_enhanced_structure_dump.rb → oracle_enhanced/structure_dump.rb} +26 -4
  19. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +1 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +159 -66
  21. data/lib/active_record/oracle_enhanced/type/integer.rb +13 -0
  22. data/lib/active_record/oracle_enhanced/type/raw.rb +13 -0
  23. data/lib/active_record/oracle_enhanced/type/timestamp.rb +11 -0
  24. data/lib/activerecord-oracle_enhanced-adapter.rb +1 -1
  25. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +6 -31
  26. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +1 -1
  27. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +2 -2
  28. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +2 -2
  29. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +63 -63
  30. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +1 -1
  31. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +7 -13
  32. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +25 -178
  33. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +14 -5
  34. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +1 -0
  35. data/spec/spec_config.yaml.template +10 -0
  36. data/spec/spec_helper.rb +21 -10
  37. metadata +27 -23
  38. data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +0 -77
  39. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -350
  40. data/lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb +0 -262
  41. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -45
  42. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -197
  43. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -450
  44. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
  45. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
  46. /data/lib/active_record/connection_adapters/{oracle_enhanced_cpk.rb → oracle_enhanced/cpk.rb} +0 -0
  47. /data/lib/active_record/connection_adapters/{oracle_enhanced_database_tasks.rb → oracle_enhanced/database_tasks.rb} +0 -0
  48. /data/lib/active_record/connection_adapters/{oracle_enhanced_jdbc_connection.rb → oracle_enhanced/jdbc_connection.rb} +0 -0
  49. /data/lib/active_record/connection_adapters/{oracle_enhanced_oci_connection.rb → oracle_enhanced/oci_connection.rb} +0 -0
  50. /data/lib/active_record/connection_adapters/{oracle_enhanced_procedures.rb → oracle_enhanced/procedures.rb} +0 -0
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,17 @@
1
- require 'rubygems'
1
+ require "rubygems"
2
2
  require "bundler"
3
+ require "yaml"
3
4
  Bundler.setup(:default, :development)
4
5
 
5
6
  $:.unshift(File.expand_path('../../lib', __FILE__))
7
+ config_path = File.expand_path('../spec_config.yaml', __FILE__)
8
+ if File.exist?(config_path)
9
+ puts "==> Loading config from #{config_path}"
10
+ config = YAML.load_file(config_path)
11
+ else
12
+ puts "==> Loading config from ENV or use default"
13
+ config = {"rails" => {}, "database" => {}}
14
+ end
6
15
 
7
16
  require 'rspec'
8
17
 
@@ -13,10 +22,10 @@ elsif RUBY_ENGINE == 'jruby'
13
22
  puts "==> Running specs with JRuby version #{JRUBY_VERSION}"
14
23
  end
15
24
 
16
- ENV['RAILS_GEM_VERSION'] ||= '4.0-master'
25
+ ENV['RAILS_GEM_VERSION'] ||= config["rails"]["gem_version"] || '4.0-master'
17
26
  NO_COMPOSITE_PRIMARY_KEYS = true
18
27
 
19
- puts "==> Running specs with Rails version #{ENV['RAILS_GEM_VERSION']}"
28
+ puts "==> Selected Rails version #{ENV['RAILS_GEM_VERSION']}"
20
29
 
21
30
  require 'active_record'
22
31
 
@@ -32,6 +41,8 @@ require 'logger'
32
41
  require 'active_record/connection_adapters/oracle_enhanced_adapter'
33
42
  require 'ruby-plsql'
34
43
 
44
+ puts "==> Effective ActiveRecord version #{ActiveRecord::VERSION::STRING}"
45
+
35
46
  module LoggerSpecHelper
36
47
  def set_logger
37
48
  @logger = MockLogger.new
@@ -109,12 +120,12 @@ module SchemaSpecHelper
109
120
  end
110
121
  end
111
122
 
112
- DATABASE_NAME = ENV['DATABASE_NAME'] || 'orcl'
113
- DATABASE_HOST = ENV['DATABASE_HOST']
114
- DATABASE_PORT = ENV['DATABASE_PORT']
115
- DATABASE_USER = ENV['DATABASE_USER'] || 'oracle_enhanced'
116
- DATABASE_PASSWORD = ENV['DATABASE_PASSWORD'] || 'oracle_enhanced'
117
- DATABASE_SYS_PASSWORD = ENV['DATABASE_SYS_PASSWORD'] || 'admin'
123
+ DATABASE_NAME = config["database"]["name"] || ENV['DATABASE_NAME'] || 'orcl'
124
+ DATABASE_HOST = config["database"]["host"] || ENV['DATABASE_HOST'] || "127.0.0.1"
125
+ DATABASE_PORT = config["database"]["port"] || ENV['DATABASE_PORT'] || 1521
126
+ DATABASE_USER = config["database"]["user"] || ENV['DATABASE_USER'] || 'oracle_enhanced'
127
+ DATABASE_PASSWORD = config["database"]["password"] || ENV['DATABASE_PASSWORD'] || 'oracle_enhanced'
128
+ DATABASE_SYS_PASSWORD = config["database"]["sys_password"] || ENV['DATABASE_SYS_PASSWORD'] || 'admin'
118
129
 
119
130
  CONNECTION_PARAMS = {
120
131
  :adapter => "oracle_enhanced",
@@ -148,7 +159,7 @@ DATABASE_NON_DEFAULT_TABLESPACE = ENV['DATABASE_NON_DEFAULT_TABLESPACE'] || "SYS
148
159
 
149
160
  # set default time zone in TZ environment variable
150
161
  # which will be used to set session time zone
151
- ENV['TZ'] ||= 'Europe/Riga'
162
+ ENV['TZ'] ||= config["timezone"] || 'Europe/Riga'
152
163
 
153
164
  # ActiveRecord::Base.logger = Logger.new(STDOUT)
154
165
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-oracle_enhanced-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.6
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raimonds Simanovskis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-31 00:00:00.000000000 Z
11
+ date: 2015-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jeweler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.8'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.8'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -211,25 +211,28 @@ files:
211
211
  - VERSION
212
212
  - activerecord-oracle_enhanced-adapter.gemspec
213
213
  - lib/active_record/connection_adapters/emulation/oracle_adapter.rb
214
+ - lib/active_record/connection_adapters/oracle_enhanced/column.rb
215
+ - lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb
216
+ - lib/active_record/connection_adapters/oracle_enhanced/connection.rb
217
+ - lib/active_record/connection_adapters/oracle_enhanced/context_index.rb
218
+ - lib/active_record/connection_adapters/oracle_enhanced/cpk.rb
219
+ - lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb
220
+ - lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb
221
+ - lib/active_record/connection_adapters/oracle_enhanced/dirty.rb
222
+ - lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb
223
+ - lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb
224
+ - lib/active_record/connection_adapters/oracle_enhanced/procedures.rb
225
+ - lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb
226
+ - lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb
227
+ - lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb
228
+ - lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb
229
+ - lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb
230
+ - lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb
231
+ - lib/active_record/connection_adapters/oracle_enhanced/version.rb
214
232
  - lib/active_record/connection_adapters/oracle_enhanced_adapter.rb
215
- - lib/active_record/connection_adapters/oracle_enhanced_column.rb
216
- - lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb
217
- - lib/active_record/connection_adapters/oracle_enhanced_connection.rb
218
- - lib/active_record/connection_adapters/oracle_enhanced_context_index.rb
219
- - lib/active_record/connection_adapters/oracle_enhanced_cpk.rb
220
- - lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb
221
- - lib/active_record/connection_adapters/oracle_enhanced_database_tasks.rb
222
- - lib/active_record/connection_adapters/oracle_enhanced_dirty.rb
223
- - lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb
224
- - lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb
225
- - lib/active_record/connection_adapters/oracle_enhanced_procedures.rb
226
- - lib/active_record/connection_adapters/oracle_enhanced_schema_creation.rb
227
- - lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb
228
- - lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb
229
- - lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb
230
- - lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb
231
- - lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb
232
- - lib/active_record/connection_adapters/oracle_enhanced_version.rb
233
+ - lib/active_record/oracle_enhanced/type/integer.rb
234
+ - lib/active_record/oracle_enhanced/type/raw.rb
235
+ - lib/active_record/oracle_enhanced/type/timestamp.rb
233
236
  - lib/activerecord-oracle_enhanced-adapter.rb
234
237
  - spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb
235
238
  - spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb
@@ -244,6 +247,7 @@ files:
244
247
  - spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb
245
248
  - spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb
246
249
  - spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb
250
+ - spec/spec_config.yaml.template
247
251
  - spec/spec_helper.rb
248
252
  homepage: http://github.com/rsim/oracle-enhanced
249
253
  licenses:
@@ -265,7 +269,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
265
269
  version: '0'
266
270
  requirements: []
267
271
  rubyforge_project:
268
- rubygems_version: 2.4.6
272
+ rubygems_version: 2.4.8
269
273
  signing_key:
270
274
  specification_version: 4
271
275
  summary: Oracle enhanced adapter for ActiveRecord
@@ -1,77 +0,0 @@
1
- module ActiveRecord #:nodoc:
2
- module ConnectionAdapters #:nodoc:
3
- module OracleEnhancedColumnDumper #:nodoc:
4
-
5
- def self.included(base) #:nodoc:
6
- base.class_eval do
7
- private
8
- alias_method_chain :column_spec, :oracle_enhanced
9
- alias_method_chain :prepare_column_options, :oracle_enhanced
10
- alias_method_chain :migration_keys, :oracle_enhanced
11
-
12
- def oracle_enhanced_adapter?
13
- # return original method if not using 'OracleEnhanced'
14
- if (rails_env = defined?(Rails.env) ? Rails.env : (defined?(RAILS_ENV) ? RAILS_ENV : nil)) &&
15
- ActiveRecord::Base.configurations[rails_env] &&
16
- ActiveRecord::Base.configurations[rails_env]['adapter'] != 'oracle_enhanced'
17
- return false
18
- else
19
- return true
20
- end
21
- end
22
- end
23
- end
24
-
25
- def column_spec_with_oracle_enhanced(column, types)
26
- # return original method if not using 'OracleEnhanced'
27
- return column_spec_without_oracle_enhanced(column, types) unless oracle_enhanced_adapter?
28
-
29
- spec = prepare_column_options(column, types)
30
- (spec.keys - [:name, :type]).each do |k|
31
- key_s = (k == :virtual_type ? "type: " : "#{k.to_s}: ")
32
- spec[k] = key_s + spec[k]
33
- end
34
- spec
35
- end
36
-
37
- def prepare_column_options_with_oracle_enhanced(column, types)
38
- # return original method if not using 'OracleEnhanced'
39
- return prepare_column_options_without_oracle_enhanced(column, types) unless oracle_enhanced_adapter?
40
-
41
- spec = {}
42
-
43
- spec[:name] = column.name.inspect
44
- spec[:type] = column.virtual? ? 'virtual' : column.type.to_s
45
- spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit] && column.type != :decimal
46
- spec[:precision] = column.precision.inspect if !column.precision.nil?
47
- spec[:scale] = column.scale.inspect if !column.scale.nil?
48
- spec[:null] = 'false' if !column.null
49
- spec[:as] = column.virtual_column_data_default if column.virtual?
50
- spec[:default] = default_string(column.default) if column.has_default? && !column.virtual?
51
-
52
- if column.virtual?
53
- # Supports backwards compatibility with older OracleEnhancedAdapter versions where 'NUMBER' virtual column type is not included in dump
54
- if column.sql_type != "NUMBER" || ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.number_datatype_coercion != :decimal
55
- spec[:virtual_type] = column.type.inspect
56
- end
57
- end
58
-
59
- spec
60
- end
61
-
62
- def migration_keys_with_oracle_enhanced
63
- # TODO `& column_specs.map(&:keys).flatten` should be exetuted here
64
- # return original method if not using 'OracleEnhanced'
65
- return migration_keys_without_oracle_enhanced unless oracle_enhanced_adapter?
66
-
67
- [:name, :limit, :precision, :scale, :default, :null, :as, :virtual_type]
68
- end
69
-
70
-
71
- end
72
- end
73
- end
74
-
75
- ActiveRecord::ConnectionAdapters::ColumnDumper.class_eval do
76
- include ActiveRecord::ConnectionAdapters::OracleEnhancedColumnDumper
77
- end
@@ -1,350 +0,0 @@
1
- module ActiveRecord
2
- module ConnectionAdapters
3
- module OracleEnhancedContextIndex
4
-
5
- # Define full text index with Oracle specific CONTEXT index type
6
- #
7
- # Oracle CONTEXT index by default supports full text indexing of one column.
8
- # This method allows full text index creation also on several columns
9
- # as well as indexing related table columns by generating stored procedure
10
- # that concatenates all columns for indexing as well as generating trigger
11
- # that will update main index column to trigger reindexing of record.
12
- #
13
- # Use +contains+ ActiveRecord model instance method to add CONTAINS where condition
14
- # and order by score of matched results.
15
- #
16
- # Options:
17
- #
18
- # * <tt>:name</tt>
19
- # * <tt>:index_column</tt>
20
- # * <tt>:index_column_trigger_on</tt>
21
- # * <tt>:tablespace</tt>
22
- # * <tt>:sync</tt> - 'MANUAL', 'EVERY "interval-string"' or 'ON COMMIT' (defaults to 'MANUAL').
23
- # * <tt>:lexer</tt> - Lexer options (e.g. <tt>:type => 'BASIC_LEXER', :base_letter => true</tt>).
24
- # * <tt>:wordlist</tt> - Wordlist options (e.g. <tt>:type => 'BASIC_WORDLIST', :prefix_index => true</tt>).
25
- # * <tt>:transactional</tt> - When +true+, the CONTAINS operator will process inserted and updated rows.
26
- #
27
- # ===== Examples
28
- #
29
- # ====== Creating single column index
30
- # add_context_index :posts, :title
31
- # search with
32
- # Post.contains(:title, 'word')
33
- #
34
- # ====== Creating index on several columns
35
- # add_context_index :posts, [:title, :body]
36
- # search with (use first column as argument for contains method but it will search in all index columns)
37
- # Post.contains(:title, 'word')
38
- #
39
- # ====== Creating index on several columns with dummy index column and commit option
40
- # add_context_index :posts, [:title, :body], :index_column => :all_text, :sync => 'ON COMMIT'
41
- # search with
42
- # Post.contains(:all_text, 'word')
43
- #
44
- # ====== Creating index with trigger option (will reindex when specified columns are updated)
45
- # add_context_index :posts, [:title, :body], :index_column => :all_text, :sync => 'ON COMMIT',
46
- # :index_column_trigger_on => [:created_at, :updated_at]
47
- # search with
48
- # Post.contains(:all_text, 'word')
49
- #
50
- # ====== Creating index on multiple tables
51
- # add_context_index :posts,
52
- # [:title, :body,
53
- # # specify aliases always with AS keyword
54
- # "SELECT comments.author AS comment_author, comments.body AS comment_body FROM comments WHERE comments.post_id = :id"
55
- # ],
56
- # :name => 'post_and_comments_index',
57
- # :index_column => :all_text, :index_column_trigger_on => [:updated_at, :comments_count],
58
- # :sync => 'ON COMMIT'
59
- # search in any table columns
60
- # Post.contains(:all_text, 'word')
61
- # search in specified column
62
- # Post.contains(:all_text, "aaa within title")
63
- # Post.contains(:all_text, "bbb within comment_author")
64
- #
65
- # ====== Creating index using lexer
66
- # add_context_index :posts, :title, :lexer => { :type => 'BASIC_LEXER', :base_letter => true, ... }
67
- #
68
- # ====== Creating index using wordlist
69
- # add_context_index :posts, :title, :wordlist => { :type => 'BASIC_WORDLIST', :prefix_index => true, ... }
70
- #
71
- # ====== Creating transactional index (will reindex changed rows when querying)
72
- # add_context_index :posts, :title, :transactional => true
73
- #
74
- def add_context_index(table_name, column_name, options = {})
75
- self.all_schema_indexes = nil
76
- column_names = Array(column_name)
77
- index_name = options[:name] || index_name(table_name, :column => options[:index_column] || column_names,
78
- # CONEXT index name max length is 25
79
- :identifier_max_length => 25)
80
-
81
- quoted_column_name = quote_column_name(options[:index_column] || column_names.first)
82
- if options[:index_column_trigger_on]
83
- raise ArgumentError, "Option :index_column should be specified together with :index_column_trigger_on option" \
84
- unless options[:index_column]
85
- create_index_column_trigger(table_name, index_name, options[:index_column], options[:index_column_trigger_on])
86
- end
87
-
88
- sql = "CREATE INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
89
- sql << " (#{quoted_column_name})"
90
- sql << " INDEXTYPE IS CTXSYS.CONTEXT"
91
- parameters = []
92
- if column_names.size > 1
93
- procedure_name = default_datastore_procedure(index_name)
94
- datastore_name = default_datastore_name(index_name)
95
- create_datastore_procedure(table_name, procedure_name, column_names, options)
96
- create_datastore_preference(datastore_name, procedure_name)
97
- parameters << "DATASTORE #{datastore_name} SECTION GROUP CTXSYS.AUTO_SECTION_GROUP"
98
- end
99
- if options[:tablespace]
100
- storage_name = default_storage_name(index_name)
101
- create_storage_preference(storage_name, options[:tablespace])
102
- parameters << "STORAGE #{storage_name}"
103
- end
104
- if options[:sync]
105
- parameters << "SYNC(#{options[:sync]})"
106
- end
107
- if options[:lexer] && (lexer_type = options[:lexer][:type])
108
- lexer_name = default_lexer_name(index_name)
109
- (lexer_options = options[:lexer].dup).delete(:type)
110
- create_lexer_preference(lexer_name, lexer_type, lexer_options)
111
- parameters << "LEXER #{lexer_name}"
112
- end
113
- if options[:wordlist] && (wordlist_type = options[:wordlist][:type])
114
- wordlist_name = default_wordlist_name(index_name)
115
- (wordlist_options = options[:wordlist].dup).delete(:type)
116
- create_wordlist_preference(wordlist_name, wordlist_type, wordlist_options)
117
- parameters << "WORDLIST #{wordlist_name}"
118
- end
119
- if options[:transactional]
120
- parameters << "TRANSACTIONAL"
121
- end
122
- unless parameters.empty?
123
- sql << " PARAMETERS ('#{parameters.join(' ')}')"
124
- end
125
- execute sql
126
- end
127
-
128
- # Drop full text index with Oracle specific CONTEXT index type
129
- def remove_context_index(table_name, options = {})
130
- self.all_schema_indexes = nil
131
- unless Hash === options # if column names passed as argument
132
- options = {:column => Array(options)}
133
- end
134
- index_name = options[:name] || index_name(table_name,
135
- :column => options[:index_column] || options[:column], :identifier_max_length => 25)
136
- execute "DROP INDEX #{index_name}"
137
- drop_ctx_preference(default_datastore_name(index_name))
138
- drop_ctx_preference(default_storage_name(index_name))
139
- procedure_name = default_datastore_procedure(index_name)
140
- execute "DROP PROCEDURE #{quote_table_name(procedure_name)}" rescue nil
141
- drop_index_column_trigger(index_name)
142
- end
143
-
144
- private
145
-
146
- def create_datastore_procedure(table_name, procedure_name, column_names, options)
147
- quoted_table_name = quote_table_name(table_name)
148
- select_queries, column_names = column_names.partition { |c| c.to_s =~ /^\s*SELECT\s+/i }
149
- select_queries = select_queries.map { |s| s.strip.gsub(/\s+/, ' ') }
150
- keys, selected_columns = parse_select_queries(select_queries)
151
- quoted_column_names = (column_names+keys).map{|col| quote_column_name(col)}
152
- execute compress_lines(<<-SQL)
153
- CREATE OR REPLACE PROCEDURE #{quote_table_name(procedure_name)}
154
- (p_rowid IN ROWID,
155
- p_clob IN OUT NOCOPY CLOB) IS
156
- -- add_context_index_parameters #{(column_names+select_queries).inspect}#{!options.empty? ? ', ' << options.inspect[1..-2] : ''}
157
- #{
158
- selected_columns.map do |cols|
159
- cols.map do |col|
160
- raise ArgumentError, "Alias #{col} too large, should be 28 or less characters long" unless col.length <= 28
161
- "l_#{col} VARCHAR2(32767);\n"
162
- end.join
163
- end.join
164
- } BEGIN
165
- FOR r1 IN (
166
- SELECT #{quoted_column_names.join(', ')}
167
- FROM #{quoted_table_name}
168
- WHERE #{quoted_table_name}.ROWID = p_rowid
169
- ) LOOP
170
- #{
171
- (column_names.map do |col|
172
- col = col.to_s
173
- "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+2}, '<#{col}>');\n" <<
174
- "IF LENGTH(r1.#{col}) > 0 THEN\n" <<
175
- "DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(r1.#{col}), r1.#{col});\n" <<
176
- "END IF;\n" <<
177
- "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+3}, '</#{col}>');\n"
178
- end.join) <<
179
- (selected_columns.zip(select_queries).map do |cols, query|
180
- (cols.map do |col|
181
- "l_#{col} := '';\n"
182
- end.join) <<
183
- "FOR r2 IN (\n" <<
184
- query.gsub(/:(\w+)/,"r1.\\1") << "\n) LOOP\n" <<
185
- (cols.map do |col|
186
- "l_#{col} := l_#{col} || r2.#{col} || CHR(10);\n"
187
- end.join) <<
188
- "END LOOP;\n" <<
189
- (cols.map do |col|
190
- col = col.to_s
191
- "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+2}, '<#{col}>');\n" <<
192
- "IF LENGTH(l_#{col}) > 0 THEN\n" <<
193
- "DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(l_#{col}), l_#{col});\n" <<
194
- "END IF;\n" <<
195
- "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+3}, '</#{col}>');\n"
196
- end.join)
197
- end.join)
198
- }
199
- END LOOP;
200
- END;
201
- SQL
202
- end
203
-
204
- def parse_select_queries(select_queries)
205
- keys = []
206
- selected_columns = []
207
- select_queries.each do |query|
208
- # get primary or foreign keys like :id or :something_id
209
- keys << (query.scan(/:\w+/).map{|k| k[1..-1].downcase.to_sym})
210
- select_part = query.scan(/^select\s.*\sfrom/i).first
211
- selected_columns << select_part.scan(/\sas\s+(\w+)/i).map{|c| c.first}
212
- end
213
- [keys.flatten.uniq, selected_columns]
214
- end
215
-
216
- def create_datastore_preference(datastore_name, procedure_name)
217
- drop_ctx_preference(datastore_name)
218
- execute <<-SQL
219
- BEGIN
220
- CTX_DDL.CREATE_PREFERENCE('#{datastore_name}', 'USER_DATASTORE');
221
- CTX_DDL.SET_ATTRIBUTE('#{datastore_name}', 'PROCEDURE', '#{procedure_name}');
222
- END;
223
- SQL
224
- end
225
-
226
- def create_storage_preference(storage_name, tablespace)
227
- drop_ctx_preference(storage_name)
228
- sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{storage_name}', 'BASIC_STORAGE');\n"
229
- ['I_TABLE_CLAUSE', 'K_TABLE_CLAUSE', 'R_TABLE_CLAUSE',
230
- 'N_TABLE_CLAUSE', 'I_INDEX_CLAUSE', 'P_TABLE_CLAUSE'].each do |clause|
231
- default_clause = case clause
232
- when 'R_TABLE_CLAUSE'; 'LOB(DATA) STORE AS (CACHE) '
233
- when 'I_INDEX_CLAUSE'; 'COMPRESS 2 '
234
- else ''
235
- end
236
- sql << "CTX_DDL.SET_ATTRIBUTE('#{storage_name}', '#{clause}', '#{default_clause}TABLESPACE #{tablespace}');\n"
237
- end
238
- sql << "END;\n"
239
- execute sql
240
- end
241
-
242
- def create_lexer_preference(lexer_name, lexer_type, options)
243
- drop_ctx_preference(lexer_name)
244
- sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{lexer_name}', '#{lexer_type}');\n"
245
- options.each do |key, value|
246
- plsql_value = case value
247
- when String; "'#{value}'"
248
- when true; "'YES'"
249
- when false; "'NO'"
250
- when nil; 'NULL'
251
- else value
252
- end
253
- sql << "CTX_DDL.SET_ATTRIBUTE('#{lexer_name}', '#{key}', #{plsql_value});\n"
254
- end
255
- sql << "END;\n"
256
- execute sql
257
- end
258
-
259
- def create_wordlist_preference(wordlist_name, wordlist_type, options)
260
- drop_ctx_preference(wordlist_name)
261
- sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{wordlist_name}', '#{wordlist_type}');\n"
262
- options.each do |key, value|
263
- plsql_value = case value
264
- when String; "'#{value}'"
265
- when true; "'YES'"
266
- when false; "'NO'"
267
- when nil; 'NULL'
268
- else value
269
- end
270
- sql << "CTX_DDL.SET_ATTRIBUTE('#{wordlist_name}', '#{key}', #{plsql_value});\n"
271
- end
272
- sql << "END;\n"
273
- execute sql
274
- end
275
-
276
- def drop_ctx_preference(preference_name)
277
- execute "BEGIN CTX_DDL.DROP_PREFERENCE('#{preference_name}'); END;" rescue nil
278
- end
279
-
280
- def create_index_column_trigger(table_name, index_name, index_column, index_column_source)
281
- trigger_name = default_index_column_trigger_name(index_name)
282
- columns = Array(index_column_source)
283
- quoted_column_names = columns.map{|col| quote_column_name(col)}.join(', ')
284
- execute compress_lines(<<-SQL)
285
- CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
286
- BEFORE UPDATE OF #{quoted_column_names} ON #{quote_table_name(table_name)} FOR EACH ROW
287
- BEGIN
288
- :new.#{quote_column_name(index_column)} := '1';
289
- END;
290
- SQL
291
- end
292
-
293
- def drop_index_column_trigger(index_name)
294
- trigger_name = default_index_column_trigger_name(index_name)
295
- execute "DROP TRIGGER #{quote_table_name(trigger_name)}" rescue nil
296
- end
297
-
298
- def default_datastore_procedure(index_name)
299
- "#{index_name}_prc"
300
- end
301
-
302
- def default_datastore_name(index_name)
303
- "#{index_name}_dst"
304
- end
305
-
306
- def default_storage_name(index_name)
307
- "#{index_name}_sto"
308
- end
309
-
310
- def default_index_column_trigger_name(index_name)
311
- "#{index_name}_trg"
312
- end
313
-
314
- def default_lexer_name(index_name)
315
- "#{index_name}_lex"
316
- end
317
-
318
- def default_wordlist_name(index_name)
319
- "#{index_name}_wl"
320
- end
321
-
322
- module BaseClassMethods
323
- # Declare that model table has context index defined.
324
- # As a result <tt>contains</tt> class scope method is defined.
325
- def has_context_index
326
- extend ContextIndexClassMethods
327
- end
328
- end
329
-
330
- module ContextIndexClassMethods
331
- # Add context index condition.
332
- def contains(column, query, options ={})
333
- score_label = options[:label].to_i || 1
334
- where("CONTAINS(#{connection.quote_column_name(column)}, ?, #{score_label}) > 0", query).
335
- order("SCORE(#{score_label}) DESC")
336
- end
337
- end
338
-
339
- end
340
-
341
- end
342
- end
343
-
344
- ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
345
- include ActiveRecord::ConnectionAdapters::OracleEnhancedContextIndex
346
- end
347
-
348
- ActiveRecord::Base.class_eval do
349
- extend ActiveRecord::ConnectionAdapters::OracleEnhancedContextIndex::BaseClassMethods
350
- end