<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>generators/ultrasphinx_migration/templates/migration.rb</filename>
    </added>
    <added>
      <filename>generators/ultrasphinx_migration/ultrasphinx_migration_generator.rb</filename>
    </added>
    <added>
      <filename>install.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,4 +1,6 @@
 
+v1.9.3. Move Postgres stored procedures into a migration generator (Lang Riley).
+
 v1.9.2. Add geodistance support (Jeremy Seitz, Mark Lane). 
 
 v1.9.1. Add ultrasphinx:index:merge task for index merging, and a note in DEPLOYMENT_NOTES about how to use it.</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -125,7 +125,7 @@ See DEPLOYMENT_NOTES[link:files/DEPLOYMENT_NOTES.html].
 
 Note that since Ultrasphinx preloads indexed models, you need to make sure those models have their own dependencies in place early in the boot process. This may require adjusting the general plugin load order or moving monkey-patches from &lt;tt&gt;lib/&lt;/tt&gt; to &lt;tt&gt;vendor/plugins/&lt;/tt&gt;.
 
-PostgreSQL 8.2 and higher are well supported. However, make sure you have executed &lt;tt&gt;CREATE LANGUAGE plpgsql;&lt;/tt&gt; at least once. This step does not need to be repeated, so depending on your DB permissions, you might be able to put it in a migration.
+PostgreSQL 8.2 and higher are well supported. However, make sure the stored procedure migration gets generated correctly. If you did not install the plugin via &lt;tt&gt;script/install&lt;/tt&gt;, run &lt;tt&gt;script/generate ultrasphinx_migration&lt;/tt&gt; by hand.
 
 == Reporting problems
 </diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -231,8 +231,14 @@ module Ultrasphinx
       
 
       def build_regular_fields(klass, fields, entries, column_strings, join_strings, group_bys, remaining_columns)          
-        entries.to_a.each do |entry|
-          source_string = &quot;#{entry['table_alias']}.#{entry['field']}&quot;
+        entries.to_a.each do |entry|          
+
+          source_string = if entry['sortable']            
+            entry['field'] # Use the alias
+          else            
+            &quot;#{entry['table_alias']}.#{entry['field']}&quot; # Use the column
+          end
+          
           group_bys &lt;&lt; source_string
           column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], column_strings, remaining_columns)
         end</diff>
      <filename>lib/ultrasphinx/configure.rb</filename>
    </modified>
    <modified>
      <diff>@@ -99,10 +99,7 @@ This is a special singleton configuration class that stores the index field conf
         begin
         
           # Fields are from the model
-          # We destructively canonicize them back onto the configuration hash
           options['fields'] = options['fields'].to_a.map do |entry|
-            entry = {'field' =&gt; entry} unless entry.is_a? Hash
-
             extract_table_alias!(entry, klass)
             extract_field_alias!(entry, klass)
             </diff>
      <filename>lib/ultrasphinx/fields.rb</filename>
    </modified>
    <modified>
      <diff>@@ -198,7 +198,8 @@ If the associations weren't just &lt;tt&gt;has_many&lt;/tt&gt; and &lt;tt&gt;belongs_to&lt;/tt&gt;, you
           entry.assert_valid_keys ['field', 'as', 'facet', 'function_sql', 'sortable']
           entry
         else
-          entry.to_s
+          # Single strings
+          {'field' =&gt; entry.to_s}
         end
       end
       
@@ -219,6 +220,18 @@ If the associations weren't just &lt;tt&gt;has_many&lt;/tt&gt; and &lt;tt&gt;belongs_to&lt;/tt&gt;, you
         entry._stringify_all!('sortable', 'facet')
         entry.assert_valid_keys ['class_name', 'association_name', 'field', 'as', 'association_sql', 'facet', 'function_sql', 'sortable']
       end
