Completely replace rails' version of pk_and_sequence_for
authorTom Hughes <tom@compton.nu>
Mon, 14 Jun 2010 12:00:49 +0000 (13:00 +0100)
committerTom Hughes <tom@compton.nu>
Mon, 14 Jun 2010 12:00:49 +0000 (13:00 +0100)
Falling back to the rails code for pk_and_sequence_for doesn't work
because we still wind up retrying the slow version of the query for
tables with no primary key. So just replace it instead.

config/initializers/postgresql_adapter.rb

index 0420a74..c5a69a1 100644 (file)
@@ -1,9 +1,9 @@
 module ActiveRecord
   module ConnectionAdapters
     class PostgreSQLAdapter
-      alias_method :old_pk_and_sequence_for, :pk_and_sequence_for
-
       def pk_and_sequence_for(table)
+        # First try looking for a sequence with a dependency on the
+        # given table's primary key.
         result = query(<<-end_sql, 'PK and serial sequence')[0]
           SELECT attr.attname, seq.relname
           FROM pg_class      seq,
@@ -24,10 +24,31 @@ module ActiveRecord
         end_sql
 
         if result.nil? or result.empty?
-          old_pk_and_sequence_for(table)
-        else
-          [result.first, result.last]
+          # If that fails, try parsing the primary key's default value.
+          # Support the 7.x and 8.0 nextval('foo'::text) as well as
+          # the 8.1+ nextval('foo'::regclass).
+          result = query(<<-end_sql, 'PK and custom sequence')[0]
+            SELECT attr.attname,
+              CASE
+                WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
+                  substr(split_part(def.adsrc, '''', 2),
+                         strpos(split_part(def.adsrc, '''', 2), '.')+1)
+                ELSE split_part(def.adsrc, '''', 2)
+              END
+            FROM pg_class       t
+            JOIN pg_attribute   attr ON (t.oid = attrelid)
+            JOIN pg_attrdef     def  ON (adrelid = attrelid AND adnum = attnum)
+            JOIN pg_constraint  cons ON (conrelid = adrelid AND adnum = conkey[1])
+            WHERE t.oid = '#{quote_table_name(table)}'::regclass
+              AND cons.contype = 'p'
+              AND def.adsrc ~* 'nextval'
+          end_sql
         end
+
+        # [primary_key, sequence]
+        [result.first, result.last]
+      rescue
+        nil
       end
     end
   end