Su descripción da como resultado una definición de tabla como esta:
CREATE TABLE tbl (
   lap_id   serial PRIMARY KEY
 , lap_no   int NOT NULL
 , car_type enum NOT NULL
 , race_id  int NOT NULL  -- REFERENCES ...
 , UNIQUE(race_id, car_type, lap_no)
);
Solución general para esta clase de problemas.
Para obtener la secuencia más larga (1 resultado, el más largo de todos, selección arbitraria si hay empates):
SELECT race_id, car_type, count(*) AS seq_len
FROM  (
   SELECT *, count(*) FILTER (WHERE step)
                      OVER (ORDER BY race_id, car_type, lap_no) AS grp
   FROM  (
      SELECT *, (lag(lap_no) OVER (PARTITION BY race_id, car_type ORDER BY lap_no) + 1)
                 IS DISTINCT FROM lap_no AS step
      FROM   tbl
      ) x
   ) y
GROUP  BY race_id, car_type, grp
ORDER  BY seq_len DESC
LIMIT  1;
count(*) FILTER (WHERE step)solo cuenta TRUE(= paso al siguiente grupo), lo que da como resultado un nuevo número para cada nuevo grupo.
Pregunta relacionada sobre SO, una respuesta con una solución de procedimiento con plpgsql :
Si el requisito principal es el rendimiento, la función plpgsql suele ser más rápida en este caso particular porque puede calcular el resultado en un solo escaneo.
Más rápido para números consecutivos
Podemos aprovechar el hecho de que una secuencia consecutiva lap_no define una secuencia mucho más simple y rápida :
SELECT race_id, car_type, count(*) AS seq_len
FROM  (
   SELECT race_id, car_type
        , row_number() OVER (PARTITION BY race_id, car_type ORDER BY lap_no) - lap_no AS grp
   FROM   tbl
   ) x
GROUP  BY race_id, car_type, grp
ORDER  BY seq_len DESC
LIMIT  1;
Vueltas consecutivas terminan en el mismo grp. Cada vuelta que falta resulta en una baja grppor partición.
Esto se basa en el (race_id, car_type, lap_no)ser UNIQUE NOT NULL. Los valores NULL o duplicados podrían romper la lógica.
Discusión de la alternativa más simple de Jack
La versión de @ Jack cuenta de manera efectiva todas las vueltas (filas) donde la anterior lap_noen esto race_idtenía lo mismo car_type. Eso es más simple, más rápido y correcto, siempre y cuando cada uno car_typesolo pueda tener una secuencia por race_id.
Pero para una tarea así de simple, la consulta podría ser aún más simple. De ello se sigue lógicamente que todo lap_nopor (car_type, race_id)debe ser de forma secuencial , y que sólo podía contar las vueltas:
SELECT race_id, car_type, count(*) AS seq_len
FROM   tbl
GROUP  BY race_id, car_type
ORDER  BY seq_len DESC
LIMIT  1;
Si, por otro lado, uno car_typepuede tener múltiples secuencias separadas por race_id (y la pregunta no especifica lo contrario), la versión de Jack fallará.
Más rápido para una carrera / tipo de auto determinado
En respuesta al comentario / aclaraciones en la pregunta: restringir la consulta a una dada (race_id, car_type) lo hará mucho más rápido , por supuesto:
SELECT count(*) AS seq_len
FROM  (
   SELECT row_number() OVER (ORDER BY lap_no) - lap_no AS grp
   FROM   tbl
   WHERE  race_id = 1
   AND    car_type = 'red'
   ) x
GROUP  BY grp
ORDER  BY seq_len DESC
LIMIT  1;
db <> violín aquí
 
Viejo violín de SQL
Índice
La clave para lograr un rendimiento superior es un índice de ajuste (a excepción de la solución de procedimiento mencionada que funciona con un solo escaneo secuencial). Un índice de varias columnas como este sirve mejor:
CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);
Si su tabla tiene la UNIQUErestricción que asumí en la parte superior, eso se implementa solo con este índice (único) internamente, y no necesita crear otro índice.
                 
                
fuente
sum((lap_no=(prev+1))::integer)+1pero no estoy seguro de que sea más fácil de leer