+
+      # Add special alias columns for text sortables so that the original column doesn't get
+      # removed, since Sphinx will either store stems or ordinals but not both. We use the
+      # original alias as source so that the query doesn't get repeated in case it is 
+      # resource-intensive.
+      opts.slice('fields', 'concatenate', 'include').values._flatten_once.each do |entry|
+        if entry['sortable']
+          entry.delete('sortable')
+          name = &quot;#{entry['as'] || entry['field']}&quot;
+          opts['fields'] &lt;&lt; {'field' =&gt; name, 'as' =&gt; &quot;#{name}_sortable&quot;, 'sortable' =&gt; true}
+        end
+      end
             
       Ultrasphinx::MODEL_CONFIGURATION[self.name] = opts
     end</diff>
      <filename>lib/ultrasphinx/is_indexed.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,11 +1,11 @@
 
 /* http://osdir.com/ml/db.postgresql.admIN/2003-08/msg00057.html */
 
-CREATE OR REPLACE FUNCTION MAKE_CONCAT_WS() RETURNS text AS '
+CREATE FUNCTION MAKE_CONCAT_WS() RETURNS text AS '
 declare
   v_args int := 32;
-  v_first text := ''CREATE OR REPLACE FUNCTION CONCAT_WS(text,text,text) RETURNS text AS ''''SELECT CASE WHEN $1 IS NULL THEN NULL WHEN $3 IS NULL THEN $2 ELSE $2 || $1 || $3 END'''' LANGUAGE sql IMMUTABLE'';
-  v_part1 text := ''CREATE OR REPLACE FUNCTION CONCAT_WS(text,text'';
+  v_first text := ''CREATE FUNCTION CONCAT_WS(text,text,text) RETURNS text AS ''''SELECT CASE WHEN $1 IS NULL THEN NULL WHEN $3 IS NULL THEN $2 ELSE $2 || $1 || $3 END'''' LANGUAGE sql IMMUTABLE'';
+  v_part1 text := ''CREATE FUNCTION CONCAT_WS(text,text'';
   v_part2 text := '') RETURNS text AS ''''SELECT CONCAT_WS($1,CONCAT_WS($1,$2'';
   v_part3 text := '')'''' LANGUAGE sql IMMUTABLE'';  
   v_sql text;</diff>
      <filename>lib/ultrasphinx/postgresql/concat_ws.sql</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 
 /* Fake CRC32 */
 
-CREATE OR REPLACE FUNCTION crc32(text)
+CREATE FUNCTION crc32(text)
 RETURNS bigint AS $$
   DECLARE
     tmp bigint;</diff>
      <filename>lib/ultrasphinx/postgresql/crc32.sql</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@
  Note: For DISTINCT and ORDER BY a subquery is required
 */
 
-CREATE OR REPLACE FUNCTION _group_concat(text, text)
+CREATE FUNCTION _group_concat(text, text)
 RETURNS text AS $$
   SELECT CASE
     WHEN $2 IS NULL THEN $1
@@ -15,8 +15,6 @@ RETURNS text AS $$
   END
 $$ IMMUTABLE LANGUAGE SQL;
 
-DROP AGGREGATE IF EXISTS group_concat(text);
-
 CREATE AGGREGATE group_concat (
 	BASETYPE = text,
 	SFUNC = _group_concat,</diff>
      <filename>lib/ultrasphinx/postgresql/group_concat.sql</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 
-CREATE OR REPLACE FUNCTION hex_to_int(varchar) RETURNS int4 AS '
+CREATE FUNCTION hex_to_int(varchar) RETURNS int4 AS '
   DECLARE
     h alias for $1;
     exec varchar;</diff>
      <filename>lib/ultrasphinx/postgresql/hex_to_int.sql</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@
  UNIX_TIMESTAMP(date)
 */
 
-CREATE OR REPLACE FUNCTION unix_timestamp(timestamp without time zone)
+CREATE FUNCTION unix_timestamp(timestamp without time zone)
 RETURNS bigint AS $$
   SELECT EXTRACT(EPOCH FROM $1)::bigint
 $$ VOLATILE LANGUAGE SQL;</diff>
      <filename>lib/ultrasphinx/postgresql/unix_timestamp.sql</filename>
    </modified>
    <modified>
      <diff>@@ -83,7 +83,7 @@ Note that Sphinx expects lat/long to be indexed as radians. If you have degrees
 
 Then, set &lt;tt&gt;Ultrasphinx::Search.client_options[:location][:units] = 'degrees'&lt;/tt&gt;.
 
-The &lt;tt&gt;:double&lt;/tt&gt; column type is recommended for storing location data.
+The MySQL &lt;tt&gt;:double&lt;/tt&gt; column type is recommended for storing location data. For Postgres, use &lt;tt&gt;:float&lt;/tt.
 
 == Interlock integration
   </diff>
      <filename>lib/ultrasphinx/search.rb</filename>
    </modified>
    <modified>
      <diff>@@ -48,6 +48,9 @@ module Ultrasphinx
           end
         end
         
+        # Use the additional sortable column if it is a text type
+        sort_by += &quot;_sortable&quot; if Fields.instance.types[sort_by] == &quot;text&quot;
+        
         unless sort_by.blank?
           if opts['sort_mode'].to_s == 'relevance'
             # If you're sorting by a field you don't want 'relevance' order
@@ -124,7 +127,7 @@ module Ultrasphinx
                 raise NoMethodError
             end
           rescue NoMethodError =&gt; e
-            raise UsageError, &quot;filter value #{value.inspect} for field #{field.inspect} is invalid&quot;
+            raise UsageError, &quot;Filter value #{value.inspect} for field #{field.inspect} is invalid&quot;
           end
         end
         
@@ -258,7 +261,10 @@ module Ultrasphinx
             
       # Inverse-modulus map the Sphinx ids to the table-specific ids
       def convert_sphinx_ids(sphinx_ids)    
-        number_of_models = IDS_TO_MODELS.size
+        
+        number_of_models = IDS_TO_MODELS.size        
+        raise ConfigurationError, &quot;No model mappings were found. Your #{RAILS_ENV}.conf file is corrupted, or your application container needs to be restarted.&quot; if number_of_models == 0
+        
         sphinx_ids.sort_by do |item| 
           item[:index]
         end.map do |item|</diff>
      <filename>lib/ultrasphinx/search/internals.rb</filename>
    </modified>
    <modified>
      <diff>@@ -67,43 +67,30 @@ module Ultrasphinx
       'group_concat' =&gt; &quot;CAST(GROUP_CONCAT(DISTINCT ? ? SEPARATOR ' ') AS CHAR)&quot;,
       'delta' =&gt; &quot;DATE_SUB(NOW(), INTERVAL ? SECOND)&quot;,      
       'hash' =&gt; &quot;CAST(CRC32(?) AS unsigned)&quot;,
-      'range_cast' =&gt; &quot;?&quot;,
-      'stored_procedures' =&gt; {}
+      'range_cast' =&gt; &quot;?&quot;
     },
     'postgresql' =&gt; {
       'group_concat' =&gt; &quot;GROUP_CONCAT(?)&quot;,
       'delta' =&gt; &quot;(NOW() - '? SECOND'::interval)&quot;,
       'range_cast' =&gt; &quot;cast(coalesce(?,1) AS integer)&quot;,
-      'hash' =&gt; &quot;CRC32(?)&quot;,
-      'stored_procedures' =&gt; Hash[*(
-        ['hex_to_int', 'group_concat', 'concat_ws', 'unix_timestamp', 'crc32'].map do |name|
-          [name, load_stored_procedure(name)]
-        end.flatten
-        )
-      ]
+      'hash' =&gt; &quot;CRC32(?)&quot;
     }      
   }
   
   DEFAULTS = {
     'mysql' =&gt; %(
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  ), 
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    ), 
     'postgresql' =&gt; %(
-type = pgsql
-sql_query_pre = ) + SQL_FUNCTIONS['postgresql']['stored_procedures'].values.join(' ') + %(
-  )
-}
+      type = pgsql
+      sql_query_pre =
+    )
+  }
     
   ADAPTER = ActiveRecord::Base.connection.instance_variable_get(&quot;@config&quot;)[:adapter] rescue 'mysql'
