diff --git a/mapdraw.c b/mapdraw.c
index a4c3d78..6a1c3b8 100644
--- a/mapdraw.c
+++ b/mapdraw.c
@@ -964,8 +964,10 @@ int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)
   if((map->projection.numargs > 0) && (layer->projection.numargs > 0))
     msProjectRect(&map->projection, &layer->projection, &searchrect); /* project the searchrect to source coords */
 #endif
-    
+
+  layer->paginate = MS_TRUE; /* mapscript pagination support for drawing ? */
   status = msLayerWhichShapes(layer, searchrect, MS_FALSE);
+  layer->paginate = MS_FALSE;
   if(status == MS_DONE) { /* no overlap */
     msLayerClose(layer);
     return MS_SUCCESS;
diff --git a/mapfile.c b/mapfile.c
index f2be043..5ea8bb1 100644
--- a/mapfile.c
+++ b/mapfile.c
@@ -3632,6 +3632,7 @@ int initLayer(layerObj *layer, mapObj *map)
 
   layer->maxfeatures = -1; /* no quota */
   layer->startindex = -1; /*used for pagination*/
+  layer->paginate = MS_FALSE; /* driver level pagination */
   
   layer->template = layer->header = layer->footer = NULL;
 
diff --git a/mapgml.c b/mapgml.c
index ad5539a..24f5927 100644
--- a/mapgml.c
+++ b/mapgml.c
@@ -1532,8 +1532,6 @@ int msGMLWriteWFSQuery(mapObj *map, FILE *stream, int startindex, int maxfeature
         msFreeShape(&shape); /* init too */
 
         features++;
-        if (maxfeatures > 0 && features == maxfeatures)
-          break; 
       }      
 
       /* done with this layer, do a little clean-up */      
@@ -1547,9 +1545,6 @@ int msGMLWriteWFSQuery(mapObj *map, FILE *stream, int startindex, int maxfeature
       /* msLayerClose(lp); */
     }
 
-    if (maxfeatures > 0 && features == maxfeatures)
-      break;
-
   } /* next layer */
 
   return(MS_SUCCESS);
