activerecord-postgresql-extensions 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/Rakefile +2 -2
  2. data/VERSION +1 -1
  3. data/activerecord-postgresql-extensions.gemspec +20 -17
  4. data/lib/activerecord-postgresql-extensions.rb +2 -0
  5. data/lib/postgresql_extensions/postgresql_adapter_extensions.rb +50 -53
  6. data/lib/postgresql_extensions/postgresql_constraints.rb +142 -153
  7. data/lib/postgresql_extensions/postgresql_extensions.rb +246 -0
  8. data/lib/postgresql_extensions/postgresql_functions.rb +31 -33
  9. data/lib/postgresql_extensions/postgresql_geometry.rb +2 -2
  10. data/lib/postgresql_extensions/postgresql_indexes.rb +13 -14
  11. data/lib/postgresql_extensions/postgresql_languages.rb +4 -4
  12. data/lib/postgresql_extensions/postgresql_permissions.rb +12 -14
  13. data/lib/postgresql_extensions/postgresql_roles.rb +2 -2
  14. data/lib/postgresql_extensions/postgresql_rules.rb +11 -10
  15. data/lib/postgresql_extensions/postgresql_schemas.rb +4 -4
  16. data/lib/postgresql_extensions/postgresql_sequences.rb +15 -16
  17. data/lib/postgresql_extensions/postgresql_tables.rb +20 -21
  18. data/lib/postgresql_extensions/postgresql_text_search.rb +313 -0
  19. data/lib/postgresql_extensions/postgresql_triggers.rb +13 -14
  20. data/lib/postgresql_extensions/postgresql_types.rb +1 -1
  21. data/lib/postgresql_extensions/postgresql_views.rb +13 -14
  22. data/test/{adapter_test.rb → adapter_tests.rb} +6 -6
  23. data/test/{constraints_test.rb → constraints_tests.rb} +13 -13
  24. data/test/extensions_tests.rb +275 -0
  25. data/test/{functions_test.rb → functions_tests.rb} +10 -10
  26. data/test/{geometry_test.rb → geometry_tests.rb} +16 -16
  27. data/test/{index_test.rb → index_tests.rb} +11 -11
  28. data/test/{languages_test.rb → languages_tests.rb} +6 -6
  29. data/test/{permissions_test.rb → permissions_tests.rb} +36 -36
  30. data/test/{roles_test.rb → roles_tests.rb} +6 -6
  31. data/test/{rules_test.rb → rules_tests.rb} +3 -3
  32. data/test/{schemas_test.rb → schemas_tests.rb} +6 -6
  33. data/test/{sequences_test.rb → sequences_tests.rb} +10 -10
  34. data/test/{tables_test.rb → tables_tests.rb} +2 -2
  35. data/test/text_search_tests.rb +263 -0
  36. metadata +19 -16
  37. data/postgresql-extensions.gemspec +0 -50
@@ -80,7 +80,7 @@ module ActiveRecord
80
80
  sql << ' TO '
81
81
  sql << Array(role_names).collect { |r| quote_role(r) }.join(', ')
82
82
  sql << ' WITH ADMIN OPTION' if options[:with_admin_option]
83
- execute sql
83
+ execute("#{sql};")
84
84
  end
85
85
 
86
86
  # Revokes table privileges. You can specify multiple tables,
@@ -155,7 +155,7 @@ module ActiveRecord
155
155
  sql << ' FROM '
156
156
  sql << Array(role_names).collect { |r| quote_role(r) }.join(', ')
157
157
  sql << ' CASCADE' if options[:cascade]
158
- execute sql
158
+ execute("#{sql};")
159
159
  end
160
160
  end
161
161
 
@@ -204,12 +204,11 @@ module ActiveRecord
204
204
  #
205
205
  # ==== Examples
206
206
  #
207
- # ### ruby
208
- # grant_table_privileges([ :table1, :table2 ], :select, :joe)
209
- # # => GRANT SELECT ON TABLE "table1", "table2" TO "joe"
207
+ # grant_table_privileges([ :table1, :table2 ], :select, :joe)
208
+ # # => GRANT SELECT ON TABLE "table1", "table2" TO "joe"
210
209
  #