-  
-  # Install the stored procedures.
-  # XXX This shouldn't be done at every index, say the Postgres people.
-  SQL_FUNCTIONS[ADAPTER]['stored_procedures'].each do |key, value|
-    ActiveRecord::Base.connection.execute(value)
-  end
-  
+    
   # Warn-mode logger. Also called from rake tasks.  
   def self.say msg
     # XXX Method name is stupid.</diff>
      <filename>lib/ultrasphinx/ultrasphinx.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ class Seller &lt; ActiveRecord::Base
   delegate :address, :to =&gt; :user
   
   is_indexed :fields =&gt; [
-    {:field =&gt; :company_name, :facet =&gt; true},
+    {:field =&gt; :company_name, :facet =&gt; true, :sortable =&gt; true},
     {:field =&gt; 'mission_statement', :sortable =&gt; true},
     'created_at', 
     :capitalization, </diff>
      <filename>test/integration/app/app/models/seller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 
-# Auto-generated at Fri Mar 28 00:16:11 -0400 2008.
+# Auto-generated at Mon Mar 31 00:50:43 -0400 2008.
 # Hand modifications will be overwritten.
-# /Users/eweaver/Desktop/projects/fauna/ultrasphinx/trunk/test/integration/app/config/ultrasphinx/default.base
+# /Users/eweaver/Desktop/projects/ub/vendor/plugins/ultrasphinx/test/integration/app/config/ultrasphinx/default.base
 indexer {
   mem_limit = 256M
 }
@@ -26,27 +26,28 @@ source geo__states_main
   sql_query_post = 
   sql_ranged_throttle = 0
 
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    
 sql_db = ultrasphinx_development
 sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM states 
-sql_query = SELECT (states.id * 5 + 0) AS id, CAST(GROUP_CONCAT(DISTINCT addresses.name  SEPARATOR ' ') AS CHAR) AS address_name, 0 AS capitalization, 'Geo::State' AS class, 0 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_two, 0 AS company_two_facet, '' AS content, 18000 AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, '' AS mission_statement, '' AS name, '' AS state, 0 AS user_id FROM states LEFT OUTER JOIN addresses AS addresses ON states.id = addresses.state_id WHERE states.id &gt;= $start AND states.id &lt;= $end GROUP BY states.id
+sql_query = SELECT (states.id * 5 + 0) AS id, CAST(GROUP_CONCAT(DISTINCT addresses.name  SEPARATOR ' ') AS CHAR) AS address_name, 0 AS capitalization, 'Geo::State' AS class, 0 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, 18000 AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM states LEFT OUTER JOIN addresses AS addresses ON states.id = addresses.state_id WHERE states.id &gt;= $start AND states.id &lt;= $end GROUP BY states.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
 sql_attr_uint = company_facet
 sql_attr_uint = company_name_facet
+sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
 sql_attr_float = lat
 sql_attr_float = lng
-sql_attr_str2ordinal = mission_statement
+sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM states WHERE states.id = (($id - 0) / 5)
 }
