Skip to content

Commit 8ef65a2

Browse files
szekerestrouault
andauthored
MSSQL: Implement server side paging support (#5842)
* MSSQL: Implement server side paging support (#5842) * MSSQL: Fix paging query problem if sortBy is not specified (#5842) * Update mapmssql2008.c Co-Authored-By: Even Rouault <even.rouault@spatialys.com> * Update mapmssql2008.c Co-Authored-By: Even Rouault <even.rouault@spatialys.com> * Apply simplifications in USE_ICONV conditions * Remove USE_PROJ condition Co-authored-by: Even Rouault <even.rouault@spatialys.com>
1 parent 923c4c9 commit 8ef65a2

File tree

2 files changed

+269
-23
lines changed

2 files changed

+269
-23
lines changed

mapmssql2008.c

+268-22
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ typedef struct ms_MSSQL2008_layer_info_t {
248248
char *index_name; /* hopefully this isn't necessary - but if the optimizer ain't cuttin' it... */
249249
char *sort_spec; /* the sort by specification which should be applied to the generated select statement */
250250
int mssqlversion_major; /* the sql server major version number */
251+
int paging; /* Driver handling of pagination, enabled by default */
251252
SQLSMALLINT *itemtypes; /* storing the sql field types for further reference */
252253

253254
msODBCconn * conn; /* Connection to db */
@@ -979,6 +980,7 @@ int msMSSQL2008LayerOpen(layerObj *layer)
979980
layerinfo->conn = NULL;
980981
layerinfo->itemtypes = NULL;
981982
layerinfo->mssqlversion_major = 0;
983+
layerinfo->paging = MS_TRUE;
982984

983985
layerinfo->conn = (msODBCconn *) msConnPoolRequest(layer);
984986

@@ -1339,6 +1341,8 @@ static int prepare_database(layerObj *layer, rectObj rect, char **query_string)
13391341
char *data_source = 0;
13401342
char *f_table_name = 0;
13411343
char *geom_table = 0;
1344+
char *tmp = 0;
1345+
char *paging_query = 0;
13421346
/*
13431347
"Geometry::STGeomFromText('POLYGON(())',)" + terminator = 40 chars
13441348
Plus 10 formatted doubles (15 digits of precision, a decimal point, a space/comma delimiter each = 17 chars each)
@@ -1446,32 +1450,66 @@ static int prepare_database(layerObj *layer, rectObj rect, char **query_string)
14461450
/* start creating the query */
14471451

14481452
query = msStringConcatenate(query, "SELECT ");
1453+
if (layerinfo->paging && (layer->maxfeatures >= 0 || layer->startindex > 0))
1454+
paging_query = msStringConcatenate(paging_query, "SELECT ");
14491455

14501456
/* adding items to the select list */
14511457
for (t = 0; t < layer->numitems; t++) {
14521458
#ifdef USE_ICONV
1453-
query = msStringConcatenate(query, "convert(nvarchar(max), [");
1454-
query = msStringConcatenate(query, layer->items[t]);
1455-
query = msStringConcatenate(query, "]),");
1459+
query = msStringConcatenate(query, "convert(nvarchar(max), [");
14561460
#else
14571461
query = msStringConcatenate(query, "convert(varchar(max), [");
1458-
query = msStringConcatenate(query, layer->items[t]);
1459-
query = msStringConcatenate(query, "]),");
14601462
#endif
1463+
query = msStringConcatenate(query, layer->items[t]);
1464+
query = msStringConcatenate(query, "]) '");
1465+
tmp = msIntToString(t);
1466+
query = msStringConcatenate(query, tmp);
1467+
if (paging_query) {
1468+
paging_query = msStringConcatenate(paging_query, "[");
1469+
paging_query = msStringConcatenate(paging_query, tmp);
1470+
paging_query = msStringConcatenate(paging_query, "], ");
1471+
}
1472+
msFree(tmp);
1473+
query = msStringConcatenate(query, "',");
14611474
}
14621475

14631476
/* adding geometry column */
14641477
query = msStringConcatenate(query, "[");
14651478
query = msStringConcatenate(query, layerinfo->geom_column);
14661479
if (layerinfo->geometry_format == MSSQLGEOMETRY_NATIVE)
1467-
query = msStringConcatenate(query, "],");
1468-
else
1469-
query = msStringConcatenate(query, "].STAsBinary(),");
1480+
query = msStringConcatenate(query, "] 'geom',");
1481+
else {
1482+
query = msStringConcatenate(query, "].STAsBinary() 'geom',");
1483+
}
14701484

14711485
/* adding id column */
14721486
query = msStringConcatenate(query, "convert(varchar(36), [");
14731487
query = msStringConcatenate(query, layerinfo->urid_name);
1474-
query = msStringConcatenate(query, "]) FROM ");
1488+
if (paging_query) {
1489+
paging_query = msStringConcatenate(paging_query, "[geom], [id] FROM (");
1490+
query = msStringConcatenate(query, "]) 'id', row_number() over (");
1491+
if (layerinfo->sort_spec) {
1492+
query = msStringConcatenate(query, layerinfo->sort_spec);
1493+
}
1494+
1495+
if (layer->sortBy.nProperties > 0) {
1496+
tmp = msLayerBuildSQLOrderBy(layer);
1497+
if (layerinfo->sort_spec)
1498+
query = msStringConcatenate(query, ", ");
1499+
else
1500+
query = msStringConcatenate(query, " ORDER BY ");
1501+
query = msStringConcatenate(query, tmp);
1502+
msFree(tmp);
1503+
}
1504+
else {
1505+
query = msStringConcatenate(query, "ORDER BY ");
1506+
query = msStringConcatenate(query, layerinfo->urid_name);
1507+
}
1508+
query = msStringConcatenate(query, ") 'rownum' FROM ");
1509+
}
1510+
else {
1511+
query = msStringConcatenate(query, "]) 'id' FROM ");
1512+
}
14751513

14761514
/* adding the source */
14771515
query = msStringConcatenate(query, data_source);
@@ -1510,19 +1548,44 @@ static int prepare_database(layerObj *layer, rectObj rect, char **query_string)
15101548
query = msStringConcatenate(query, box3d);
15111549
query = msStringConcatenate(query, ") = 1 ");
15121550

1513-
if (layerinfo->sort_spec) {
1514-
query = msStringConcatenate(query, layerinfo->sort_spec);
1515-
}
1551+
if (paging_query) {
1552+
paging_query = msStringConcatenate(paging_query, query);
1553+
paging_query = msStringConcatenate(paging_query, ") tbl where [rownum] ");
1554+
if (layer->startindex > 0) {
1555+
tmp = msIntToString(layer->startindex);
1556+
paging_query = msStringConcatenate(paging_query, ">= ");
1557+
paging_query = msStringConcatenate(paging_query, tmp);
1558+
if (layer->maxfeatures >= 0) {
1559+
msFree(tmp);
1560+
tmp = msIntToString(layer->startindex + layer->maxfeatures);
1561+
paging_query = msStringConcatenate(paging_query, " and [rownum] < ");
1562+
paging_query = msStringConcatenate(paging_query, tmp);
1563+
}
1564+
}
1565+
else {
1566+
tmp = msIntToString(layer->maxfeatures);
1567+
paging_query = msStringConcatenate(paging_query, "< ");
1568+
paging_query = msStringConcatenate(paging_query, tmp);
1569+
}
1570+
msFree(tmp);
1571+
msFree(query);
1572+
query = paging_query;
1573+
}
1574+
else {
1575+
if (layerinfo->sort_spec) {
1576+
query = msStringConcatenate(query, layerinfo->sort_spec);
1577+
}
15161578

1517-
/* Add extra sort by */
1518-
if( layer->sortBy.nProperties > 0 ) {
1519-
char* pszTmp = msLayerBuildSQLOrderBy(layer);
1520-
if (layerinfo->sort_spec)
1521-
query = msStringConcatenate(query, ", ");
1522-
else
1523-
query = msStringConcatenate(query, " ORDER BY ");
1524-
query = msStringConcatenate(query, pszTmp);
1525-
msFree(pszTmp);
1579+
/* Add extra sort by */
1580+
if (layer->sortBy.nProperties > 0) {
1581+
char* pszTmp = msLayerBuildSQLOrderBy(layer);
1582+
if (layerinfo->sort_spec)
1583+
query = msStringConcatenate(query, ", ");
1584+
else
1585+
query = msStringConcatenate(query, " ORDER BY ");
1586+
query = msStringConcatenate(query, pszTmp);
1587+
msFree(pszTmp);
1588+
}
15261589
}
15271590

15281591

@@ -2453,6 +2516,140 @@ int msMSSQL2008LayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record
24532516
return msMSSQL2008LayerGetShapeRandom(layer, shape, &(layerinfo->row_num));
24542517
}
24552518

2519+
/*
2520+
** Returns the number of shapes that match the potential filter and extent.
2521+
* rectProjection is the projection in which rect is expressed, or can be NULL if
2522+
* rect should be considered in the layer projection.
2523+
* This should be equivalent to calling msLayerWhichShapes() and counting the
2524+
* number of shapes returned by msLayerNextShape(), honouring layer->maxfeatures
2525+
* limitation if layer->maxfeatures>=0, and honouring layer->startindex if
2526+
* layer->startindex >= 1 and paging is enabled.
2527+
* Returns -1 in case of failure.
2528+
*/
2529+
int msMSSQL2008LayerGetShapeCount(layerObj *layer, rectObj rect, projectionObj *rectProjection)
2530+
{
2531+
msMSSQL2008LayerInfo *layerinfo;
2532+
char *query = 0;
2533+
char result_data[256];
2534+
char box3d[40 + 10 * 22 + 11];
2535+
SQLLEN retLen;
2536+
SQLRETURN rc;
2537+
int hasFilter = MS_FALSE;
2538+
2539+
rectObj searchrectInLayerProj = rect;
2540+
2541+
if (layer->debug) {
2542+
msDebug("msMSSQL2008LayerGetShapeCount called.\n");
2543+
}
2544+
2545+
layerinfo = getMSSQL2008LayerInfo(layer);
2546+
2547+
if (!layerinfo) {
2548+
/* Layer not open */
2549+
msSetError(MS_QUERYERR, "msMSSQL2008LayerGetShapeCount called on unopened layer (layerinfo = NULL)", "msMSSQL2008LayerGetShapeCount()");
2550+
2551+
return MS_FAILURE;
2552+
}
2553+
2554+
// Special processing if the specified projection for the rect is different from the layer projection
2555+
// We want to issue a WHERE that includes
2556+
// ((the_geom && rect_reprojected_in_layer_SRID) AND NOT ST_Disjoint(ST_Transform(the_geom, rect_SRID), rect))
2557+
if (rectProjection != NULL && layer->project &&
2558+
msProjectionsDiffer(&(layer->projection), rectProjection))
2559+
{
2560+
// If we cannot guess the EPSG code of the rectProjection, fallback on slow implementation
2561+
if (rectProjection->numargs < 1 ||
2562+
strncasecmp(rectProjection->args[0], "init=epsg:", strlen("init=epsg:")) != 0)
2563+
{
2564+
if (layer->debug) {
2565+
msDebug("msMSSQL2008LayerGetShapeCount(): cannot find EPSG code of rectProjection. Falling back on client-side feature count.\n");
2566+
}
2567+
return LayerDefaultGetShapeCount(layer, rect, rectProjection);
2568+
}
2569+
2570+
// Reproject the passed rect into the layer projection and get
2571+
// the SRID from the rectProjection
2572+
msProjectRect(rectProjection, &(layer->projection), &searchrectInLayerProj); /* project the searchrect to source coords */
2573+
}
2574+
2575+
if (searchrectInLayerProj.minx == searchrectInLayerProj.maxx ||
2576+
searchrectInLayerProj.miny == searchrectInLayerProj.maxy) {
2577+
/* create point shape for rectangles with zero area */
2578+
snprintf(box3d, sizeof(box3d), "%s::STGeomFromText('POINT(%.15g %.15g)',%s)", /* %s.STSrid)", */
2579+
layerinfo->geom_column_type, searchrectInLayerProj.minx, searchrectInLayerProj.miny, layerinfo->user_srid);
2580+
}
2581+
else {
2582+
snprintf(box3d, sizeof(box3d), "%s::STGeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%s)", /* %s.STSrid)", */
2583+
layerinfo->geom_column_type,
2584+
searchrectInLayerProj.minx, searchrectInLayerProj.miny,
2585+
searchrectInLayerProj.maxx, searchrectInLayerProj.miny,
2586+
searchrectInLayerProj.maxx, searchrectInLayerProj.maxy,
2587+
searchrectInLayerProj.minx, searchrectInLayerProj.maxy,
2588+
searchrectInLayerProj.minx, searchrectInLayerProj.miny,
2589+
layerinfo->user_srid
2590+
);
2591+
}
2592+
2593+
msLayerTranslateFilter(layer, &layer->filter, layer->filteritem);
2594+
2595+
/* set up statement */
2596+
query = msStringConcatenate(query, "SELECT count(*) FROM ");
2597+
query = msStringConcatenate(query, layerinfo->geom_table);
2598+
2599+
/* adding attribute filter */
2600+
if (layer->filter.native_string) {
2601+
query = msStringConcatenate(query, " WHERE (");
2602+
query = msStringConcatenate(query, layer->filter.native_string);
2603+
query = msStringConcatenate(query, ")");
2604+
hasFilter = MS_TRUE;
2605+
}
2606+
else if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) {
2607+
query = msStringConcatenate(query, " WHERE (");
2608+
query = msStringConcatenate(query, msLayerGetProcessingKey(layer, "NATIVE_FILTER"));
2609+
query = msStringConcatenate(query, ")");
2610+
hasFilter = MS_TRUE;
2611+
}
2612+
2613+
/* adding spatial filter */
2614+
if (hasFilter == MS_FALSE)
2615+
query = msStringConcatenate(query, " WHERE ");
2616+
else
2617+
query = msStringConcatenate(query, " AND ");
2618+
2619+
query = msStringConcatenate(query, layerinfo->geom_column);
2620+
query = msStringConcatenate(query, ".MakeValid().STIntersects(");
2621+
query = msStringConcatenate(query, box3d);
2622+
query = msStringConcatenate(query, ") = 1 ");
2623+
2624+
if (!executeSQL(layerinfo->conn, query)) {
2625+
msFree(query);
2626+
return -1;
2627+
}
2628+
2629+
msFree(query);
2630+
2631+
rc = SQLFetch(layerinfo->conn->hstmt);
2632+
2633+
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
2634+
if (layer->debug) {
2635+
msDebug("msMSSQL2008LayerGetShapeCount: No results found.\n");
2636+
}
2637+
2638+
return -1;
2639+
}
2640+
2641+
rc = SQLGetData(layerinfo->conn->hstmt, 1, SQL_C_CHAR, result_data, sizeof(result_data), &retLen);
2642+
2643+
if (rc == SQL_ERROR) {
2644+
msSetError(MS_QUERYERR, "Failed to get feature count", "msMSSQL2008LayerGetShapeCount()");
2645+
return -1;
2646+
}
2647+
2648+
result_data[retLen] = 0;
2649+
2650+
return atoi(result_data);
2651+
}
2652+
24562653
/* Query the DB for info about the requested table */
24572654
int msMSSQL2008LayerGetItems(layerObj *layer)
24582655
{
@@ -2804,6 +3001,32 @@ char *msMSSQL2008LayerEscapeSQLParam(layerObj *layer, const char *pszString)
28043001
return pszEscapedStr;
28053002
}
28063003

3004+
int msMSSQL2008GetPaging(layerObj *layer)
3005+
{
3006+
msMSSQL2008LayerInfo *layerinfo = NULL;
3007+
3008+
if (!msMSSQL2008LayerIsOpen(layer))
3009+
return MS_TRUE;
3010+
3011+
assert(layer->layerinfo != NULL);
3012+
layerinfo = (msMSSQL2008LayerInfo *)layer->layerinfo;
3013+
3014+
return layerinfo->paging;
3015+
}
3016+
3017+
void msMSSQL2008EnablePaging(layerObj *layer, int value)
3018+
{
3019+
msMSSQL2008LayerInfo *layerinfo = NULL;
3020+
3021+
if (!msMSSQL2008LayerIsOpen(layer))
3022+
msMSSQL2008LayerOpen(layer);
3023+
3024+
assert(layer->layerinfo != NULL);
3025+
layerinfo = (msMSSQL2008LayerInfo *)layer->layerinfo;
3026+
3027+
layerinfo->paging = value;
3028+
}
3029+
28073030
int process_node(layerObj* layer, expressionObj *filter)
28083031
{
28093032
char *snippet = NULL;
@@ -3413,6 +3636,12 @@ int msMSSQL2008LayerGetShape(layerObj *layer, shapeObj *shape, long record)
34133636
return MS_FAILURE;
34143637
}
34153638

3639+
int msMSSQL2008LayerGetShapeCount(layerObj *layer, rectObj rect, projectionObj *rectProjection)
3640+
{
3641+
msSetError(MS_QUERYERR, "msMSSQL2008LayerGetShapeCount called but unimplemented!(mapserver not compiled with MSSQL2008 support)", "msMSSQL2008LayerGetShapeCount()");
3642+
return MS_FAILURE;
3643+
}
3644+
34163645
int msMSSQL2008LayerGetExtent(layerObj *layer, rectObj *extent)
34173646
{
34183647
msSetError(MS_QUERYERR, "msMSSQL2008LayerGetExtent called but unimplemented!(mapserver not compiled with MSSQL2008 support)", "msMSSQL2008LayerGetExtent()");
@@ -3437,6 +3666,18 @@ int msMSSQL2008LayerGetItems(layerObj *layer)
34373666
return MS_FAILURE;
34383667
}
34393668

3669+
void msMSSQL2008EnablePaging(layerObj *layer, int value)
3670+
{
3671+
msSetError(MS_QUERYERR, "msMSSQL2008EnablePaging called but unimplemented!(mapserver not compiled with MSSQL2008 support)", "msMSSQL2008EnablePaging()");
3672+
return;
3673+
}
3674+
3675+
int msMSSQL2008GetPaging(layerObj *layer)
3676+
{
3677+
msSetError(MS_QUERYERR, "msMSSQL2008GetPaging called but unimplemented!(mapserver not compiled with MSSQL2008 support)", "msMSSQL2008GetPaging()");
3678+
return MS_FAILURE;
3679+
}
3680+
34403681
int msMSSQL2008LayerTranslateFilter(layerObj *layer, expressionObj *filter, char *filteritem)
34413682
{
34423683
msSetError(MS_QUERYERR, "msMSSQL2008LayerTranslateFilter called but unimplemented!(mapserver not compiled with MSSQL2008 support)", "msMSSQL2008LayerTranslateFilter()");
@@ -3465,6 +3706,8 @@ MS_DLL_EXPORT int PluginInitializeVirtualTable(layerVTableObj* vtable, layerObj
34653706
assert(layer != NULL);
34663707
assert(vtable != NULL);
34673708

3709+
vtable->LayerEnablePaging = msMSSQL2008EnablePaging;
3710+
vtable->LayerGetPaging = msMSSQL2008GetPaging;
34683711
vtable->LayerTranslateFilter = msMSSQL2008LayerTranslateFilter;
34693712
vtable->LayerEscapeSQLParam = msMSSQL2008LayerEscapeSQLParam;
34703713
vtable->LayerEscapePropertyName = msMSSQL2008LayerEscapePropertyName;
@@ -3476,6 +3719,7 @@ MS_DLL_EXPORT int PluginInitializeVirtualTable(layerVTableObj* vtable, layerObj
34763719
vtable->LayerWhichShapes = msMSSQL2008LayerWhichShapes;
34773720
vtable->LayerNextShape = msMSSQL2008LayerNextShape;
34783721
vtable->LayerGetShape = msMSSQL2008LayerGetShape;
3722+
vtable->LayerGetShapeCount = msMSSQL2008LayerGetShapeCount;
34793723

34803724
vtable->LayerClose = msMSSQL2008LayerClose;
34813725

@@ -3503,6 +3747,8 @@ msMSSQL2008LayerInitializeVirtualTable(layerObj *layer)
35033747
assert(layer != NULL);
35043748
assert(layer->vtable != NULL);
35053749

3750+
layer->vtable->LayerEnablePaging = msMSSQL2008EnablePaging;
3751+
layer->vtable->LayerGetPaging = msMSSQL2008GetPaging;
35063752
layer->vtable->LayerTranslateFilter = msMSSQL2008LayerTranslateFilter;
35073753
layer->vtable->LayerEscapeSQLParam = msMSSQL2008LayerEscapeSQLParam;
35083754
layer->vtable->LayerEscapePropertyName = msMSSQL2008LayerEscapePropertyName;
@@ -3514,7 +3760,7 @@ msMSSQL2008LayerInitializeVirtualTable(layerObj *layer)
35143760
layer->vtable->LayerWhichShapes = msMSSQL2008LayerWhichShapes;
35153761
layer->vtable->LayerNextShape = msMSSQL2008LayerNextShape;
35163762
layer->vtable->LayerGetShape = msMSSQL2008LayerGetShape;
3517-
/* layer->vtable->LayerGetShapeCount, use default */
3763+
layer->vtable->LayerGetShapeCount = msMSSQL2008LayerGetShapeCount;
35183764

35193765
layer->vtable->LayerClose = msMSSQL2008LayerClose;
35203766

0 commit comments

Comments
 (0)