211
- # grant_sequence_privileges(:my_seq, [ :select, :update ], :public)
212
- # # => GRANT SELECT, UPDATE ON SEQUENCE "my_seq" TO PUBLIC
210
+ # grant_sequence_privileges(:my_seq, [ :select, :update ], :public)
211
+ # # => GRANT SELECT, UPDATE ON SEQUENCE "my_seq" TO PUBLIC
213
212
  #
214
213
  # You can specify the <tt>:with_grant_option</tt> in any of the
215
214
  # grant_*_privilege methods to add a WITH GRANT OPTION clause to
@@ -244,7 +243,7 @@ module ActiveRecord
244
243
  end.join(', ')
245
244
 
246
245
  sql << ' WITH GRANT OPTION' if options[:with_grant_option]
247
- sql
246
+ "#{sql};"
248
247
  end
249
248
  alias :to_s :to_sql
250
249
  end
@@ -264,12 +263,11 @@ module ActiveRecord
264
263
  #
265
264
  # ==== Examples
266
265
  #
267
- # ### ruby
268
- # revoke_table_privileges([ :table1, :table2 ], :select, :joe)
269
- # # => REVOKE SELECT ON TABLE "table1", "table2" FROM "joe"
266
+ # revoke_table_privileges([ :table1, :table2 ], :select, :joe)
267
+ # # => REVOKE SELECT ON TABLE "table1", "table2" FROM "joe"
270
268
  #
271
- # revoke_sequence_privileges(:my_seq, [ :select, :update ], :public)
272
- # # => REVOKE SELECT, UPDATE ON SEQUENCE "my_seq" FROM PUBLIC
269
+ # revoke_sequence_privileges(:my_seq, [ :select, :update ], :public)
270
+ # # => REVOKE SELECT, UPDATE ON SEQUENCE "my_seq" FROM PUBLIC
273
271
  #
274
272
  # You can specify the <tt>:grant_option_for</tt> in any of the
275
273
  # revoke_*_privilege methods to add a GRANT OPTION FOR clause to
@@ -314,7 +312,7 @@ module ActiveRecord
314
312
  end.join(', ')
315
313
 
316
314
  sql << ' CASCADE' if options[:cascade]
317
- sql
315
+ "#{sql};"
318
316
  end
319
317
  alias :to_s :to_sql
320
318
  end
@@ -24,7 +24,7 @@ module ActiveRecord
24
24
  sql = 'DROP ROLE '
25
25
  sql << 'IF EXISTS ' if options[:if_exists]
26
26
  sql << Array(name).collect { |r| quote_role(r) }.join(', ')
27
- execute sql
27
+ execute("#{sql};")
28
28
  end
29
29
  alias :drop_user :drop_role
30
30
  end
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  sql << Array(options[:admin]).collect { |r| base.quote_role(r) }.join(', ')
113
113
  end
114
114
 
115
- sql.join(' ')
115
+ "#{sql.join(' ')};"
116
116
  end
117
117
  alias :to_s :to_sql
118
118
 
@@ -37,22 +37,21 @@ module ActiveRecord
37
37
  #
38
38
  # ==== Examples
39
39
  #
40
- # ### ruby
41
- # create_rule(
42
- # 'check_it_out_rule',
43
- # :select,
44
- # :child,
45
- # :instead,
46
- # 'select * from public.another', :conditions => 'id = 1'
47
- # )
48
- # # => CREATE RULE "check_it_out_rule" AS ON SELECT TO "child" WHERE id = 1 DO INSTEAD select * from public.another;
40
+ # create_rule(
41
+ # 'check_it_out_rule',
42
+ # :select,
43
+ # :child,
44
+ # :instead,
45
+ # 'select * from public.another', :conditions => 'id = 1'
46
+ # )
47
+ # # => CREATE RULE "check_it_out_rule" AS ON SELECT TO "child" WHERE id = 1 DO INSTEAD select * from public.another;
49
48
  def create_rule(name, event, table, action, commands, options = {})
50
49
  execute PostgreSQLRuleDefinition.new(self, name, event, table, action, commands, options).to_s
51
50
  end
52
51
 
53
52
  # Drops a PostgreSQL rule.
54
53
  def drop_rule(name, table)
55
- execute "DROP RULE #{quote_rule(name)} ON #{quote_table_name(table)}"
54
+ execute "DROP RULE #{quote_rule(name)} ON #{quote_table_name(table)};"
56
55
  end
57
56
  end
58
57
 