@@ -60,27 +61,28 @@ source sellers_main
   sql_query_post = 
   sql_ranged_throttle = 0
 
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    
 sql_db = ultrasphinx_development
 sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM sellers 
-sql_query = SELECT (sellers.id * 5 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, 0 AS company_facet, sellers.company_name AS company_name, CAST(CRC32(sellers.company_name) AS unsigned) AS company_name_facet, '' AS company_two, 0 AS company_two_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, sellers.mission_statement AS mission_statement, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id &gt;= $start AND sellers.id &lt;= $end GROUP BY sellers.id
+sql_query = SELECT (sellers.id * 5 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, 0 AS company_facet, sellers.company_name AS company_name, CAST(CRC32(sellers.company_name) AS unsigned) AS company_name_facet, company_name AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, sellers.mission_statement AS mission_statement, mission_statement AS mission_statement_sortable, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id &gt;= $start AND sellers.id &lt;= $end GROUP BY sellers.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
 sql_attr_uint = company_facet
 sql_attr_uint = company_name_facet
+sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
 sql_attr_float = lat
 sql_attr_float = lng
-sql_attr_str2ordinal = mission_statement
+sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM sellers WHERE sellers.id = (($id - 1) / 5)
 }
@@ -94,27 +96,28 @@ source geo__addresses_main
   sql_query_post = 
   sql_ranged_throttle = 0
 
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    
 sql_db = ultrasphinx_development
 sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM addresses 
-sql_query = SELECT (addresses.id * 5 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_two, 0 AS company_two_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, 18000 AS created_at, 0 AS deleted, '' AS email, RADIANS(addresses.lat) AS lat, RADIANS(addresses.lng) AS lng, '' AS login, '' AS mission_statement, addresses.name AS name, state.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states AS state ON state.id = addresses.state_id WHERE addresses.id &gt;= $start AND addresses.id &lt;= $end GROUP BY addresses.id
+sql_query = SELECT (addresses.id * 5 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, 18000 AS created_at, 0 AS deleted, '' AS email, RADIANS(addresses.lat) AS lat, RADIANS(addresses.lng) AS lng, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, addresses.name AS name, state.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states AS state ON state.id = addresses.state_id WHERE addresses.id &gt;= $start AND addresses.id &lt;= $end GROUP BY addresses.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
 sql_attr_uint = company_facet
 sql_attr_uint = company_name_facet
+sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
 sql_attr_float = lat
 sql_attr_float = lng
-sql_attr_str2ordinal = mission_statement
+sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM addresses WHERE addresses.id = (($id - 2) / 5)
 }
@@ -128,27 +131,28 @@ source users_main
   sql_query_post = 
   sql_ranged_throttle = 0
 
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    
 sql_db = ultrasphinx_development
 sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM users 
-sql_query = SELECT (users.id * 5 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, specific_seller.company_name AS company, CAST(CRC32(specific_seller.company_name) AS unsigned) AS company_facet, '' AS company_name, 0 AS company_name_facet, REPLACE(sellers_two.company_name, '6', ' replacement ') AS company_two, CAST(CRC32(REPLACE(sellers_two.company_name, '6', ' replacement ')) AS unsigned) AS company_two_facet, '' AS content, 18000 AS created_at, users.deleted AS deleted, users.email AS email, 0 AS lat, 0 AS lng, users.login AS login, '' AS mission_statement, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers AS specific_seller ON users.id = specific_seller.user_id LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id WHERE users.id &gt;= $start AND users.id &lt;= $end AND (deleted = '0') GROUP BY users.id
+sql_query = SELECT (users.id * 5 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, specific_seller.company_name AS company, CAST(CRC32(specific_seller.company_name) AS unsigned) AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, REPLACE(sellers_two.company_name, '6', ' replacement ') AS company_two, CAST(CRC32(REPLACE(sellers_two.company_name, '6', ' replacement ')) AS unsigned) AS company_two_facet, '' AS content, 18000 AS created_at, users.deleted AS deleted, users.email AS email, 0 AS lat, 0 AS lng, users.login AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers AS specific_seller ON users.id = specific_seller.user_id LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id WHERE users.id &gt;= $start AND users.id &lt;= $end AND (deleted = '0') GROUP BY users.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
 sql_attr_uint = company_facet
 sql_attr_uint = company_name_facet
+sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
 sql_attr_float = lat
 sql_attr_float = lng
-sql_attr_str2ordinal = mission_statement
+sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM users WHERE users.id = (($id - 3) / 5)
 }
@@ -162,27 +166,28 @@ source geo__countries_main
   sql_query_post = 
   sql_ranged_throttle = 0
 
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    
 sql_db = ultrasphinx_development
 sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM countries 
-sql_query = SELECT (countries.id * 5 + 4) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Country' AS class, 4 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_two, 0 AS company_two_facet, '' AS content, 18000 AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, '' AS mission_statement, countries.name AS name, '' AS state, 0 AS user_id FROM countries WHERE countries.id &gt;= $start AND countries.id &lt;= $end GROUP BY countries.id
+sql_query = SELECT (countries.id * 5 + 4) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Country' AS class, 4 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, 18000 AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, countries.name AS name, '' AS state, 0 AS user_id FROM countries WHERE countries.id &gt;= $start AND countries.id &lt;= $end GROUP BY countries.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
 sql_attr_uint = company_facet
 sql_attr_uint = company_name_facet
+sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
 sql_attr_float = lat
 sql_attr_float = lng
-sql_attr_str2ordinal = mission_statement
+sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM countries WHERE countries.id = (($id - 4) / 5)
 }
@@ -219,27 +224,28 @@ source sellers_delta
   sql_query_post = 
   sql_ranged_throttle = 0
 
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    
 sql_db = ultrasphinx_development
 sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM sellers WHERE sellers.updated_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND)
-sql_query = SELECT (sellers.id * 5 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, 0 AS company_facet, sellers.company_name AS company_name, CAST(CRC32(sellers.company_name) AS unsigned) AS company_name_facet, '' AS company_two, 0 AS company_two_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, sellers.mission_statement AS mission_statement, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id &gt;= $start AND sellers.id &lt;= $end AND sellers.updated_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND) GROUP BY sellers.id
+sql_query = SELECT (sellers.id * 5 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, 0 AS company_facet, sellers.company_name AS company_name, CAST(CRC32(sellers.company_name) AS unsigned) AS company_name_facet, company_name AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, sellers.mission_statement AS mission_statement, mission_statement AS mission_statement_sortable, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id &gt;= $start AND sellers.id &lt;= $end AND sellers.updated_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND) GROUP BY sellers.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
 sql_attr_uint = company_facet
 sql_attr_uint = company_name_facet
+sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
 sql_attr_float = lat
 sql_attr_float = lng
-sql_attr_str2ordinal = mission_statement
+sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM sellers WHERE sellers.id = (($id - 1) / 5)
 }