diff --git a/maplayer.c b/maplayer.c
index d630f7f..e6bf5dc 100644
--- a/maplayer.c
+++ b/maplayer.c
@@ -1194,7 +1194,9 @@ msLayerApplyCondSQLFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
 
 int msLayerSupportsPaging(layerObj *layer)
 {
-    if (layer && layer->connectiontype == MS_ORACLESPATIAL)
+    if (layer &&
+        ((layer->connectiontype == MS_ORACLESPATIAL) ||
+         (layer->connectiontype == MS_POSTGIS)) )
       return MS_TRUE;
 
     return MS_FALSE;
diff --git a/maporaclespatial.c b/maporaclespatial.c
index 9d5d27d..6e9cb3f 100644
--- a/maporaclespatial.c
+++ b/maporaclespatial.c
@@ -2072,7 +2072,7 @@ int msOracleSpatialLayerWhichShapes( layerObj *layer, rectObj rect, int isQuery)
 
     osFilteritem(layer, function, query_str, sizeof(query_str), 1);
 
-    if (layer->maxfeatures > 0 && layer->startindex < 0)
+    if (layer->paginate && layer->maxfeatures > 0 && layer->startindex < 0)
     {
        if (function == FUNCTION_NONE && layer->filter.string == NULL)
            snprintf( query_str + strlen(query_str), sizeof(query_str)-strlen(query_str), "%s"," WHERE ");
@@ -2091,7 +2091,7 @@ int msOracleSpatialLayerWhichShapes( layerObj *layer, rectObj rect, int isQuery)
 
 
     /*assuming startindex starts at 1*/
-    if (layer->startindex > 0)
+    if (layer->paginate && layer->startindex > 0)
      {
        tmp1_str = msStrdup("SELECT * from (SELECT atmp.*, ROWNUM rnum from (");
        tmp_str = msStringConcatenate(tmp_str,  tmp1_str);
diff --git a/mappostgis.c b/mappostgis.c
index 3078a41..f4bcbd4 100644
--- a/mappostgis.c
+++ b/mappostgis.c
@@ -1862,10 +1862,12 @@ char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid) {
     char *strUid = 0;
     char *strWhere = 0;
     char *strLimit = 0;
+    char *strOffset = 0;
     size_t strRectLength = 0;
     size_t strFilterLength = 0;
     size_t strUidLength = 0;
     size_t strLimitLength = 0;
+    size_t strOffsetLength = 0;
     size_t bufferSize = 0;
     int insert_and = 0;
     msPostGISLayerInfo *layerinfo;
@@ -1884,13 +1886,21 @@ char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid) {
     }
 
     /* Populate strLimit, if necessary. */
-    if( layer->maxfeatures >= 0 ) {
+    if ( layer->paginate && layer->maxfeatures >= 0 ) {
         static char *strLimitTemplate = " limit %d";
         strLimit = msSmallMalloc(strlen(strLimitTemplate) + 12);
         sprintf(strLimit, strLimitTemplate, layer->maxfeatures);
         strLimitLength = strlen(strLimit);
     }
-    
+
+    /* Populate strOffset, if necessary. */
+    if ( layer->paginate && layer->startindex > 0 ) {
+        static char *strOffsetTemplate = " offset %d";
+        strOffset = msSmallMalloc(strlen(strOffsetTemplate) + 12);
+        sprintf(strOffset, strOffsetTemplate, layer->maxfeatures);
+        strOffsetLength = strlen(strOffset);
+    }
+
     /* Populate strRect, if necessary. */
     if ( rect && layerinfo->geomcolumn ) {
         char *strBox = 0;
@@ -1936,7 +1946,8 @@ char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid) {
         strUidLength = strlen(strUid);
     }
 
-    bufferSize = strRectLength + 5 + strFilterLength + 5 + strUidLength + strLimitLength;
+    bufferSize = strRectLength + 5 + strFilterLength + 5 + strUidLength
+        + strLimitLength + strOffsetLength;
     strWhere = (char*)msSmallMalloc(bufferSize);
     *strWhere = '\0';
     if ( strRect ) {
@@ -1964,6 +1975,10 @@ char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid) {
         strlcat(strWhere, strLimit, bufferSize);
         free(strLimit);
     }
+    if ( strOffset ) {
+        strlcat(strWhere, strOffset, bufferSize);
+        free(strOffset);
+    }
 
     return strWhere;
 }
diff --git a/mapquery.c b/mapquery.c
index c527572..516f16a 100644
--- a/mapquery.c
+++ b/mapquery.c
@@ -667,6 +667,8 @@ int msQueryByAttributes(mapObj *map)
 
   /* identify candidate shapes */
   searchrect = map->query.rect;
+  /* enable driver-level pagination */
+  lp->paginate = MS_TRUE;
 #ifdef USE_PROJ  
   if(lp->project && msProjectionsDiffer(&(lp->projection), &(map->projection)))  
     msProjectRect(&(map->projection), &(lp->projection), &searchrect); /* project the searchrect to source coords */
@@ -675,6 +677,7 @@ int msQueryByAttributes(mapObj *map)
 #endif
 
   status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
+  lp->paginate = MS_FALSE; /* reset */
   if(status == MS_DONE) { /* no overlap */
     msRestoreOldFilter(lp, old_filtertype, old_filteritem, old_filterstring); /* manually reset the filter */
     msLayerClose(lp);
@@ -731,13 +734,18 @@ int msQueryByAttributes(mapObj *map)
 #endif
 
     addResult(lp->resultcache, &shape);
-    
     msFreeShape(&shape);
 
     if(map->query.mode == MS_QUERY_SINGLE) { /* no need to look any further */
       status = MS_DONE;
       break;
     }
+
+    /* check shape count */
+    if(lp->maxfeatures > 0 && lp->maxfeatures == lp->resultcache->numresults) {
+      status = MS_DONE;
+      break;
+    }
   }
 
   if (classgroup)
@@ -912,6 +920,12 @@ int msQueryByFilter(mapObj *map)
 
       addResult(lp->resultcache, &shape);
       msFreeShape(&shape);
+
+      /* check shape count */
+      if(lp->maxfeatures > 0 && lp->maxfeatures == lp->resultcache->numresults) {
+        status = MS_DONE;
+        break;
+      }
     } /* next shape */
 
     if(classgroup) msFree(classgroup);
@@ -1029,6 +1043,9 @@ int msQueryByRect(mapObj *map)
     status = msLayerWhichItems(lp, MS_TRUE, NULL);
     if(status != MS_SUCCESS) return(MS_FAILURE);
 
+    /* enable driver-level pagination */
+    lp->paginate = MS_TRUE;
+
 #ifdef USE_PROJ
     if(lp->project && msProjectionsDiffer(&(lp->projection), &(map->projection)))
       msProjectRect(&(map->projection), &(lp->projection), &searchrect); /* project the searchrect to source coords */
@@ -1036,6 +1053,7 @@ int msQueryByRect(mapObj *map)
       lp->project = MS_FALSE;
 #endif
     status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
+    lp->paginate = MS_FALSE; /* reset */
     if(status == MS_DONE) { /* no overlap */
       msLayerClose(lp);
       continue;
@@ -1108,8 +1126,13 @@ int msQueryByRect(mapObj *map)
 
       if(status == MS_TRUE)
 	addResult(lp->resultcache, &shape);
-
       msFreeShape(&shape);
+
+      /* check shape count */
+      if(lp->maxfeatures > 0 && lp->maxfeatures == lp->resultcache->numresults) {
+        status = MS_DONE;
+        break;
+      }
     } /* next shape */
       
     if (classgroup)
@@ -1407,8 +1430,13 @@ int msQueryByFeatures(mapObj *map)
 
 	if(status == MS_TRUE)
 	  addResult(lp->resultcache, &shape);
-
 	msFreeShape(&shape);
+
+	/* check shape count */
+	if(lp->maxfeatures > 0 && lp->maxfeatures == lp->resultcache->numresults) {
+	  status = MS_DONE;
+	  break;
+	}
       } /* next shape */
 
       if (classgroup)