@@ -89,6 +88,8 @@ module ActiveRecord
89
88
  else
90
89
  commands.to_s
91
90
  end
91
+
92
+ "#{sql};"
92
93
  end
93
94
  alias :to_s :to_sql
94
95
 
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  def create_schema schema, options = {}
19
19
  sql = "CREATE SCHEMA #{quote_schema(schema)}"
20
20
  sql << " AUTHORIZATION #{quote_role(options[:authorization])}" if options[:authorization]
21
- execute sql
21
+ execute("#{sql};")
22
22
  end
23
23
 
24
24
  # Drops a schema.
@@ -32,17 +32,17 @@ module ActiveRecord
32
32
  sql << 'IF EXISTS ' if options[:if_exists]
33
33
  sql << Array(schemas).collect { |s| quote_schema(s) }.join(', ')
34
34
  sql << ' CASCADE' if options[:cascade]
35
- execute sql
35
+ execute("#{sql};")
36
36
  end
37
37
 
38
38
  # Alter's a schema's name.
39
39
  def alter_schema_name old_schema, new_schema
40
- execute "ALTER SCHEMA #{quote_schema(old_schema)} RENAME TO #{quote_schema(new_schema)}"
40
+ execute "ALTER SCHEMA #{quote_schema(old_schema)} RENAME TO #{quote_schema(new_schema)};"
41
41
  end
42
42
 
43
43
  # Changes a schema's owner.
44
44
  def alter_schema_owner schema, role
45
- execute "ALTER SCHEMA #{quote_schema(schema)} OWNER TO #{quote_schema(role)}"
45
+ execute "ALTER SCHEMA #{quote_schema(schema)} OWNER TO #{quote_role(role)};"
46
46
  end
47
47
  end
48
48
  end
@@ -42,17 +42,16 @@ module ActiveRecord
42
42
  #
43
43
  # ==== Example
44
44
  #
45
- # ### ruby
46
- # create_sequence(
47
- # 'what_a_sequence_of_events',
48
- # :increment => 2,
49
- # :cache => 2,
50
- # :min_value => nil,
51
- # :max_value => 10,
52
- # :owned_by => [ :foo, :id ]
53
- # )
54
- # # => CREATE SEQUENCE "what_a_sequence_of_events" INCREMENT BY 2
55
- # # NO MINVALUE MAXVALUE 10 CACHE 2 OWNED BY "foo"."id";
45
+ # create_sequence(
46
+ # 'what_a_sequence_of_events',
47
+ # :increment => 2,
48
+ # :cache => 2,
49
+ # :min_value => nil,
50
+ # :max_value => 10,
51
+ # :owned_by => [ :foo, :id ]
52
+ # )
53
+ # # => CREATE SEQUENCE "what_a_sequence_of_events" INCREMENT BY 2
54
+ # # NO MINVALUE MAXVALUE 10 CACHE 2 OWNED BY "foo"."id";
56
55
  def create_sequence(name, options = {})
57
56
  execute PostgreSQLSequenceDefinition.new(self, :create, name, options).to_s
58
57
  end
@@ -69,17 +68,17 @@ module ActiveRecord
69
68
  sql << 'IF EXISTS ' if options[:if_exists]
70
69
  sql << Array(name).collect { |s| quote_sequence(s) }.join(', ')
71
70
  sql << ' CASCADE' if options[:cascade]
72
- execute sql
71
+ execute("#{sql};")
73
72
  end
74
73
 
75
74
  # Renames the sequence.
76
75
  def rename_sequence(name, rename, options = {})
77
- execute "ALTER SEQUENCE #{quote_sequence(name)} RENAME TO #{quote_generic_ignore_schema(rename)}"
76
+ execute "ALTER SEQUENCE #{quote_sequence(name)} RENAME TO #{quote_generic_ignore_schema(rename)};"
78
77
  end
79
78
 
80
79
  # Alters the sequence's schema.
81
80
  def alter_sequence_schema(name, schema, options = {})
82
- execute "ALTER SEQUENCE #{quote_sequence(name)} SET SCHEMA #{quote_schema(schema)}"
81
+ execute "ALTER SEQUENCE #{quote_sequence(name)} SET SCHEMA #{quote_schema(schema)};"
83
82
  end
84
83
 
85
84
  # Alters any of the various options for a sequence. See
@@ -112,7 +111,7 @@ module ActiveRecord
112
111
  else