@@ -253,27 +259,28 @@ source geo__addresses_delta
   sql_query_post = 
   sql_ranged_throttle = 0
 
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    
 sql_db = ultrasphinx_development
 sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM addresses 
-sql_query = SELECT (addresses.id * 5 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_two, 0 AS company_two_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, 18000 AS created_at, 0 AS deleted, '' AS email, RADIANS(addresses.lat) AS lat, RADIANS(addresses.lng) AS lng, '' AS login, '' AS mission_statement, addresses.name AS name, state.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states AS state ON state.id = addresses.state_id WHERE addresses.id &gt;= $start AND addresses.id &lt;= $end GROUP BY addresses.id
+sql_query = SELECT (addresses.id * 5 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, 18000 AS created_at, 0 AS deleted, '' AS email, RADIANS(addresses.lat) AS lat, RADIANS(addresses.lng) AS lng, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, addresses.name AS name, state.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states AS state ON state.id = addresses.state_id WHERE addresses.id &gt;= $start AND addresses.id &lt;= $end GROUP BY addresses.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
 sql_attr_uint = company_facet
 sql_attr_uint = company_name_facet
+sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
 sql_attr_float = lat
 sql_attr_float = lng
-sql_attr_str2ordinal = mission_statement
+sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM addresses WHERE addresses.id = (($id - 2) / 5)
 }
@@ -287,27 +294,28 @@ source users_delta
   sql_query_post = 
   sql_ranged_throttle = 0
 
-type = mysql
-sql_query_pre = SET SESSION group_concat_max_len = 65535
-sql_query_pre = SET NAMES utf8
-  
+      type = mysql
+      sql_query_pre = SET SESSION group_concat_max_len = 65535
+      sql_query_pre = SET NAMES utf8
+    
 sql_db = ultrasphinx_development
 sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM users WHERE users.created_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND)