@@ -1481,7 +1509,7 @@ int msQueryByPoint(mapObj *map)
     /* conditions may have changed since this layer last drawn, so set 
        layer->project true to recheck projection needs (Bug #673) */ 
     lp->project = MS_TRUE; 
-
+    
     /* free any previous search results, do it now in case one of the next few tests fail */
     if(lp->resultcache) {
       if(lp->resultcache->results) free(lp->resultcache->results);
@@ -1542,6 +1570,8 @@ int msQueryByPoint(mapObj *map)
 
     /* identify target shapes */
     searchrect = rect;
+    /* enable driver-level pagination */
+    lp->paginate = MS_TRUE;
 #ifdef USE_PROJ
     if(lp->project && msProjectionsDiffer(&(lp->projection), &(map->projection)))
       msProjectRect(&(map->projection), &(lp->projection), &searchrect); /* project the searchrect to source coords */
@@ -1549,6 +1579,7 @@ int msQueryByPoint(mapObj *map)
       lp->project = MS_FALSE;
 #endif
     status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
+    lp->paginate = MS_FALSE; /* reset */
     if(status == MS_DONE) { /* no overlap */
       msLayerClose(lp);
       continue;
@@ -1618,6 +1649,12 @@ int msQueryByPoint(mapObj *map)
         status = MS_DONE;   /* got enough results for this layer */
         break;
       }
+
+      /* check shape count */
+      if(lp->maxfeatures > 0 && lp->maxfeatures == lp->resultcache->numresults) {
+        status = MS_DONE;
+        break;
+      }
     } /* next shape */
 
     if (classgroup)
@@ -1877,8 +1914,13 @@ int msQueryByShape(mapObj *map)
 
       if(status == MS_TRUE)
 	addResult(lp->resultcache, &shape);
-
       msFreeShape(&shape);
+
+      /* check shape count */
+      if(lp->maxfeatures > 0 && lp->maxfeatures == lp->resultcache->numresults) {
+        status = MS_DONE;
+        break;
+      }
     } /* next shape */
 
     if(status != MS_DONE) return(MS_FAILURE);
diff --git a/mapscript/swiginc/layer.i b/mapscript/swiginc/layer.i
index 772f633..0a945b0 100644
--- a/mapscript/swiginc/layer.i
+++ b/mapscript/swiginc/layer.i
@@ -313,6 +313,9 @@
         self->status = MS_ON;
         retval = msQueryByAttributes(map);
         self->status = status;
+        /* reset paginate property */
+        self->paginate = MS_FALSE;
+
         return retval;
     }
 
@@ -333,6 +336,9 @@
         self->status = MS_ON;
         retval = msQueryByPoint(map);
         self->status = status;
+        /* reset paginate property */
+        self->paginate = MS_FALSE;
+
         return retval;
     }
 
@@ -352,6 +358,9 @@
         self->status = MS_ON;
         retval = msQueryByRect(map);
         self->status = status;
+        /* reset paginate property */
+        self->paginate = MS_FALSE;
+
         return retval;
     }
 
diff --git a/mapserver.h b/mapserver.h
index 82e2988..6bd7dda 100644
--- a/mapserver.h
+++ b/mapserver.h
@@ -1505,14 +1505,18 @@ typedef struct layer_obj {
   double mingeowidth, maxgeowidth; /* map width (in map units) at which the layer should be drawn */
 
   int sizeunits; /* applies to all classes */
-
+  
   int maxfeatures;      
   int startindex;
-
+#ifndef SWIG
+  /* boolean variable, do we need to handle pagination at driver level */
+  int paginate;
+#endif
+  
   colorObj offsite; /* transparent pixel value for raster images */
-
+  
   int transform; /* does this layer have to be transformed to file coordinates */
-
+  
   int labelcache, postlabelcache; /* on or off */
 
   char *labelitem;
diff --git a/mapshape.c b/mapshape.c
index 6924a36..c529a80 100644
--- a/mapshape.c
+++ b/mapshape.c
@@ -2554,19 +2554,6 @@ int msSHPLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery)
     return status;
   }
 