113
112
  'false'
114
113
  end <<
115
- ')'
114
+ ');'
116
115
  end
117
116
 
118
117
  # Returns an Array of available sequences.
@@ -193,7 +192,7 @@ module ActiveRecord
193
192
  if action != :create && options.has_key?(:restart_with)
194
193
  sql << "RESTART WITH #{options[:restart_with].to_i}"
195
194
  end
196
- sql.join(' ')
195
+ "#{sql.join(' ')};"
197
196
  end
198
197
  alias :to_s :to_sql
199
198
 
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
  class PostgreSQLAdapter < AbstractAdapter
14
14
  # Set the schema of a table.
15
15
  def alter_table_schema table_name, schema, options = {}
16
- execute "ALTER TABLE #{quote_schema(table_name)} SET SCHEMA #{quote_schema(schema)}"
16
+ execute "ALTER TABLE #{quote_schema(table_name)} SET SCHEMA #{quote_schema(schema)};"
17
17
  end
18
18
 
19
19
  alias :original_create_table :create_table
@@ -69,24 +69,23 @@ module ActiveRecord
69
69
  #
70
70
  # ==== Examples
71
71
  #
72
- # ### ruby
73
- # create_table(:foo, :inherits => :parent) do |t|
74
- # t.integer :bar_id, :references => :bar
75
- # t.like :base, :including => [ :defaults, :indexes ], :excluding => :constraints
76
- # t.check_constraint "bar_id < 100"
77
- # t.unique_constraint :bar_id
78
- # end
72
+ # create_table(:foo, :inherits => :parent) do |t|
73
+ # t.integer :bar_id, :references => :bar
74
+ # t.like :base, :including => [ :defaults, :indexes ], :excluding => :constraints
75
+ # t.check_constraint "bar_id < 100"
76
+ # t.unique_constraint :bar_id
77
+ # end
79
78
  #
80
- # # Produces:
81
- # #
82
- # # CREATE TABLE "foo" (
83
- # # "id" serial primary key,
84
- # # "bar_id" integer DEFAULT NULL NULL,
85
- # # LIKE "base" INCLUDING DEFAULTS INCLUDING INDEXES EXCLUDING CONSTRAINTS,
86
- # # FOREIGN KEY ("bar_id") REFERENCES "bar",
87
- # # CHECK (bar_id < 100),
88
- # # UNIQUE ("bar_id")
89
- # # ) INHERITS ("parent");
79
+ # # Produces:
80
+ # #
81
+ # # CREATE TABLE "foo" (
82
+ # # "id" serial primary key,
83
+ # # "bar_id" integer DEFAULT NULL NULL,
84
+ # # LIKE "base" INCLUDING DEFAULTS INCLUDING INDEXES EXCLUDING CONSTRAINTS,
85
+ # # FOREIGN KEY ("bar_id") REFERENCES "bar",
86
+ # # CHECK (bar_id < 100),
87
+ # # UNIQUE ("bar_id")
88
+ # # ) INHERITS ("parent");
90
89
  #
91
90
  # This is a fairly convoluted example, but there you have it.
92
91
  #
@@ -131,7 +130,7 @@ module ActiveRecord
131
130
  sql << 'IF EXISTS ' if options[:if_exists]
132
131
  sql << Array(tables).collect { |t| quote_table_name(t) }.join(', ')
133
132
  sql << ' CASCADE' if options[:cascade]
134
- execute sql
133
+ execute("#{sql};")
135
134
  end
136
135
 
137
136
  alias :original_rename_table :rename_table
@@ -140,7 +139,7 @@ module ActiveRecord
140
139
  # capabilities. You can still access the original method via
141
140
  # original_rename_table.
142
141
  def rename_table(name, new_name, options = {})
143
- execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_generic_ignore_schema(new_name)}"
142
+ execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_generic_ignore_schema(new_name)};"
144
143
  end
145
144
 
146
145
  private
@@ -195,7 +194,7 @@ module ActiveRecord
195
194
  sql << "ON COMMIT #{options[:on_commit].to_s.upcase}" if options[:on_commit]
196
195
  sql << "#{options[:options]}" if options[:options]
197
196
  sql << "TABLESPACE #{base.quote_tablespace(options[:tablespace])}" if options[:tablespace]
198
- sql
197
+ "#{sql};"
199
198
  end