-sql_query = SELECT (users.id * 5 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, specific_seller.company_name AS company, CAST(CRC32(specific_seller.company_name) AS unsigned) AS company_facet, '' AS company_name, 0 AS company_name_facet, REPLACE(sellers_two.company_name, '6', ' replacement ') AS company_two, CAST(CRC32(REPLACE(sellers_two.company_name, '6', ' replacement ')) AS unsigned) AS company_two_facet, '' AS content, 18000 AS created_at, users.deleted AS deleted, users.email AS email, 0 AS lat, 0 AS lng, users.login AS login, '' AS mission_statement, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers AS specific_seller ON users.id = specific_seller.user_id LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id WHERE users.id &gt;= $start AND users.id &lt;= $end AND (deleted = '0') AND users.created_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND) GROUP BY users.id
+sql_query = SELECT (users.id * 5 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, specific_seller.company_name AS company, CAST(CRC32(specific_seller.company_name) AS unsigned) AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, REPLACE(sellers_two.company_name, '6', ' replacement ') AS company_two, CAST(CRC32(REPLACE(sellers_two.company_name, '6', ' replacement ')) AS unsigned) AS company_two_facet, '' AS content, 18000 AS created_at, users.deleted AS deleted, users.email AS email, 0 AS lat, 0 AS lng, users.login AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers AS specific_seller ON users.id = specific_seller.user_id LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id WHERE users.id &gt;= $start AND users.id &lt;= $end AND (deleted = '0') AND users.created_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND) GROUP BY users.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
 sql_attr_uint = company_facet
 sql_attr_uint = company_name_facet
+sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
 sql_attr_float = lat
 sql_attr_float = lng
-sql_attr_str2ordinal = mission_statement
+sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM users WHERE users.id = (($id - 3) / 5)
 }</diff>
      <filename>test/integration/app/config/ultrasphinx/development.conf.canonical</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 class AddLatAndLongToAddress &lt; ActiveRecord::Migration
   def self.up
-    add_column :addresses, :lat, :double
-    add_column :addresses, :lng, :double
+    add_column :addresses, :lat, :float # Postgres doesn't have :double
+    add_column :addresses, :lng, :float
   end
 
   def self.down</diff>
      <filename>test/integration/app/db/migrate/007_add_lat_and_long_to_address.rb</filename>
    </modified>
    <modified>
      <diff>@@ -227,7 +227,7 @@ class SearchTest &lt; Test::Unit::TestCase
   
   def test_unconfigured_sortable_name
     assert_raises(Ultrasphinx::UsageError) do
-      S.new(:class_names =&gt; 'Seller', :sort_by =&gt; 'company_name', :sort_mode =&gt; 'ascending', :per_page =&gt; 5).run
+      S.new(:class_names =&gt; 'User', :sort_by =&gt; 'company', :sort_mode =&gt; 'ascending', :per_page =&gt; 5).run
     end
   end
   </diff>
      <filename>test/integration/search_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,11 +17,18 @@ Dir.chdir &quot;#{File.dirname(__FILE__)}/integration/app/&quot; do
     system(&quot;ln -s ../../../../../ ultrasphinx&quot;)
   end
   
-  system(&quot;rake db:drop&quot;)
-  system(&quot;rake db:create&quot;)
-  system(&quot;rake db:migrate db:fixtures:load&quot;)
+  if ENV['DB'] == 'postgresql'
+    # http://dev.rubyonrails.org/ticket/10559
+    system(&quot;echo 'DROP DATABASE ultrasphinx_development;' | psql template1&quot;) 
+  else
+    system(&quot;rake db:drop --trace&quot;)
+  end
+  
+  system(&quot;rake db:create --trace&quot;)
+  system(&quot;script/generate ultrasphinx_migration --svn&quot;) if ENV['DB'] == 'postgresql'
+  system(&quot;rake db:migrate db:fixtures:load --trace&quot;)
 
-  system(&quot;rake us:boot&quot;)
+  system(&quot;rake us:boot --trace&quot;)
   system(&quot;rm /tmp/ultrasphinx-stopwords.txt&quot;)
-  system(&quot;rake ultrasphinx:spelling:build&quot;)
+  system(&quot;rake ultrasphinx:spelling:build --trace&quot;)
 end</diff>
      <filename>test/setup.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,4 +5,9 @@ Dir.chdir &quot;#{File.dirname(__FILE__)}/integration/app/&quot; do
   # Remove the symlink created by the setup method, for people with tools
   # that can't handle recursive directories (Textmate).
   system(&quot;rm vendor/plugins/ultrasphinx&quot;) unless ENV['USER'] == 'eweaver'
+  
+  # Remove the generated Postgres migration, if it exists
+  Dir[&quot;db/migrate/*_install_ultrasphinx_stored_procedures.rb&quot;].each do |file|
+    system(&quot;svn del --force #{file}&quot;)
+  end  
 end
\ No newline at end of file</diff>
      <filename>test/teardown.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>881780cfc1296302ad8f3b10e0e2263090536849</id>
    </parent>
  </parents>
  <author>
    <name>Evan Weaver</name>
    <email>evan@cloudbur.st</email>
  </author>
  <url>http://github.com/fauna/ultrasphinx/commit/69d9f5cab84753d0778a2460be3d17cf866419a9</url>
  <id>69d9f5cab84753d0778a2460be3d17cf866419a9</id>
  <committed-date>2008-03-30T22:25:52-07:00</committed-date>
  <authored-date>2008-03-30T22:25:52-07:00</authored-date>
  <message>Use a migration generator for installing Postgres stored procedures (Lang Riley).</message>
  <tree>f2d5b0e7cd97739ef840553e95c861fd15890ea8</tree>
  <committer>
    <name>Evan Weaver</name>
    <email>evan@cloudbur.st</email>
  </committer>
</commit>