-  /* now apply the maxshapes criteria (NOTE: this ignores the filter so you could get less than maxfeatures) */
-  if(layer->maxfeatures > 0) {
-
-    for( i = (shpfile->numshapes - 1); i >= 0; i-- ) {
-      n2 = msGetBit(shpfile->status, i);
-      n1 += n2;
-      if( n2 && n1 > layer->maxfeatures ) {
-        msSetBit(shpfile->status, i, 0);
-      }
-    }
-
-  }
-    
   return MS_SUCCESS;
 }
 
diff --git a/mapwfs.c b/mapwfs.c
index 754af36..a058ff5 100644
--- a/mapwfs.c
+++ b/mapwfs.c
@@ -2071,8 +2071,8 @@ int msWFSGetFeature(mapObj *map, wfsParamsObj *paramsObj, cgiRequestObj *req, ow
     startindex = paramsObj->nStartIndex;
 
 
-  /*maxfeatures set but no startindex*/
-  if (maxfeatures > 0 && startindex < 0)
+  /* maxfeatures set */
+  if (maxfeatures > 0)
   {
       for(j=0; j<map->numlayers; j++) 
       {
@@ -2088,32 +2088,12 @@ int msWFSGetFeature(mapObj *map, wfsParamsObj *paramsObj, cgiRequestObj *req, ow
       }
   }
   
-  /*no maxfeatures set but startindex set*/
-  if (maxfeatures <=0 && startindex > 0)
+  /* startindex set */
+  if (startindex > 0 &&
+      (nQueriedLayers == 1 && msLayerSupportsPaging(lpQueried)))
   {
-      if (nQueriedLayers == 1 && msLayerSupportsPaging(lpQueried))
-      {
-          lpQueried->startindex = startindex;
-          startindex = -1;
-      }
-  }
-
-  /*maxfeatures set and startindex set*/
-  if (maxfeatures >0 && startindex > 0)
-  {
-      if (nQueriedLayers == 1 && msLayerSupportsPaging(lpQueried))
-      {
           lpQueried->startindex = startindex;
-          if (lpQueried->maxfeatures <=0 || 
-              (lpQueried->maxfeatures > 0 && maxfeatures < lpQueried->maxfeatures))
-            lpQueried->maxfeatures = maxfeatures;
-          
-          startindex = -1;
-          maxfeatures = -1;
-      }
   }
-
-
   
   if (paramsObj->pszFilter) {
     bFilterSet = 1;
@@ -2235,21 +2215,28 @@ int msWFSGetFeature(mapObj *map, wfsParamsObj *paramsObj, cgiRequestObj *req, ow
 	  return msWFSException(map, "typename", "InvalidParameterValue", paramsObj->pszVersion);
 	}
 	psNode = FLTParseFilterEncoding(paszFilter[i]);
-	
-        /*if we have a complex filter, make sure that paging is done at the gml output level
-          and not at the driver level #3305*/
-        bComplexFilter = (!FLTIsSimpleFilter(psNode));
-        if (bComplexFilter && nQueriedLayers == 1 && lpQueried &&
-            msLayerSupportsPaging(lpQueried) && 
-            (lpQueried->startindex > 0 && lpQueried->maxfeatures > 0))
+
+        bComplexFilter = !FLTIsSimpleFilter(psNode);
+
+        /* Set the driver-level pagination if it's not a complex filter #4011*/
+        if (lpQueried)
         {
+          if (!bComplexFilter && (nQueriedLayers == 1))
+          {
+            lpQueried->paginate = MS_TRUE;  /* don't really need to call msLayerSupportsPaging
+                                               this will be ignored by other drivers... */
+          }
+          else
+          {
+            /* ensure it's disabled... msQueryBy* might enable it */
             startindex = lpQueried->startindex;
             lpQueried->startindex = -1;
-
+            
             maxfeatures = lpQueried->maxfeatures;
             lpQueried->maxfeatures = -1;
+          }
         }
-
+        
 	if (!psNode) {
 	  msSetError(MS_WFSERR, 
 		     "Invalid or Unsupported FILTER in GetFeature : %s", 