200
199
  alias :to_s :to_sql
201
200
 
@@ -0,0 +1,313 @@
1
+
2
+ require 'active_record/connection_adapters/postgresql_adapter'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class PostgreSQLAdapter < AbstractAdapter
7
+ # Creates a new PostgreSQL text search configuration. You must provide
8
+ # either a :parser_name or a :source_config option as per the PostgreSQL
9
+ # text search docs.
10
+ def create_text_search_configuration(name, options = {})
11
+ if options[:parser_name] && options[:source_config]
12
+ raise ArgumentError.new("You can't define both :parser_name and :source_config options.")
13
+ elsif options[:parser_name].blank? && options[:source_config].blank?
14
+ raise ArgumentError.new("You must provide either a :parser_name or a :source_config.")
15
+ end
16
+
17
+ sql = "CREATE TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} ("
18
+
19
+ ignore_schema do
20
+ sql << if options[:parser_name]
21
+ "PARSER = #{quote_generic_with_schema(options[:parser_name])}"
22
+ else
23
+ "COPY = #{quote_generic_with_schema(options[:source_config])}"
24
+ end
25
+ end
26
+
27
+ sql << ")"
28
+ execute("#{sql};")
29
+ end
30
+
31
+ def add_text_search_configuration_mapping(name, tokens, dictionaries)
32
+ add_or_alter_text_search_configuration_mapping(name, tokens, dictionaries, :action => :add)
33
+ end
34
+
35
+ def alter_text_search_configuration_mapping(name, tokens, dictionaries)
36
+ add_or_alter_text_search_configuration_mapping(name, tokens, dictionaries, :action => :alter)
37
+ end
38
+
39
+ # This method is semi-private and should only really be used via
40
+ # add_text_search_configuration_mapping and alter_text_search_configuration_mapping.
41
+ #
42
+ # ==== Options
43
+ #
44
+ # * <tt>:action</tt> - either :add or :alter.
45
+ def add_or_alter_text_search_configuration_mapping(name, tokens, dictionaries, options = {})
46
+ options = {
47
+ :action => :add
48
+ }.merge(options)
49
+
50
+ if ![ :add, :alter ].include?(options[:action])
51
+ raise ArgumentError.new(":action option must be eithe :add or :alter.")
52
+ end
53
+
54
+ add_or_alter = options[:action].to_s.upcase
55
+
56
+ sql = "ALTER TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} #{add_or_alter} MAPPING FOR "
57
+ sql << Array(tokens).collect { |token|
58
+ quote_generic(token)
59
+ }.join(', ')
60
+
61
+ sql << ' WITH '
62
+
63
+ sql << Array(dictionaries).collect { |dictionary|
64
+ quote_generic(dictionary)
65
+ }.join(', ')
66
+
67
+ execute("#{sql};")
68
+ end
69
+
70
+ def replace_text_search_configuration_dictionary(name, old_dictionary, new_dictionary)
71
+ sql = "ALTER TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} ALTER MAPPING REPLACE "
72
+ sql << "#{quote_generic(old_dictionary)} WITH #{quote_generic(new_dictionary)}"
73
+
74
+ execute("#{sql};")
75
+ end
76
+
77
+ def alter_text_search_configuration_mapping_replace_dictionary(name, mappings, old_dictionary, new_dictionary)
78
+ if mappings.blank?
79
+ raise ArgumentError.new("Expected one or more mappings to alter.")
80
+ end
81
+
82
+ sql = "ALTER TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} ALTER MAPPING FOR "
83
+ sql << Array(mappings).collect { |token_type|
84
+ quote_generic(token_type)
85
+ }.join(', ')
86
+ sql << " REPLACE #{quote_generic(old_dictionary)} WITH #{quote_generic(new_dictionary)}"
87
+
88
+ execute("#{sql};")
89
+ end
90
+
91
+ def drop_text_search_configuration_mapping(name, *args)
92
+ options = args.extract_options!
93
+ mappings = args
94
+
95
+ if mappings.blank?
96
+ raise ArgumentError.new("Expected one or more mappings to drop.")
97
+ end
98
+
99
+ sql = "ALTER TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} DROP MAPPING "
100
+
101
+ if options[:if_exists]
102
+ sql << 'IF EXISTS '
103
+ end
104
+
105
+ sql << 'FOR '
106
+ sql << mappings.collect { |token_type|
107
+ quote_generic(token_type)
108
+ }.join(', ')
109
+
110
+ execute("#{sql};")
111
+ end
112
+
113
+ def rename_text_search_configuration(old_name, new_name)
114
+ execute("ALTER TEXT SEARCH CONFIGURATION %s RENAME TO %s;" % [
115
+ quote_generic_with_schema(old_name),
116
+ quote_generic_with_schema(new_name)
117
+ ])
118
+ end
119
+
120
+ def alter_text_search_configuration_owner(name, role)
121
+ execute "ALTER TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} OWNER TO #{quote_role(role)};"
122
+ end
123
+
124
+ def alter_text_search_configuration_schema(name, schema)
125
+ execute "ALTER TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} SET SCHEMA #{quote_schema(schema)};"
126
+ end
127
+
128
+ # Drops a text search configuration.
129
+ #
130
+ # ==== Options
131
+ #
132
+ # * <tt>:if_exists</tt> - adds IF EXISTS.
133
+ # * <tt>:cascade</tt> - adds CASCADE.
134
+ def drop_text_search_configuration(name, options = {})
135
+ sql = 'DROP TEXT SEARCH CONFIGURATION '
136
+ sql << 'IF EXISTS ' if options[:if_exists]
137
+ sql << quote_generic_with_schema(name)
138
+ sql << ' CASCADE' if options[:cascade]
139
+
140
+ execute("#{sql};")
141
+ end
142
+
143
+ def create_text_search_dictionary(name, template, options = {})
144
+ sql = "CREATE TEXT SEARCH DICTIONARY #{quote_generic_with_schema(name)} ("
145
+ sql << "TEMPLATE = #{quote_generic_with_schema(template)}"
146
+
147
+ if !options.blank?
148
+ sql << ', '
149
+ sql << options.collect { |k, v|
150
+ "#{quote_generic(k)} = #{quote(v)}"
151
+ }.join(', ')
152
+ end
153
+
154
+ sql << ')'
155
+
156
+ execute("#{sql};")
157
+ end
158
+
159
+ # ==== Options
160
+ #
161
+ # * <tt>:if_exists</tt> - adds IF EXISTS.
162
+ # * <tt>:cascade</tt> - adds CASCADE.
163
+ def drop_text_search_dictionary(name, options = {})
164
+ sql = 'DROP TEXT SEARCH DICTIONARY '
165
+ sql << 'IF EXISTS ' if options[:if_exists]
166
+ sql << quote_generic_with_schema(name)
167
+ sql << ' CASCADE' if options[:cascade]
168
+
169
+ execute("#{sql};")
170
+ end
171
+
172
+ def alter_text_search_dictionary(name, options)
173
+ if options.blank?
174
+ raise ArgumentError.new("Expected some options to alter.")
175
+ end
176
+
177
+ sql = "ALTER TEXT SEARCH DICTIONARY #{quote_generic_with_schema} ("
178
+ sql << options.collect { |k, v|
179
+ "#{quote_generic(k)} = #{quote(v)}"
180
+ }.join(', ')
181
+ sql << ')'
182
+
183
+ execute("#{sql};")
184
+ end
185
+
186
+ def rename_text_search_dictionary(old_name, new_name)
187
+ execute("ALTER TEXT SEARCH DICTIONARY %s RENAME TO %s;" % [
188
+ quote_generic_with_schema(old_name),
189
+ quote_generic_with_schema(new_name)
190
+ ])
191
+ end
192
+
193
+ def alter_text_search_dictionary_owner(name, role)
194
+ execute "ALTER TEXT SEARCH DICTIONARY #{quote_generic_with_schema(name)} OWNER TO #{quote_role(role)};"
195
+ end
196
+
197
+ def alter_text_search_dictionary_schema(name, schema)
198
+ execute "ALTER TEXT SEARCH DICTIONARY #{quote_generic_with_schema(name)} SET SCHEMA #{quote_schema(schema)};"
199
+ end
200
+
201
+
202
+ # ==== Options
203
+ #
204
+ # :lexize - the function used by the template lexer. Required.
205
+ # :init - the initialization function for the template. Optional.
206
+ def create_text_search_template(name, options = {})
207
+ if options[:lexize].blank?
208
+ raise ArgumentError.new("Expected to see a :lexize option.")
209
+ end
210
+
211
+ sql = "CREATE TEXT SEARCH TEMPLATE #{quote_generic_with_schema(name)} ("
212
+
213
+ if options[:init]
214
+ sql << "INIT = #{quote_function(options[:init])}, "
215
+ end
216
+
217
+ sql << "LEXIZE = #{quote_function(options[:lexize])}"
218
+ sql << ')'
219
+
220
+ execute("#{sql};")
221
+ end
222
+
223
+ # ==== Options
224
+ #
225
+ # * <tt>:if_exists</tt> - adds IF EXISTS.
226
+ # * <tt>:cascade</tt> - adds CASCADE.
227
+ def drop_text_search_template(name, options = {})
228
+ sql = 'DROP TEXT SEARCH TEMPLATE '
229
+ sql << 'IF EXISTS ' if options[:if_exists]
230
+ sql << quote_generic_with_schema(name)
231
+ sql << ' CASCADE' if options[:cascade]
232
+
233
+ execute("#{sql};")
234
+ end
235
+
236
+ def rename_text_search_template(old_name, new_name)
237
+ execute("ALTER TEXT SEARCH TEMPLATE %s RENAME TO %s;" % [
238
+ quote_generic_with_schema(old_name),
239
+ quote_generic_with_schema(new_name)
240
+ ])
241
+ end
242
+
243
+ def alter_text_search_template_schema(name, schema)
244
+ execute "ALTER TEXT SEARCH TEMPLATE #{quote_generic_with_schema(name)} SET SCHEMA #{quote_schema(schema)};"
245
+ end
246
+
247
+
248
+
249
+ # The :start, :gettoken, :end and :lextypes options are required as per
250
+ # the PostgreSQL docs, while the :headline option is optional.
251
+ def create_text_search_parser(name, options = {})
252
+ if (missing_options = [ :start, :gettoken, :end, :lextypes ] - options.keys).present?
253
+ raise ArgumentError.new("Missing options: #{missing_options}.")
254
+ end
255
+
256
+ sql = "CREATE TEXT SEARCH PARSER #{quote_generic_with_schema(name)} ("
257
+ sql << "START = #{quote_function(options[:start])}, "
258
+ sql << "GETTOKEN = #{quote_function(options[:gettoken])}, "
259
+ sql << "END = #{quote_function(options[:end])}, "
260
+ sql << "LEXTYPES = #{quote_function(options[:lextypes])}"
261
+
262
+ if options[:headline]
263
+ sql << ", HEADLINE = #{quote_function(options[:headline])}"
264
+ end
265
+
266
+ sql << ')'
267
+
268
+ execute("#{sql};")
269
+ end
270
+
271
+ # ==== Options
272
+ #
273
+ # * <tt>:if_exists</tt> - adds IF EXISTS.
274
+ # * <tt>:cascade</tt> - adds CASCADE.
275
+ def drop_text_search_parser(name, options = {})
276
+ sql = 'DROP TEXT SEARCH PARSER '
277
+ sql << 'IF EXISTS ' if options[:if_exists]
278
+ sql << quote_generic_with_schema(name)
279
+ sql << ' CASCADE' if options[:cascade]
280
+
281
+ execute("#{sql};")
282
+ end
283
+
284
+ def rename_text_search_parser(old_name, new_name)
285
+ execute("ALTER TEXT SEARCH PARSER %s RENAME TO %s;" % [
286
+ quote_generic_with_schema(old_name),
287
+ quote_generic_with_schema(new_name)
288
+ ])
289
+ end
290
+
291
+ def alter_text_search_parser_schema(name, schema)
292
+ execute "ALTER TEXT SEARCH PARSER #{quote_generic_with_schema(name)} SET SCHEMA #{quote_schema(schema)};"
293
+ end
294
+
295
+ private
296
+ def extract_hash_or_array_options(hash_or_array, *keys)
297
+ case v = hash_or_array[0]
298
+ when Hash
299
+ if (keys - (sliced = v.slice(*keys)).keys).length == 0
300
+ keys.collect do |k|
301
+ sliced[k]
302
+ end
303
+ else
304
+ [ v.keys.first, v.values.first ]
305
+ end
306
+ else
307
+ v = hash_or_array.flatten
308
+ [ v.shift, *v ]
309
+ end
310
+ end
311
+ end
312
+ end
313
+ end