[mapguide-commits] r6239 - in trunk/MgDev:
Oem/FDO/ProviderList/ServerCommunity Server/src/PostBuild
Server/src/Services/Feature Server/src/UnitTesting
UnitTest/TestData/FeatureService/SQLite
svn_mapguide at osgeo.org
svn_mapguide at osgeo.org
Mon Nov 21 06:00:25 EST 2011
Author: jng
Date: 2011-11-21 03:00:25 -0800 (Mon, 21 Nov 2011)
New Revision: 6239
Added:
trunk/MgDev/Server/src/Services/Feature/ExtendedSelectCommand.cpp
trunk/MgDev/Server/src/Services/Feature/ExtendedSelectCommand.h
trunk/MgDev/Server/src/Services/Feature/FdoForcedOneToOneFeatureReader.cpp
trunk/MgDev/Server/src/Services/Feature/FdoForcedOneToOneFeatureReader.h
trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/JoinTest.sqlite
trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/ParcelsJoinTest.sqlite
trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/UT_FdoJoin.FeatureSource
trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/UT_Parcels_SQLite_Join.FeatureSource
Modified:
trunk/MgDev/Oem/FDO/ProviderList/ServerCommunity/providers.xml
trunk/MgDev/Server/src/PostBuild/PostBuild.mak
trunk/MgDev/Server/src/Services/Feature/FeatureServiceCommand.cpp
trunk/MgDev/Server/src/Services/Feature/SelectAggregateCommand.cpp
trunk/MgDev/Server/src/Services/Feature/SelectAggregateCommand.h
trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcproj
trunk/MgDev/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp
trunk/MgDev/Server/src/Services/Feature/ServerFeatureUtil.cpp
trunk/MgDev/Server/src/Services/Feature/ServerFeatureUtil.h
trunk/MgDev/Server/src/Services/Feature/ServerSelectFeatures.cpp
trunk/MgDev/Server/src/Services/Feature/ServerSelectFeatures.h
trunk/MgDev/Server/src/UnitTesting/TestFeatureService.cpp
trunk/MgDev/Server/src/UnitTesting/TestFeatureService.h
Log:
#1854: Implement RFC123. This submission takes advantage of recently introduced FDO Join APIs to provide a high-performance optimization shortcut for Feature Joins if:
a) The extended feature class joins against another feature class from the same feature source
b) The feature source supports the FDO Join APIs. As of FDO 3.7, SQLite and SQL Server satisfy this condition.
Although use of the FDO expression engine is generally avoided where possible due to performance, we have no choice but to use the FdoExpressionEngineUtilDataReader here in order to support SpatialExtents(). The reason we need to support SpatialExtents() on extended feature classes is in order for the Feature Source Preview to generate a usable MBR for previewing data.
Also included are unit tests to benchmark and test some SQLite-based feature sources set up to take advantage of this optimization shortcut. The 2 SQLite data stores used (and included in this submission) are:
1. The Sheboygan Parcels data set, split into 2 parts:
- The autogenerated id, geometry and join key (ParcelFeatures)
- The remaining attributes, including the join key (Parcels)
2. A hand-built data set of US/Australian countries/states/cities
See the RFC123 link for full details.
Building in VS2010 will be momentarily broken (as I do not have access to VS2010 on hand to update its vcxproj files)
Modified: trunk/MgDev/Oem/FDO/ProviderList/ServerCommunity/providers.xml
===================================================================
--- trunk/MgDev/Oem/FDO/ProviderList/ServerCommunity/providers.xml 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Oem/FDO/ProviderList/ServerCommunity/providers.xml 2011-11-21 11:00:25 UTC (rev 6239)
@@ -1,21 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FeatureProviderRegistry>
<FeatureProvider>
- <Name>OSGeo.SDF.3.5</Name>
+ <Name>OSGeo.SDF.3.7</Name>
<DisplayName>OSGeo FDO Provider for SDF</DisplayName>
<Description>Read/write access to Autodesk's spatial database format, a file-based personal geodatabase that supports multiple features/attributes, spatial indexing, and file-locking.</Description>
<IsManaged>False</IsManaged>
- <Version>3.5.0.0</Version>
- <FeatureDataObjectsVersion>3.5.0.0</FeatureDataObjectsVersion>
+ <Version>3.7.0.0</Version>
+ <FeatureDataObjectsVersion>3.7.0.0</FeatureDataObjectsVersion>
<LibraryPath>SDFProvider.dll</LibraryPath>
</FeatureProvider>
<FeatureProvider>
- <Name>OSGeo.SHP.3.5</Name>
+ <Name>OSGeo.SHP.3.7</Name>
<DisplayName>OSGeo FDO Provider for SHP</DisplayName>
<Description>Read/write access to spatial and attribute data in an ESRI SHP file.</Description>
<IsManaged>False</IsManaged>
- <Version>3.5.0.0</Version>
- <FeatureDataObjectsVersion>3.5.0.0</FeatureDataObjectsVersion>
+ <Version>3.7.0.0</Version>
+ <FeatureDataObjectsVersion>3.7.0.0</FeatureDataObjectsVersion>
<LibraryPath>ShpProvider.dll</LibraryPath>
</FeatureProvider>
+ <FeatureProvider>
+ <Name>OSGeo.SQLite.3.7</Name>
+ <DisplayName>OSGeo FDO Provider for SQLite</DisplayName>
+ <Description>Read/write access to spatial and attribute data in an SQLite file.</Description>
+ <IsManaged>False</IsManaged>
+ <Version>3.7.0.0</Version>
+ <FeatureDataObjectsVersion>3.7.0.0</FeatureDataObjectsVersion>
+ <LibraryPath>SQLiteProvider.dll</LibraryPath>
+ </FeatureProvider>
</FeatureProviderRegistry>
Modified: trunk/MgDev/Server/src/PostBuild/PostBuild.mak
===================================================================
--- trunk/MgDev/Server/src/PostBuild/PostBuild.mak 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/PostBuild/PostBuild.mak 2011-11-21 11:00:25 UTC (rev 6239)
@@ -243,6 +243,10 @@
..\..\bin\UnitTestFiles\UT_Annotation3.mdf \
..\..\bin\UnitTestFiles\SavePointTest.sqlite \
..\..\bin\UnitTestFiles\SavePointTest.FeatureSource \
+ ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource \
+ ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource \
+ ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite \
+ ..\..\bin\UnitTestFiles\JoinTest.sqlite \
CopyFdoComponentsDebug \
CopyFdoProvidersDebug \
CopySchemaDebug \
@@ -496,6 +500,10 @@
..\..\bin\UnitTestFiles\UT_Annotation3.mdf \
..\..\bin\UnitTestFiles\SavePointTest.sqlite \
..\..\bin\UnitTestFiles\SavePointTest.FeatureSource \
+ ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource \
+ ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource \
+ ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite \
+ ..\..\bin\UnitTestFiles\JoinTest.sqlite \
CopyFdoComponentsDebug64 \
CopyFdoProvidersDebug64 \
CopySchemaDebug64 \
@@ -749,6 +757,10 @@
..\..\bin\UnitTestFiles\UT_Annotation3.mdf \
..\..\bin\UnitTestFiles\SavePointTest.sqlite \
..\..\bin\UnitTestFiles\SavePointTest.FeatureSource \
+ ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource \
+ ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource \
+ ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite \
+ ..\..\bin\UnitTestFiles\JoinTest.sqlite \
CopyFdoComponentsRelease \
CopyFdoProvidersRelease \
CopySchemaRelease \
@@ -1002,6 +1014,10 @@
..\..\bin\UnitTestFiles\UT_Annotation3.mdf \
..\..\bin\UnitTestFiles\SavePointTest.sqlite \
..\..\bin\UnitTestFiles\SavePointTest.FeatureSource \
+ ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource \
+ ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource \
+ ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite \
+ ..\..\bin\UnitTestFiles\JoinTest.sqlite \
CopyFdoComponentsRelease64 \
CopyFdoProvidersRelease64 \
CopySchemaRelease64 \
@@ -1128,6 +1144,10 @@
if EXIST ..\..\bin\UnitTestFiles\UT_Annotation3.mdf del /F ..\..\bin\UnitTestFiles\UT_Annotation3.mdf
if EXIST ..\..\bin\UnitTestFiles\SavePointTest.sqlite del /F ..\..\bin\UnitTestFiles\SavePointTest.sqlite
if EXIST ..\..\bin\UnitTestFiles\SavePointTest.FeatureSource del /F ..\..\bin\UnitTestFiles\SavePointTest.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource del /F ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource del /F ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite del /F ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite
+ if EXIST ..\..\bin\UnitTestFiles\JoinTest.sqlite del /F ..\..\bin\UnitTestFiles\JoinTest.FeatureSource
del /F ..\..\bin\debug\*.ilk
@@ -1249,6 +1269,10 @@
if EXIST ..\..\bin\UnitTestFiles\UT_Annotation3.mdf del /F ..\..\bin\UnitTestFiles\UT_Annotation3.mdf
if EXIST ..\..\bin\UnitTestFiles\SavePointTest.sqlite del /F ..\..\bin\UnitTestFiles\SavePointTest.sqlite
if EXIST ..\..\bin\UnitTestFiles\SavePointTest.FeatureSource del /F ..\..\bin\UnitTestFiles\SavePointTest.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource del /F ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource del /F ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite del /F ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite
+ if EXIST ..\..\bin\UnitTestFiles\JoinTest.sqlite del /F ..\..\bin\UnitTestFiles\JoinTest.FeatureSource
del /F ..\..\bin\debug64\*.ilk
@@ -1370,8 +1394,11 @@
if EXIST ..\..\bin\UnitTestFiles\UT_Annotation3.mdf del /F ..\..\bin\UnitTestFiles\UT_Annotation3.mdf
if EXIST ..\..\bin\UnitTestFiles\SavePointTest.sqlite del /F ..\..\bin\UnitTestFiles\SavePointTest.sqlite
if EXIST ..\..\bin\UnitTestFiles\SavePointTest.FeatureSource del /F ..\..\bin\UnitTestFiles\SavePointTest.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource del /F ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource del /F ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite del /F ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite
+ if EXIST ..\..\bin\UnitTestFiles\JoinTest.sqlite del /F ..\..\bin\UnitTestFiles\JoinTest.FeatureSource
-
cleanrelease64:
if EXIST ..\..\bin\release64\MgFoundation.dll del /F ..\..\bin\release64\MgFoundation.dll
if EXIST ..\..\bin\release64\MgFoundation.pdb del /F ..\..\bin\release64\MgFoundation.pdb
@@ -1490,6 +1517,10 @@
if EXIST ..\..\bin\UnitTestFiles\UT_Annotation3.mdf del /F ..\..\bin\UnitTestFiles\UT_Annotation3.mdf
if EXIST ..\..\bin\UnitTestFiles\SavePointTest.sqlite del /F ..\..\bin\UnitTestFiles\SavePointTest.sqlite
if EXIST ..\..\bin\UnitTestFiles\SavePointTest.FeatureSource del /F ..\..\bin\UnitTestFiles\SavePointTest.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource del /F ..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource del /F ..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource
+ if EXIST ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite del /F ..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite
+ if EXIST ..\..\bin\UnitTestFiles\JoinTest.sqlite del /F ..\..\bin\UnitTestFiles\JoinTest.FeatureSource
"..\..\..\UnitTest\TestData\ResourceService\LibraryRepositoryContent.xml" :
@@ -1575,6 +1606,10 @@
"..\..\..\UnitTest\TestData\Symbology\UT_Annotation2.mdf" :
"..\..\..\UnitTest\TestData\Symbology\UT_Annotation3.ldf" :
"..\..\..\UnitTest\TestData\Symbology\UT_Annotation3.mdf" :
+"..\..\..\UnitTest\TestData\Symbology\UT_Parcels_SQLite_Join.FeatureSource" :
+"..\..\..\UnitTest\TestData\Symbology\UT_FdoJoin.FeatureSource" :
+"..\..\..\UnitTest\TestData\Symbology\ParcelsJoinTest.sqlite" :
+"..\..\..\UnitTest\TestData\Symbology\JoinTest.sqlite" :
..\..\bin\UnitTestFiles\LibraryRepositoryContent.xml : "..\..\..\UnitTest\TestData\ResourceService\LibraryRepositoryContent.xml"
if NOT EXIST ..\..\bin\UnitTestFiles\nul mkdir ..\..\bin\UnitTestFiles
@@ -1919,3 +1954,19 @@
..\..\bin\UnitTestFiles\SavePointTest.FeatureSource : "..\..\..\UnitTest\TestData\FeatureService\SQLite\SavePointTest.FeatureSource"
if NOT EXIST ..\..\bin\UnitTestFiles\nul mkdir ..\..\bin\UnitTestFiles
if EXIST "..\..\..\UnitTest\TestData\FeatureService\SQLite\SavePointTest.FeatureSource" xcopy /r /d /y "..\..\..\UnitTest\TestData\FeatureService\SQLite\SavePointTest.FeatureSource" ..\..\bin\UnitTestFiles\
+
+..\..\bin\UnitTestFiles\UT_Parcels_SQLite_Join.FeatureSource : "..\..\..\UnitTest\TestData\FeatureService\SQLite\UT_Parcels_SQLite_Join.FeatureSource"
+ if NOT EXIST ..\..\bin\UnitTestFiles\nul mkdir ..\..\bin\UnitTestFiles
+ if EXIST "..\..\..\UnitTest\TestData\FeatureService\SQLite\UT_Parcels_SQLite_Join.FeatureSource" xcopy /r /d /y "..\..\..\UnitTest\TestData\FeatureService\SQLite\UT_Parcels_SQLite_Join.FeatureSource" ..\..\bin\UnitTestFiles\
+
+..\..\bin\UnitTestFiles\UT_FdoJoin.FeatureSource : "..\..\..\UnitTest\TestData\FeatureService\SQLite\UT_FdoJoin.FeatureSource"
+ if NOT EXIST ..\..\bin\UnitTestFiles\nul mkdir ..\..\bin\UnitTestFiles
+ if EXIST "..\..\..\UnitTest\TestData\FeatureService\SQLite\UT_FdoJoin.FeatureSource" xcopy /r /d /y "..\..\..\UnitTest\TestData\FeatureService\SQLite\UT_FdoJoin.FeatureSource" ..\..\bin\UnitTestFiles\
+
+..\..\bin\UnitTestFiles\ParcelsJoinTest.sqlite : "..\..\..\UnitTest\TestData\FeatureService\SQLite\ParcelsJoinTest.sqlite"
+ if NOT EXIST ..\..\bin\UnitTestFiles\nul mkdir ..\..\bin\UnitTestFiles
+ if EXIST "..\..\..\UnitTest\TestData\FeatureService\SQLite\ParcelsJoinTest.sqlite" xcopy /r /d /y "..\..\..\UnitTest\TestData\FeatureService\SQLite\ParcelsJoinTest.sqlite" ..\..\bin\UnitTestFiles\
+
+..\..\bin\UnitTestFiles\JoinTest.sqlite : "..\..\..\UnitTest\TestData\FeatureService\SQLite\JoinTest.sqlite"
+ if NOT EXIST ..\..\bin\UnitTestFiles\nul mkdir ..\..\bin\UnitTestFiles
+ if EXIST "..\..\..\UnitTest\TestData\FeatureService\SQLite\JoinTest.sqlite" xcopy /r /d /y "..\..\..\UnitTest\TestData\FeatureService\SQLite\JoinTest.sqlite" ..\..\bin\UnitTestFiles\
\ No newline at end of file
Added: trunk/MgDev/Server/src/Services/Feature/ExtendedSelectCommand.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ExtendedSelectCommand.cpp (rev 0)
+++ trunk/MgDev/Server/src/Services/Feature/ExtendedSelectCommand.cpp 2011-11-21 11:00:25 UTC (rev 6239)
@@ -0,0 +1,417 @@
+//
+// Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of version 2.1 of the GNU Lesser
+// General Public License as published by the Free Software Foundation.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+#include "ServerFeatureServiceDefs.h"
+#include "MapGuideCommon.h"
+#include "Services/FeatureService.h"
+#include "FeatureServiceCommand.h"
+#include "ExtendedSelectCommand.h"
+#include "SelectAggregateCommand.h"
+#include "ServerFeatureReader.h"
+#include "ServerFeatureConnection.h"
+#include "FdoFeatureReader.h"
+#include "FdoFilterCollection.h"
+#include "FdoReaderCollection.h"
+#include "Util/FdoExpressionEngineUtilDataReader.h"
+#include "FdoForcedOneToOneFeatureReader.h"
+
+// The maximum size of the subfilter for a selection query. Tune this value for optimal selection perfomance.
+#define MG_MAX_SUBFILTER_SIZE 250
+
+MgExtendedSelectCommand::MgExtendedSelectCommand(MgResourceIdentifier* resource)
+{
+ CHECKNULL((MgResourceIdentifier*)resource, L"MgExtendedSelectCommand.MgSelectCommand");
+
+ // Connect to provider
+ m_connection = new MgServerFeatureConnection(resource);
+ if ((NULL != m_connection.p) && ( m_connection->IsConnectionOpen() ))
+ {
+ m_providerName = m_connection->GetProviderName();
+ }
+ else
+ {
+ throw new MgConnectionFailedException(L"MgExtendedSelectCommand.MgSelectCommand", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+ // Create FdoISelect command
+ FdoPtr<FdoIConnection> fdoConn = m_connection->GetConnection();
+ m_command = (FdoIExtendedSelect*)fdoConn->CreateCommand(FdoCommandType_ExtendedSelect);
+ CHECKNULL((FdoIExtendedSelect*)m_command, L"MgExtendedSelectCommand.MgSelectCommand");
+}
+
+MgExtendedSelectCommand::~MgExtendedSelectCommand()
+{
+ m_command = NULL;
+ m_filter = NULL;
+}
+
+FdoIdentifierCollection* MgExtendedSelectCommand::GetPropertyNames()
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.GetPropertyNames");
+ return m_command->GetPropertyNames();
+}
+
+FdoJoinCriteriaCollection* MgExtendedSelectCommand::GetJoinCriteria()
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.GetJoinCriteria");
+ return m_command->GetJoinCriteria();
+}
+
+void MgExtendedSelectCommand::SetAlias(FdoString* alias)
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.SetDistinct");
+ m_command->SetAlias(alias);
+}
+
+void MgExtendedSelectCommand::SetDistinct(bool value)
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.SetDistinct");
+ // This operation is not supported by FdoISelect
+ // m_command->SetDistinct(value);
+
+ // throw new MgInvalidOperationException(L"MgExtendedSelectCommand.SetDistinct", __LINE__, __WFILE__, NULL, L"", NULL);
+}
+
+bool MgExtendedSelectCommand::GetDistinct()
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.GetDistinct");
+ // This operation is not supported by FdoISelect
+ // return m_command->GetDistinct();
+
+ // throw new MgInvalidOperationException(L"MgExtendedSelectCommand.GetDistinct", __LINE__, __WFILE__, NULL, L"", NULL);
+
+ return false;
+}
+
+void MgExtendedSelectCommand::SetFetchSize(FdoInt32 fetchSize)
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.SetFetchSize");
+ m_command->SetFetchSize(fetchSize);
+}
+
+FdoInt32 MgExtendedSelectCommand::GetFetchSize()
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.GetFetchSize");
+ return m_command->GetFetchSize();
+}
+
+FdoIdentifierCollection* MgExtendedSelectCommand::GetOrdering()
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.GetOrdering");
+ return m_command->GetOrdering();
+}
+
+void MgExtendedSelectCommand::SetOrderingOption(FdoOrderingOption option)
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.SetOrderingOption");
+ ((FdoIBaseSelect*)m_command)->SetOrderingOption(option);
+}
+
+FdoOrderingOption MgExtendedSelectCommand::GetOrderingOption()
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.GetOrderingOption");
+ return ((FdoIBaseSelect*)m_command)->GetOrderingOption();
+}
+
+FdoIdentifierCollection* MgExtendedSelectCommand::GetGrouping()
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.GetGrouping");
+ // This operation is not supported by FdoISelect
+ // return m_command->GetGrouping();
+
+ // throw new MgInvalidOperationException(L"MgExtendedSelectCommand.GetGrouping", __LINE__, __WFILE__, NULL, L"", NULL);
+ return NULL;
+}
+
+void MgExtendedSelectCommand::SetGroupingFilter(FdoFilter* filter)
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.SetGroupingFilter");
+ // This operation is not supported by FdoISelect
+ // m_command->SetGroupingFilter(filter);
+
+ // throw new MgInvalidOperationException(L"MgExtendedSelectCommand.SetGroupingFilter", __LINE__, __WFILE__, NULL, L"", NULL);
+}
+
+FdoFilter* MgExtendedSelectCommand::GetGroupingFilter()
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.GetGroupingFilter");
+ // This operation is not supported by FdoISelect
+ // return m_command->GetGroupingFilter(filter);
+
+ // throw new MgInvalidOperationException(L"MgExtendedSelectCommand.GetGroupingFilter", __LINE__, __WFILE__, NULL, L"", NULL);
+ return NULL;
+}
+
+void MgExtendedSelectCommand::SetFeatureClassName(FdoString* value)
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.SetFeatureClassName");
+ m_command->SetFeatureClassName(value);
+}
+
+void MgExtendedSelectCommand::SetFilter(FdoString* value)
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.SetFilter");
+ m_command->SetFilter(value);
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join] Set Filter: %W"), value));
+#endif
+}
+
+void MgExtendedSelectCommand::SetFilter(FdoFilter* value)
+{
+ CHECKNULL((FdoISelect*)m_command, L"MgExtendedSelectCommand.SetFilter");
+ m_command->SetFilter(value);
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join] Set Filter: %W"), value->ToString()));
+#endif
+ m_filter = FDO_SAFE_ADDREF(value);
+}
+
+MgReader* MgExtendedSelectCommand::Execute()
+{
+ throw new MgNotImplementedException(L"MgExtendedSelectCommand.Execute", __LINE__, __WFILE__, NULL, L"", NULL);
+}
+
+MgReader* MgExtendedSelectCommand::ExecuteJoined(MgStringCollection* idPropNames, bool bForceOneToOne)
+{
+ Ptr<MgReader> ret;
+
+ MG_FEATURE_SERVICE_TRY()
+
+ FdoPtr<FdoIFeatureReader> fdoReader = m_command->Execute();
+ if (bForceOneToOne)
+ {
+ FdoPtr<FdoStringCollection> names = MgServerFeatureUtil::MgToFdoStringCollection(idPropNames, false);
+ FdoPtr<FdoIFeatureReader> forcedReader = new MgFdoForcedOneToOneFeatureReader(fdoReader, names);
+ ret = new MgServerFeatureReader(m_connection, forcedReader);
+ }
+ else
+ {
+ ret = new MgServerFeatureReader(m_connection, fdoReader);
+ }
+ MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgExtendedSelectCommand.ExecuteJoined")
+
+ return ret.Detach();
+}
+
+bool MgExtendedSelectCommand::IsSupportedFunction(FdoFunction* fdoFunc)
+{
+ FdoPtr<FdoIConnection> fdoConn = m_connection->GetConnection();
+ return this->IsFdoSupportedFunction(fdoConn, fdoFunc);
+}
+
+bool MgExtendedSelectCommand::SupportsSelectGrouping()
+{
+ FdoPtr<FdoIConnection> fdoConn = m_connection->GetConnection();
+ return MgFeatureServiceCommand::SupportsSelectGrouping(fdoConn);
+}
+
+bool MgExtendedSelectCommand::SupportsSelectOrdering()
+{
+ FdoPtr<FdoIConnection> fdoConn = m_connection->GetConnection();
+ return MgFeatureServiceCommand::SupportsSelectOrdering(fdoConn);
+}
+
+bool MgExtendedSelectCommand::SupportsSelectDistinct()
+{
+ FdoPtr<FdoIConnection> fdoConn = m_connection->GetConnection();
+ return MgFeatureServiceCommand::SupportsSelectDistinct(fdoConn);
+}
+
+FdoFilter* MgExtendedSelectCommand::GetFilter()
+{
+ return FDO_SAFE_ADDREF(m_filter.p);
+}
+
+MgFdoFilterCollection* MgExtendedSelectCommand::GetSubFilters()
+{
+ // Break up a filter into a bunch of smaller filters
+
+ // For now we just reduce a simple case with datastore limitations in handling the number of OR conditions.
+ // This is the case where a filter has only OR spatial conditions that can be broken up into a collection
+ // of smaller OR filters.
+
+ class FdoCommonFilterFragmenter : public virtual FdoIFilterProcessor
+ {
+ private:
+ FdoPtr<FdoFilter> m_newFilter;
+ FdoPtr<FdoIGeometry> m_geomRight;
+ FdoPtr<FdoIGeometry> m_geomLeft;
+
+ int m_OrCount;
+ std::vector<FdoFilter*> m_filters;
+ bool m_isFragmented;
+
+ protected:
+ void HandleFilter( FdoFilter *filter )
+ {
+ filter->Process( this );
+ }
+ public:
+
+ FdoCommonFilterFragmenter( ):m_isFragmented(true)
+ {
+ m_OrCount = 0;
+ m_filters.clear();
+ }
+
+ int GetOrCount() { return m_OrCount; }
+ std::vector<FdoFilter*>& GetFilters() { return m_filters; }
+ bool IsFragmented() { return m_isFragmented; }
+ FdoFilter* GetNewFilter() { return m_newFilter ? FDO_SAFE_ADDREF(m_newFilter.p) : NULL; }
+
+ virtual void Dispose()
+ {
+ delete this;
+ }
+
+ virtual void ProcessBinaryLogicalOperator(FdoBinaryLogicalOperator& filter)
+ {
+ if( filter.GetOperation() != FdoBinaryLogicalOperations_Or )
+ {
+ m_isFragmented = false;
+ return;
+ }
+
+ HandleFilter( FdoPtr<FdoFilter>(filter.GetRightOperand()) );
+ m_newFilter = filter.GetLeftOperand();
+
+ m_OrCount++;
+ }
+ virtual void ProcessComparisonCondition(FdoComparisonCondition& filter)
+ {
+ // Add filter to collection
+ m_filters.push_back(&filter);
+ return;
+ }
+ virtual void ProcessDistanceCondition(FdoDistanceCondition& filter)
+ {
+ m_isFragmented = false;
+ return;
+ }
+
+ virtual void ProcessInCondition(FdoInCondition& filter)
+ {
+ m_isFragmented = false;
+ return;
+ }
+ virtual void ProcessNullCondition(FdoNullCondition& filter)
+ {
+ m_isFragmented = false;
+ return;
+ }
+ virtual void ProcessSpatialCondition(FdoSpatialCondition& filter)
+ {
+ m_isFragmented = false;
+ return;
+ }
+
+ virtual void ProcessUnaryLogicalOperator(FdoUnaryLogicalOperator& filter)
+ {
+ m_isFragmented = false;
+ return;
+ }
+ };
+
+
+ FdoCommonFilterFragmenter fragmenter;
+ if (m_filter)
+ m_filter->Process( &fragmenter );
+
+ FdoPtr<FdoFilter> newFilter = fragmenter.GetNewFilter();
+ while (newFilter != NULL)
+ {
+ newFilter->Process( &fragmenter );
+ FdoPtr<FdoFilter> tempFilter = fragmenter.GetNewFilter();
+ if (tempFilter != newFilter)
+ {
+ newFilter = tempFilter;
+ }
+ else
+ {
+ newFilter = NULL;
+ }
+ }
+
+#ifdef _DEBUG
+ int nCount = fragmenter.GetOrCount();
+ if ( nCount > 0)
+ {
+ char temp[1024];
+ sprintf(temp, "Or_Count = %d", nCount); // NOXLATE
+ printf("%s\n", temp);
+ }
+#endif
+
+ FdoPtr<MgFdoFilterCollection> filters = MgFdoFilterCollection::Create();
+
+ if (fragmenter.IsFragmented() && fragmenter.GetOrCount() > 0)
+ {
+ int nSelectionCount = 0;
+
+ std::vector<FdoFilter*>::iterator filterIter;
+ bool bFirst = true;
+
+ FdoStringP filterString;
+ std::vector<FdoFilter*>& fragmentedFilters = fragmenter.GetFilters();
+
+
+ bool bIsAddedToCollection = false;
+
+ for (filterIter = fragmentedFilters.begin(); filterIter != fragmentedFilters.end(); filterIter++)
+ {
+ FdoStringP tempString = (*filterIter)->ToString();
+ FdoStringP orString = L" OR "; // NOXLATE
+ if (bFirst)
+ {
+ filterString = tempString;
+ bFirst = false;
+ }
+ else
+ {
+ filterString = filterString + orString + tempString;
+ nSelectionCount++;
+ }
+
+ if (nSelectionCount >= MG_MAX_SUBFILTER_SIZE)
+ {
+ FdoPtr<FdoFilter> filter = FdoFilter::Parse(filterString);
+ filters->Add(filter);
+ bFirst = true;
+ filterString = L"";
+ nSelectionCount = 0;
+ bIsAddedToCollection = true;
+ }
+ else
+ {
+ bIsAddedToCollection = false;
+ }
+ }
+
+ if ( !bIsAddedToCollection )
+ {
+ FdoPtr<FdoFilter> filter = FdoFilter::Parse(filterString);
+ filters->Add(filter);
+ }
+
+ }
+ else
+ {
+ filters->Add(m_filter);
+ }
+
+ return filters.Detach();
+}
Added: trunk/MgDev/Server/src/Services/Feature/ExtendedSelectCommand.h
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ExtendedSelectCommand.h (rev 0)
+++ trunk/MgDev/Server/src/Services/Feature/ExtendedSelectCommand.h 2011-11-21 11:00:25 UTC (rev 6239)
@@ -0,0 +1,83 @@
+//
+// Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of version 2.1 of the GNU Lesser
+// General Public License as published by the Free Software Foundation.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+#ifndef _MGEXTENDEDSELECTCOMMAND_H_
+#define _MGEXTENDEDSELECTCOMMAND_H_
+
+class MgFdoFeatureReader;
+class MgFdoReaderCollection;
+class MgFdoFilterCollection;
+
+class MgExtendedSelectCommand : public MgFeatureServiceCommand
+{
+ DECLARE_CLASSNAME(MgExtendedSelectCommand)
+
+public:
+ MgExtendedSelectCommand(MgResourceIdentifier* resource);
+ virtual ~MgExtendedSelectCommand();
+
+ virtual FdoIdentifierCollection* GetPropertyNames();
+
+ virtual void SetDistinct( bool value );
+ virtual bool GetDistinct( );
+
+ virtual void SetFetchSize(FdoInt32 fetchSize);
+ virtual FdoInt32 GetFetchSize();
+
+ virtual FdoIdentifierCollection* GetOrdering();
+ virtual void SetOrderingOption( FdoOrderingOption option );
+ virtual FdoOrderingOption GetOrderingOption( );
+
+ virtual FdoIdentifierCollection* GetGrouping();
+ virtual void SetGroupingFilter( FdoFilter* filter );
+ virtual FdoFilter* GetGroupingFilter( );
+
+ virtual void SetFeatureClassName(FdoString* value);
+ virtual void SetFilter(FdoString* value);
+ virtual void SetFilter(FdoFilter* value);
+
+ virtual FdoFilter* GetFilter();
+
+ virtual MgReader* Execute(); //Not used
+ MgReader* ExecuteJoined(MgStringCollection* idPropNames, bool bForceOneToOne);
+
+ virtual bool IsSupportedFunction(FdoFunction* fdoFunc);
+
+ virtual bool SupportsSelectGrouping();
+ virtual bool SupportsSelectOrdering();
+ virtual bool SupportsSelectDistinct();
+
+ virtual void Dispose()
+ {
+ delete this;
+ }
+
+ virtual FdoJoinCriteriaCollection* GetJoinCriteria();
+
+ virtual void SetAlias(FdoString* alias);
+
+private:
+ Ptr<MgServerFeatureConnection> m_connection;
+ STRING m_providerName;
+ FdoPtr<FdoIExtendedSelect> m_command;
+
+ FdoPtr<FdoFilter> m_filter;
+
+ MgFdoFilterCollection* GetSubFilters();
+};
+
+#endif
Added: trunk/MgDev/Server/src/Services/Feature/FdoForcedOneToOneFeatureReader.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/FdoForcedOneToOneFeatureReader.cpp (rev 0)
+++ trunk/MgDev/Server/src/Services/Feature/FdoForcedOneToOneFeatureReader.cpp 2011-11-21 11:00:25 UTC (rev 6239)
@@ -0,0 +1,376 @@
+#include "FdoForcedOneToOneFeatureReader.h"
+
+// constructs a MgFdoForcedOneToOneFeatureReader
+MgFdoForcedOneToOneFeatureReader::MgFdoForcedOneToOneFeatureReader() { }
+
+MgFdoForcedOneToOneFeatureReader::MgFdoForcedOneToOneFeatureReader(FdoIFeatureReader* reader, FdoStringCollection* idPropNames)
+{
+ m_cachedClsDef = NULL;
+ m_reader = FDO_SAFE_ADDREF(reader);
+ m_idPropNames = FDO_SAFE_ADDREF(idPropNames);
+}
+
+// default destructor
+MgFdoForcedOneToOneFeatureReader::~MgFdoForcedOneToOneFeatureReader()
+{
+ m_idValues.clear();
+ FDO_SAFE_RELEASE(m_reader);
+ FDO_SAFE_RELEASE(m_idPropNames);
+ FDO_SAFE_RELEASE(m_cachedClsDef);
+}
+
+void MgFdoForcedOneToOneFeatureReader::Dispose() { delete this; }
+
+FdoClassDefinition* MgFdoForcedOneToOneFeatureReader::GetClassDefinition()
+{
+ return m_reader->GetClassDefinition();
+}
+
+int MgFdoForcedOneToOneFeatureReader::GetDepth()
+{
+ return m_reader->GetDepth();
+}
+
+bool MgFdoForcedOneToOneFeatureReader::GetBoolean(const wchar_t *propertyName)
+{
+ return m_reader->GetBoolean(propertyName);
+}
+
+FdoByte MgFdoForcedOneToOneFeatureReader::GetByte(const wchar_t *propertyName)
+{
+ return m_reader->GetByte(propertyName);
+}
+
+double MgFdoForcedOneToOneFeatureReader::GetDouble(const wchar_t* propertyName)
+{
+ return m_reader->GetDouble(propertyName);
+}
+
+short MgFdoForcedOneToOneFeatureReader::GetInt16(const wchar_t *propertyName)
+{
+ return m_reader->GetInt16(propertyName);
+}
+
+int MgFdoForcedOneToOneFeatureReader::GetInt32(const wchar_t *propertyName)
+{
+ return m_reader->GetInt32(propertyName);
+}
+
+FdoInt64 MgFdoForcedOneToOneFeatureReader::GetInt64(const wchar_t *propertyName)
+{
+ return m_reader->GetInt64(propertyName);
+}
+
+float MgFdoForcedOneToOneFeatureReader::GetSingle(const wchar_t *propertyName)
+{
+ return m_reader->GetSingle(propertyName);
+}
+
+const wchar_t* MgFdoForcedOneToOneFeatureReader::GetString(const wchar_t *propertyName)
+{
+ return m_reader->GetString(propertyName);
+}
+
+FdoLOBValue* MgFdoForcedOneToOneFeatureReader::GetLOB(const wchar_t* propertyName)
+{
+ return m_reader->GetLOB(propertyName);
+}
+
+FdoIStreamReader* MgFdoForcedOneToOneFeatureReader::GetLOBStreamReader(const wchar_t* propertyName)
+{
+ return m_reader->GetLOBStreamReader(propertyName);
+}
+
+bool MgFdoForcedOneToOneFeatureReader::IsNull(const wchar_t *propertyName)
+{
+ return m_reader->IsNull(propertyName);
+}
+
+FdoIFeatureReader* MgFdoForcedOneToOneFeatureReader::GetFeatureObject(const wchar_t* propertyName)
+{
+ return m_reader->GetFeatureObject(propertyName);
+}
+
+FdoByteArray* MgFdoForcedOneToOneFeatureReader::GetGeometry(const wchar_t* propertyName)
+{
+ return m_reader->GetGeometry(propertyName);
+}
+
+const FdoByte * MgFdoForcedOneToOneFeatureReader::GetGeometry(const wchar_t* propertyName, FdoInt32 * count)
+{
+ return m_reader->GetGeometry(propertyName, count);
+}
+
+FdoIRaster* MgFdoForcedOneToOneFeatureReader::GetRaster(const wchar_t* propertyName)
+{
+ return m_reader->GetRaster(propertyName);
+}
+
+bool MgFdoForcedOneToOneFeatureReader::IsNull(FdoInt32 index)
+{
+ return m_reader->IsNull(index);
+}
+
+const wchar_t* MgFdoForcedOneToOneFeatureReader::GetString(FdoInt32 index)
+{
+ return m_reader->GetString(index);
+}
+
+bool MgFdoForcedOneToOneFeatureReader::GetBoolean(FdoInt32 index)
+{
+ return m_reader->GetBoolean(index);
+}
+
+FdoByte MgFdoForcedOneToOneFeatureReader::GetByte(FdoInt32 index)
+{
+ return m_reader->GetByte(index);
+}
+
+FdoDateTime MgFdoForcedOneToOneFeatureReader::GetDateTime(FdoInt32 index)
+{
+ return m_reader->GetDateTime(index);
+}
+
+double MgFdoForcedOneToOneFeatureReader::GetDouble(FdoInt32 index)
+{
+ return m_reader->GetDouble(index);
+}
+
+short MgFdoForcedOneToOneFeatureReader::GetInt16(FdoInt32 index)
+{
+ return m_reader->GetInt16(index);
+}
+
+int MgFdoForcedOneToOneFeatureReader::GetInt32(FdoInt32 index)
+{
+ return m_reader->GetInt32(index);
+}
+
+FdoInt64 MgFdoForcedOneToOneFeatureReader::GetInt64(FdoInt32 index)
+{
+ return m_reader->GetInt64(index);
+}
+
+float MgFdoForcedOneToOneFeatureReader::GetSingle(FdoInt32 index)
+{
+ return m_reader->GetSingle(index);
+}
+
+FdoLOBValue* MgFdoForcedOneToOneFeatureReader::GetLOB(FdoInt32 index)
+{
+ return m_reader->GetLOB(index);
+}
+
+FdoIStreamReader* MgFdoForcedOneToOneFeatureReader::GetLOBStreamReader(FdoInt32 index)
+{
+ return m_reader->GetLOBStreamReader(index);
+}
+
+FdoIRaster* MgFdoForcedOneToOneFeatureReader::GetRaster(FdoInt32 index)
+{
+ return m_reader->GetRaster(index);
+}
+
+const FdoByte* MgFdoForcedOneToOneFeatureReader::GetGeometry(FdoInt32 index, FdoInt32 * count)
+{
+ return m_reader->GetGeometry(index, count);
+}
+
+FdoByteArray* MgFdoForcedOneToOneFeatureReader::GetGeometry(FdoInt32 index)
+{
+ return m_reader->GetGeometry(index);
+}
+
+FdoIFeatureReader* MgFdoForcedOneToOneFeatureReader::GetFeatureObject (FdoInt32 index)
+{
+ return m_reader->GetFeatureObject(index);
+}
+
+FdoString* MgFdoForcedOneToOneFeatureReader::GetPropertyName(FdoInt32 index)
+{
+ return m_reader->GetPropertyName(index);
+}
+
+FdoInt32 MgFdoForcedOneToOneFeatureReader::GetPropertyIndex(FdoString* propertyName)
+{
+ return m_reader->GetPropertyIndex(propertyName);
+}
+
+bool MgFdoForcedOneToOneFeatureReader::ReadNext()
+{
+ bool ret = false;
+
+ ret = m_reader->ReadNext();
+ if (!ret) //End-of-reader
+ return ret;
+
+ //So we're forcing one-to-one, this means we need to keep track of
+ //identity property values. We hash each one and store in a set, if
+ //our generated hash already exists, we skip it.
+ STRING hash = GetIdentityHash();
+ while (m_idValues.find(hash) != m_idValues.end())
+ {
+ ret = m_reader->ReadNext(); //Read next feature
+ if (!ret) //End-of-reader
+ return ret;
+ hash = GetIdentityHash();
+ }
+ //Add this hash
+ m_idValues.insert(hash);
+
+ return ret;
+}
+
+void MgFdoForcedOneToOneFeatureReader::Close()
+{
+ m_reader->Close();
+}
+
+
+FdoDateTime MgFdoForcedOneToOneFeatureReader::GetDateTime(const wchar_t *propertyName )
+{
+ return m_reader->GetDateTime(propertyName);
+}
+
+STRING MgFdoForcedOneToOneFeatureReader::GetIdentityHash()
+{
+ STRING ret;
+
+ if (NULL == m_cachedClsDef)
+ {
+ m_cachedClsDef = m_reader->GetClassDefinition();
+ }
+
+ //return the current identity values as: <identity 1>|<identity 2>|...|<identity n>
+ const wchar_t* SEPARATOR = L"|";
+
+ FdoPtr<FdoPropertyDefinitionCollection> clsProps = m_cachedClsDef->GetProperties();
+ for (INT32 i = 0; i < m_idPropNames->GetCount(); i++)
+ {
+ bool append = false;
+ wchar_t tbuff[256];
+ //Don't bother with IsNull() checks. These are identity property values, how can they be null?
+ FdoStringElement* el = m_idPropNames->GetItem(i);
+ FdoStringP str = el->GetString();
+ FdoString* name = (FdoString*)str;
+
+ FdoPtr<FdoPropertyDefinition> propDef = clsProps->GetItem(name);
+ if (propDef->GetPropertyType() == FdoPropertyType_DataProperty)
+ {
+ FdoDataPropertyDefinition* dataProp = static_cast<FdoDataPropertyDefinition*>(propDef.p);
+ FdoDataType dtype = dataProp->GetDataType();
+ //I don't like to assume, but this is a safe assumption to make. There is no way in hell
+ //these types could ever be identity property types:
+ // - BLOB
+ // - CLOB
+ // - Feature
+ // - Geometry
+ // - Raster
+ //So they can be ignored here.
+ switch(dtype)
+ {
+ case FdoDataType_Boolean:
+ {
+ bool val = m_reader->GetBoolean(name);
+ swprintf (tbuff, 256, L"%s", val ? L"TRUE" : L"FALSE");
+ append = true;
+ }
+ break;
+ case FdoDataType_Byte:
+ {
+ BYTE val = m_reader->GetByte(name);
+ swprintf (tbuff, 256, L"%d", val);
+ append = true;
+ }
+ break;
+ case FdoDataType_DateTime:
+ {
+ FdoDateTime dt = m_reader->GetDateTime(name);
+ swprintf (tbuff, 256, L"%d-%d-%d %2d:%2d:%2.4f",
+ dt.year,
+ dt.month,
+ dt.day,
+ dt.hour,
+ dt.minute,
+ dt.seconds);
+ append = true;
+ }
+ break;
+ case FdoDataType_Decimal:
+ case FdoDataType_Double:
+ {
+ double val = m_reader->GetDouble(name);
+ swprintf (tbuff, 256, L"%f", val);
+ append = true;
+ }
+ break;
+ case FdoDataType_Int16:
+ {
+ INT16 val = m_reader->GetInt16(name);
+ swprintf (tbuff, 256, L"%d", val);
+ append = true;
+ }
+ break;
+ case FdoDataType_Int32:
+ {
+ INT32 val = m_reader->GetInt32(name);
+ swprintf (tbuff, 256, L"%ld", val);
+ append = true;
+ }
+ break;
+ case FdoDataType_Int64:
+ {
+ INT64 val = m_reader->GetInt64(name);
+ #ifdef _WIN32
+ _i64tow (val, tbuff, 10);
+ #else
+ swprintf(tbuff, 256, L"%lli", val);
+ #endif
+ append = true;
+ }
+ break;
+ case FdoDataType_Single:
+ {
+ float val = m_reader->GetSingle(name);
+ swprintf (tbuff, 256, L"%f", val);
+ append = true;
+ }
+ break;
+ case FdoDataType_String:
+ {
+ STRING val = m_reader->GetString(name);
+ if (ret.empty())
+ {
+ ret = val;
+ }
+ else
+ {
+ ret += SEPARATOR;
+ ret += val;
+ }
+ append = false; //already appended
+ }
+ break;
+ }
+
+ if (append)
+ {
+ if (ret.empty())
+ {
+ ret = tbuff;
+ }
+ else
+ {
+ ret += SEPARATOR;
+ ret += tbuff;
+ }
+ }
+ }
+ else
+ {
+
+ }
+ }
+
+ return ret;
+}
\ No newline at end of file
Added: trunk/MgDev/Server/src/Services/Feature/FdoForcedOneToOneFeatureReader.h
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/FdoForcedOneToOneFeatureReader.h (rev 0)
+++ trunk/MgDev/Server/src/Services/Feature/FdoForcedOneToOneFeatureReader.h 2011-11-21 11:00:25 UTC (rev 6239)
@@ -0,0 +1,94 @@
+//
+// Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of version 2.1 of the GNU Lesser
+// General Public License as published by the Free Software Foundation.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+#ifndef _MGFDOFORCEDONETOONEFEATUREREADER_H
+#define _MGFDOFORCEDONETOONEFEATUREREADER_H
+
+#include <set>
+
+class FdoIFeatureReader;
+
+class MgFdoForcedOneToOneFeatureReader: public FdoIFeatureReader
+{
+public:
+ // constructs a MgFdoForcedOneToOneFeatureReader
+ MgFdoForcedOneToOneFeatureReader();
+ MgFdoForcedOneToOneFeatureReader(FdoIFeatureReader* reader, FdoStringCollection* idPropNames);
+ // default destructor
+ virtual ~MgFdoForcedOneToOneFeatureReader();
+
+protected:
+
+ virtual void Dispose();
+
+public:
+
+ virtual FdoClassDefinition* GetClassDefinition();
+ virtual int GetDepth();
+ virtual bool GetBoolean( const wchar_t *propertyName );
+ virtual FdoByte GetByte( const wchar_t *propertyName );
+ virtual double GetDouble(const wchar_t* propertyName);
+ virtual short GetInt16( const wchar_t *propertyName );
+ virtual int GetInt32( const wchar_t *propertyName );
+ virtual FdoInt64 GetInt64( const wchar_t *propertyName );
+ virtual float GetSingle( const wchar_t *propertyName );
+ virtual const wchar_t* GetString( const wchar_t *propertyName );
+
+ virtual FdoLOBValue* GetLOB(const wchar_t* propertyName );
+ virtual FdoIStreamReader* GetLOBStreamReader(const wchar_t* propertyName );
+
+ virtual bool IsNull( const wchar_t *propertyName );
+ virtual FdoIFeatureReader* GetFeatureObject(const wchar_t* propertyName);
+ virtual FdoByteArray* GetGeometry(const wchar_t* propertyName);
+ virtual const FdoByte * GetGeometry(const wchar_t* propertyName, FdoInt32 * count);
+ virtual FdoIRaster* GetRaster(const wchar_t* propertyName);
+
+ virtual bool IsNull (FdoInt32 index);
+ virtual const wchar_t* GetString (FdoInt32 index);
+ virtual bool GetBoolean (FdoInt32 index);
+ virtual FdoByte GetByte (FdoInt32 index);
+ virtual FdoDateTime GetDateTime (FdoInt32 index);
+ virtual double GetDouble (FdoInt32 index);
+ virtual short GetInt16 (FdoInt32 index);
+ virtual int GetInt32 (FdoInt32 index);
+ virtual FdoInt64 GetInt64 (FdoInt32 index);
+ virtual float GetSingle (FdoInt32 index);
+ virtual FdoLOBValue* GetLOB (FdoInt32 index);
+ virtual FdoIStreamReader* GetLOBStreamReader (FdoInt32 index);
+ virtual FdoIRaster* GetRaster (FdoInt32 index);
+ virtual const FdoByte * GetGeometry (FdoInt32 index, FdoInt32 * count);
+ virtual FdoByteArray* GetGeometry (FdoInt32 index);
+ virtual FdoIFeatureReader* GetFeatureObject (FdoInt32 index);
+
+ virtual FdoString* GetPropertyName(FdoInt32 index);
+ virtual FdoInt32 GetPropertyIndex(FdoString* propertyName);
+
+ virtual bool ReadNext( );
+ virtual void Close();
+
+ virtual FdoDateTime GetDateTime(const wchar_t *propertyName );
+private:
+ std::set<STRING> m_idValues;
+ STRING GetIdentityHash();
+
+ FdoIFeatureReader* m_reader;
+ FdoStringCollection* m_idPropNames;
+
+ FdoClassDefinition* m_cachedClsDef;
+};
+
+#endif
\ No newline at end of file
Modified: trunk/MgDev/Server/src/Services/Feature/FeatureServiceCommand.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/FeatureServiceCommand.cpp 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/FeatureServiceCommand.cpp 2011-11-21 11:00:25 UTC (rev 6239)
@@ -20,6 +20,7 @@
#include "Services/FeatureService.h"
#include "FeatureServiceCommand.h"
#include "SelectCommand.h"
+#include "ExtendedSelectCommand.h"
#include "SelectAggregateCommand.h"
MgFeatureServiceCommand* MgFeatureServiceCommand::CreateCommand(MgResourceIdentifier* resource, FdoCommandType commandType)
@@ -37,6 +38,11 @@
command = new MgSelectAggregateCommand(resource);
break;
}
+ case FdoCommandType_ExtendedSelect:
+ {
+ command = new MgExtendedSelectCommand(resource);
+ break;
+ }
}
return command.Detach();
}
Modified: trunk/MgDev/Server/src/Services/Feature/SelectAggregateCommand.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/SelectAggregateCommand.cpp 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/SelectAggregateCommand.cpp 2011-11-21 11:00:25 UTC (rev 6239)
@@ -23,7 +23,12 @@
#include "SelectAggregateCommand.h"
#include "ServerDataReader.h"
#include "ServerFeatureConnection.h"
+#include "FdoForcedOneToOneFeatureReader.h"
+#include "ServerFeatureUtil.h"
+#include "FdoExpressionEngine.h"
+#include "Util/FdoExpressionEngineUtilDataReader.h"
+
MgSelectAggregateCommand::MgSelectAggregateCommand(MgResourceIdentifier* resource)
{
CHECKNULL((MgResourceIdentifier*)resource, L"MgSelectAggregateCommand.MgSelectAggregateCommand");
@@ -175,3 +180,161 @@
{
return FDO_SAFE_ADDREF(m_filter.p);
}
+
+FdoJoinCriteriaCollection* MgSelectAggregateCommand::GetJoinCriteria()
+{
+ CHECKNULL((FdoISelectAggregates*)m_command, L"MgSelectAggregateCommand.GetJoinCriteria");
+ return m_command->GetJoinCriteria();
+}
+
+void MgSelectAggregateCommand::SetAlias(FdoString* alias)
+{
+ CHECKARGUMENTNULL(alias, L"MgSelectAggregateCommand.SetAlias");
+ CHECKNULL((FdoISelectAggregates*)m_command, L"MgSelectAggregateCommand.SetAlias");
+ m_command->SetAlias(alias);
+}
+
+MgReader* MgSelectAggregateCommand::ExecuteJoined(MgStringCollection* idPropNames, bool bForceOneToOne)
+{
+ Ptr<MgReader> ret;
+
+ FdoPtr<FdoIConnection> fdoConn = m_connection->GetConnection();
+ FdoPtr<FdoIExtendedSelect> select = static_cast<FdoIExtendedSelect*>(fdoConn->CreateCommand(FdoCommandType_ExtendedSelect));
+
+ FdoPtr<FdoIdentifier> clsName = m_command->GetFeatureClassName();
+ select->SetFeatureClassName(clsName);
+
+
+ STRING clsNameStr = clsName->GetText();
+ STRING schemaName;
+ STRING className;
+ MgUtil::ParseQualifiedClassName(clsNameStr, schemaName, className);
+
+ FdoPtr<FdoIdentifierCollection> selectedIds = GetPropertyNames();
+ FdoPtr<FdoIdentifierCollection> computedIds = FdoIdentifierCollection::Create();
+ for (FdoInt32 i = 0; i < selectedIds->GetCount(); i++)
+ {
+ FdoPtr<FdoIdentifier> identifier = selectedIds->GetItem(i);
+ if (identifier->GetExpressionType() == FdoExpressionItemType_ComputedIdentifier)
+ {
+ FdoComputedIdentifier* comp = static_cast<FdoComputedIdentifier*>(identifier.p);
+ FdoPtr<FdoExpression> expr = comp->GetExpression();
+ if (expr->GetExpressionType() == FdoExpressionItemType_Function)
+ computedIds->Add(identifier);
+ }
+ }
+ FdoPtr<FdoClassDefinition> originalClassDef;
+
+ FdoPtr<FdoIDescribeSchema> descSchema = static_cast<FdoIDescribeSchema*>(fdoConn->CreateCommand(FdoCommandType_DescribeSchema));
+ if (!schemaName.empty())
+ {
+ descSchema->SetSchemaName(schemaName.c_str());
+ }
+ if (!className.empty())
+ {
+ FdoPtr<FdoStringCollection> classNames = FdoStringCollection::Create();
+ classNames->Add(className.c_str());
+ descSchema->SetClassNames(classNames);
+ }
+
+ FdoPtr<FdoClassDefinition> classDef;
+ FdoPtr<FdoFeatureSchemaCollection> schemas = descSchema->Execute();
+ for (FdoInt32 i = 0; i < schemas->GetCount(); i++)
+ {
+ FdoPtr<FdoFeatureSchema> schema = schemas->GetItem(i);
+ if (wcscmp(schema->GetName(), schemaName.c_str()) == 0)
+ {
+ FdoPtr<FdoClassCollection> classes = schema->GetClasses();
+ for (FdoInt32 j = 0; j < classes->GetCount(); j++)
+ {
+ FdoPtr<FdoClassDefinition> klassDef = classes->GetItem(j);
+ if (wcscmp(klassDef->GetName(), className.c_str()) == 0)
+ {
+ originalClassDef = SAFE_ADDREF(klassDef.p);
+ break;
+ }
+ }
+ }
+ }
+
+
+ if (NULL != (FdoFilter*)m_filter)
+ select->SetFilter(m_filter);
+
+ FdoCommonExpressionType exprType;
+ FdoPtr<FdoFunctionDefinitionCollection> functions = FdoExpressionEngine::GetStandardFunctions();
+ FdoPtr<FdoArray<FdoFunction*> > aggrIdents = FdoExpressionEngineUtilDataReader::GetAggregateFunctions(functions, computedIds, exprType);
+
+ FdoPtr<FdoIFeatureReader> reader;
+ FdoPtr<FdoIdentifierCollection> ids;
+ FdoPtr<FdoIdentifierCollection> orderBy = m_command->GetOrdering();
+ FdoOrderingOption orderOpt = m_command->GetOrderingOption();
+
+ //n00bism: String goes in, garbage comes out. Anyway, we know the alias we want to set
+ select->SetAlias(L"primary");
+ //select->SetAlias(m_command->GetAlias());
+
+ FdoPtr<FdoJoinCriteriaCollection> srcCriteria = GetJoinCriteria();
+ FdoPtr<FdoJoinCriteriaCollection> dstCriteria = select->GetJoinCriteria();
+ for (FdoInt32 i = 0; i < srcCriteria->GetCount(); i++)
+ {
+ FdoPtr<FdoJoinCriteria> criteria = srcCriteria->GetItem(i);
+ dstCriteria->Add(criteria);
+ }
+
+ if ((aggrIdents != NULL) && (aggrIdents->GetCount() > 0))
+ {
+ reader = select->Execute();
+ }
+ else
+ {
+ // transfer over the identifiers to the basic select command:
+ ids = select->GetPropertyNames();
+ ids->Clear();
+ if (0 == selectedIds->GetCount())
+ {
+ FdoPtr<FdoPropertyDefinitionCollection> propDefs = originalClassDef->GetProperties();
+ for (int i=0; i<propDefs->GetCount(); i++)
+ {
+ FdoPtr<FdoPropertyDefinition> propDef = propDefs->GetItem(i);
+ FdoPtr<FdoIdentifier> localId = FdoIdentifier::Create(propDef->GetName());
+ ids->Add(localId);
+ }
+ FdoPtr<FdoReadOnlyPropertyDefinitionCollection> basePropDefs = originalClassDef->GetBaseProperties();
+ for (int i=0; i<basePropDefs->GetCount(); i++)
+ {
+ FdoPtr<FdoPropertyDefinition> basePropDef = basePropDefs->GetItem(i);
+ FdoPtr<FdoIdentifier> localId = FdoIdentifier::Create(basePropDef->GetName());
+ ids->Add(localId);
+ }
+ }
+ else
+ {
+ for (int i=0; i<selectedIds->GetCount(); i++)
+ {
+ FdoPtr<FdoIdentifier> localId = selectedIds->GetItem(i);
+ ids->Add(localId);
+ }
+ }
+
+ // Execute the basic select command:
+ reader = select->Execute();
+ }
+
+ if (bForceOneToOne)
+ {
+ FdoPtr<FdoStringCollection> names = MgServerFeatureUtil::MgToFdoStringCollection(idPropNames, false);
+ FdoPtr<FdoIFeatureReader> forcedReader = new MgFdoForcedOneToOneFeatureReader(reader, names);
+ FdoPtr<FdoIDataReader> dataReader = new FdoExpressionEngineUtilDataReader(functions, forcedReader, originalClassDef, computedIds, GetDistinct(), orderBy, orderOpt, ids, aggrIdents);
+
+ ret = new MgServerDataReader(m_connection, dataReader, m_providerName);
+ }
+ else
+ {
+ FdoPtr<FdoIDataReader> dataReader = new FdoExpressionEngineUtilDataReader(functions, reader, originalClassDef, computedIds, GetDistinct(), orderBy, orderOpt, ids, aggrIdents);
+
+ ret = new MgServerDataReader(m_connection, dataReader, m_providerName);
+ }
+
+ return ret.Detach();
+}
\ No newline at end of file
Modified: trunk/MgDev/Server/src/Services/Feature/SelectAggregateCommand.h
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/SelectAggregateCommand.h 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/SelectAggregateCommand.h 2011-11-21 11:00:25 UTC (rev 6239)
@@ -49,6 +49,7 @@
virtual FdoFilter* GetFilter();
virtual MgReader* Execute();
+ MgReader* ExecuteJoined(MgStringCollection* idPropNames, bool bForceOneToOne);
virtual bool IsSupportedFunction(FdoFunction* fdoFunc);
virtual bool SupportsSelectGrouping();
virtual bool SupportsSelectOrdering();
@@ -59,6 +60,9 @@
delete this;
}
+ virtual FdoJoinCriteriaCollection* GetJoinCriteria();
+ virtual void SetAlias(FdoString* alias);
+
private:
Ptr<MgServerFeatureConnection> m_connection;
STRING m_providerName;
Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcproj
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcproj 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcproj 2011-11-21 11:00:25 UTC (rev 6239)
@@ -428,7 +428,7 @@
>
</File>
<File
- RelativePath=".\OpApplySchema.cpp"
+ RelativePath=".\OpAddSavePoint.cpp"
>
<FileConfiguration
Name="Debug|Win32"
@@ -464,11 +464,11 @@
</FileConfiguration>
</File>
<File
- RelativePath=".\OpApplySchema.h"
+ RelativePath=".\OpAddSavePoint.h"
>
- </File>
+ </File>
<File
- RelativePath=".\OpAddSavePoint.cpp"
+ RelativePath=".\OpApplySchema.cpp"
>
<FileConfiguration
Name="Debug|Win32"
@@ -504,7 +504,7 @@
</FileConfiguration>
</File>
<File
- RelativePath=".\OpAddSavePoint.h"
+ RelativePath=".\OpApplySchema.h"
>
</File>
<File
@@ -1666,7 +1666,7 @@
<File
RelativePath=".\OpReleaseSavePoint.h"
>
- </File>
+ </File>
<File
RelativePath=".\OpRollbackSavePoint.cpp"
>
@@ -2209,6 +2209,46 @@
>
</File>
<File
+ RelativePath=".\ExtendedSelectCommand.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\ExtendedSelectCommand.h"
+ >
+ </File>
+ <File
RelativePath=".\FdoFeatureReader.cpp"
>
<FileConfiguration
@@ -2289,6 +2329,46 @@
>
</File>
<File
+ RelativePath=".\FdoForcedOneToOneFeatureReader.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\FdoForcedOneToOneFeatureReader.h"
+ >
+ </File>
+ <File
RelativePath=".\FdoReaderCollection.cpp"
>
<FileConfiguration
Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp 2011-11-21 11:00:25 UTC (rev 6239)
@@ -70,6 +70,7 @@
#include "OpGetDataRows.cpp"
#include "SelectAggregateCommand.cpp"
#include "SelectCommand.cpp"
+#include "ExtendedSelectCommand.cpp"
#include "ServerDataReader.cpp"
#include "ServerSqlDataReaderPool.cpp"
#include "FeatureNumericFunctions.cpp"
@@ -100,6 +101,7 @@
#include "FdoReaderCollection.cpp"
#include "FdoFilterCollection.cpp"
#include "FdoFeatureReader.cpp"
+#include "FdoForcedOneToOneFeatureReader.cpp"
#include "ServerFeatureTransactionPool.cpp"
#include "ServerFeatureTransaction.cpp"
#include "OpBeginTransaction.cpp"
Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureUtil.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureUtil.cpp 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureUtil.cpp 2011-11-21 11:00:25 UTC (rev 6239)
@@ -3551,3 +3551,72 @@
MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgServerFeatureUtil.UpdateRasterPropertyDefinition")
}
+
+FdoIdentifierCollection* MgServerFeatureUtil::ExtractIdentifiers(FdoExpression* expr)
+{
+ CHECKNULL(expr, L"MgServerFeatureUtil.ExtractIdentifiers");
+
+ FdoPtr<FdoIdentifierCollection> ret;
+ MG_FEATURE_SERVICE_TRY()
+
+ ret = FdoIdentifierCollection::Create();
+
+ switch(expr->GetExpressionType())
+ {
+ case FdoExpressionItemType_ComputedIdentifier:
+ {
+ FdoComputedIdentifier* comp = static_cast<FdoComputedIdentifier*>(expr);
+ FdoPtr<FdoExpression> inner = comp->GetExpression();
+
+ FdoPtr<FdoIdentifierCollection> result = ExtractIdentifiers(inner);
+ for (FdoInt32 i = 0; i < result->GetCount(); i++)
+ {
+ FdoPtr<FdoIdentifier> resultItem = result->GetItem(i);
+ ret->Add(resultItem);
+ }
+ }
+ break;
+ case FdoExpressionItemType_Function:
+ {
+ FdoFunction* func = static_cast<FdoFunction*>(expr);
+ FdoExpressionCollection* funcArgs = func->GetArguments();
+ for (FdoInt32 i = 0; i < funcArgs->GetCount(); i++)
+ {
+ FdoPtr<FdoExpression> arg = funcArgs->GetItem(i);
+ FdoPtr<FdoIdentifierCollection> result = ExtractIdentifiers(arg);
+ for (FdoInt32 j = 0; j < result->GetCount(); j++)
+ {
+ FdoPtr<FdoIdentifier> resultItem = result->GetItem(j);
+ ret->Add(resultItem);
+ }
+ }
+ }
+ break;
+ case FdoExpressionItemType_Identifier:
+ ret->Add(static_cast<FdoIdentifier*>(expr));
+ break;
+ case FdoExpressionItemType_UnaryExpression:
+ {
+ FdoUnaryExpression* unexpr = static_cast<FdoUnaryExpression*>(expr);
+ FdoExpression* inner = unexpr->GetExpression();
+
+ FdoPtr<FdoIdentifierCollection> result = ExtractIdentifiers(inner);
+ for (FdoInt32 i = 0; i < result->GetCount(); i++)
+ {
+ FdoPtr<FdoIdentifier> resultItem = result->GetItem(i);
+ ret->Add(resultItem);
+ }
+ }
+ break;
+ case FdoExpressionItemType_SubSelectExpression:
+ {
+ FdoSubSelectExpression* subSelect = static_cast<FdoSubSelectExpression*>(expr);
+ FdoPtr<FdoIdentifier> propName = subSelect->GetPropertyName();
+ ret->Add(propName);
+ }
+ break;
+ }
+
+ MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgServerFeatureUtil.ExtractIdentifiers")
+ return ret.Detach();
+}
\ No newline at end of file
Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureUtil.h
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureUtil.h 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureUtil.h 2011-11-21 11:00:25 UTC (rev 6239)
@@ -118,6 +118,7 @@
static FdoObjectPropertyDefinition* GetObjectPropertyDefinition(MgObjectPropertyDefinition* objPropDef, FdoClassCollection* fdoClassCol);
static FdoParameterDirection GetFdoParameterDirection(INT32 paramDirection);
static INT32 GetMgParameterDirection(FdoParameterDirection fdoParamDirection);
+ static FdoIdentifierCollection* ExtractIdentifiers(FdoExpression* expr);
static void UpdateFdoFeatureSchema(MgFeatureSchema* mgSchema, FdoFeatureSchema* fdoSchema);
static void UpdateFdoClassCollection(MgClassDefinitionCollection* mgClassDefCol, FdoClassCollection* fdoClassCol);
Modified: trunk/MgDev/Server/src/Services/Feature/ServerSelectFeatures.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerSelectFeatures.cpp 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/ServerSelectFeatures.cpp 2011-11-21 11:00:25 UTC (rev 6239)
@@ -24,6 +24,7 @@
#include "ServerFeatureUtil.h"
#include "FeatureServiceCommand.h"
#include "SelectCommand.h"
+#include "ExtendedSelectCommand.h"
#include "SelectAggregateCommand.h"
#include "FeatureDistribution.h"
#include "ServerGwsFeatureReader.h"
@@ -35,6 +36,9 @@
#include "FdoExpressionEngineCopyFilter.h"
#include "CacheManager.h"
+//Uncomment for extra console chatter to debug FDO joins
+//#define DEBUG_FDO_JOIN
+
MgServerSelectFeatures::MgServerSelectFeatures()
{
m_command = NULL;
@@ -88,25 +92,58 @@
m_featureSourceCacheItem = cacheManager->GetFeatureSourceCacheItem(resource);
}
+ // Set options (NULL is a valid value)
+ m_options = SAFE_ADDREF(options);
// Check if a feature join is to be performed by inspecting the resource for join properties
bool bFeatureJoinProperties = FindFeatureJoinProperties(resource, className);
// Check if a feature join is only a calculation
bool bFeatureCalculation = FindFeatureCalculation(resource, className);
+ //Test for the FDO join optimization, because performance is **substantially** better
+ bool bSupportsFdoJoin = SupportsFdoJoin(resource, className, isSelectAggregate);
+#ifdef DEBUG_FDO_JOIN
+ STRING fsIdStr = resource->ToString();
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n(%t) Testing Feature Source (%W, %W) for FDO join optimization"), fsIdStr.c_str(), className.c_str()));
+#endif
if (!isSelectAggregate && bFeatureJoinProperties)
{
- // Get the FdoFilter from the options
- // Create Command
- CreateCommand(resource, isSelectAggregate);
- // Set options (NULL is a valid value)
- m_options = SAFE_ADDREF(options);
- // Apply options to FDO command
- ApplyQueryOptions(isSelectAggregate);
+ if (bSupportsFdoJoin)
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n(%t) Feature Source (%W) supports FDO join optimization"), fsIdStr.c_str()));
+#endif
+ m_command = MgFeatureServiceCommand::CreateCommand(resource, FdoCommandType_ExtendedSelect);
+ mgReader = SelectFdoJoin(resource, className, false);
+ }
+ else
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n(%t) Feature Source (%W) does not support the FDO join optimization. Using GwsQueryEngine"), fsIdStr.c_str()));
+#endif
+ // Get the FdoFilter from the options
+ // Create Command
+ CreateCommand(resource, isSelectAggregate);
- FdoPtr<FdoFilter> filter = m_command->GetFilter();
-
- // Perform feature join
- mgReader = JoinFeatures(resource, className, filter);
+ // Apply options to FDO command
+ ApplyQueryOptions(isSelectAggregate);
+ FdoPtr<FdoFilter> filter = m_command->GetFilter();
+ // Perform feature join
+ mgReader = JoinFeatures(resource, className, filter);
+ }
}
+ else if (isSelectAggregate && bFeatureJoinProperties && bSupportsFdoJoin)
+ {
+ // NOTE: If this is FDO join capable, but contains functions over prefixed secondary properties
+ // We have to go through the GWS Query Engine because we haven't implemented reverse mapping
+ // of prefixed secondary class properties. Fortunately, the common case for this is distict value
+ // queries (eg. Generating themeable values), and GWS Query Engine is surprisingly performant in this case
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n(%t) Feature Source (%W) supports aggregate FDO join optimization"), fsIdStr.c_str()));
+#endif
+ // Perform the same select query as above, but route this through the FDO expression engine
+ // Slow maybe, but anything is faster than going via the GWS query engine.
+ m_command = MgFeatureServiceCommand::CreateCommand(resource, FdoCommandType_ExtendedSelect);
+ mgReader = SelectFdoJoin(resource, className, true);
+ }
else
{
// Custom function specified from Select command is not allowed
@@ -140,14 +177,11 @@
m_command->SetFeatureClassName((FdoString*)className.c_str());
}
- // Set options (NULL is a valid value)
- m_options = SAFE_ADDREF(options);
-
// Apply options to FDO command
ApplyQueryOptions(isSelectAggregate);
// Check if the specified className is an extension (join) in the feature source
- if (bFeatureJoinProperties)
+ if (bFeatureJoinProperties && !bSupportsFdoJoin)
{
// Perform feature join to obtain the joined properties
// Note: this gwsFeatureReader is just for temporary use. it will not be returned
@@ -273,6 +307,7 @@
return mgReader.Detach();
}
+
void MgServerSelectFeatures::ApplyQueryOptions(bool isSelectAggregate)
{
CHECKNULL(m_command, L"MgServerSelectFeatures.ApplyQueryOptions");
@@ -304,6 +339,9 @@
if (cnt <= 0)
return; // Nothing to do
+ //TODO: Need to check if FDO join optimization is supported, and whether this contains prefixed
+ //secondary properties.
+
FdoPtr<FdoIdentifierCollection> fic = m_command->GetPropertyNames();
CHECKNULL((FdoIdentifierCollection*)fic, L"MgServerSelectFeatures.ApplyClassProperties");
@@ -334,6 +372,9 @@
if (cnt <= 0)
return; // Nothing to do
+ //TODO: Need to check if FDO join optimization is supported, and whether this contains prefixed
+ //secondary properties in any expressions
+
// TODO: Add support for custom functions
for (INT32 i=0; i < cnt; i++)
@@ -1490,3 +1531,669 @@
return secResId.Detach();
}
+
+bool MgServerSelectFeatures::SupportsFdoJoin(MgResourceIdentifier* featureSourceId, CREFSTRING extensionName, bool isAggregate)
+{
+ bool bSupported = false;
+
+ MG_FEATURE_SERVICE_TRY()
+
+ //This could be qualified, so parse it to be sure
+ STRING schemaName;
+ STRING extName;
+ MgUtil::ParseQualifiedClassName(extensionName, schemaName, extName);
+
+ CHECKNULL(m_featureSourceCacheItem.p, L"MgServerSelectFeatures.SupportsFdoJoin");
+ MdfModel::FeatureSource* featureSource = m_featureSourceCacheItem->Get();
+ MdfModel::ExtensionCollection* extensions = featureSource->GetExtensions();
+ CHECKNULL(extensions, L"MgServerSelectFeatures.SupportsFdoJoin");
+
+ MdfModel::Extension* extension = NULL;
+ for (INT32 i = 0; i < extensions->GetCount(); i++)
+ {
+ MdfModel::Extension* ext = extensions->GetAt(i);
+ if (ext->GetName() == extName)
+ {
+ extension = ext;
+ break;
+ }
+ }
+
+ if (NULL == extension) //Extension in question not found
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] Could not find extension named: %W"), extensionName.c_str()));
+#endif
+ return false;
+ }
+
+ Ptr<MgServerFeatureConnection> conn = new MgServerFeatureConnection(featureSourceId);
+ {
+ if (!conn->IsConnectionOpen())
+ {
+ throw new MgConnectionFailedException(L"MgServerSelectFeatures.SupportsFdoJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ FdoPtr<FdoIConnection> fdoConn = conn->GetConnection();
+ FdoPtr<FdoIConnectionCapabilities> connCaps = fdoConn->GetConnectionCapabilities();
+
+ MdfModel::AttributeRelateCollection* relates = extension->GetAttributeRelates();
+
+ //Get the easy checks out of the way
+ if (!connCaps->SupportsJoins())
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] The provider does not support the FDO join APIs")));
+#endif
+ return false;
+ }
+
+ FdoJoinType jtypes = (FdoJoinType)connCaps->GetJoinTypes();
+
+ //Now ascertain if all participating feature classes originate from the same feature source
+
+ //We've yet to figure out chained joins. TODO: Revisit later
+ if (relates->GetCount() > 1)
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] Chained/Multiple FDO Joins not supported yet")));
+#endif
+ return false;
+ }
+
+ //Need to check the filter here to see if it involves secondary class properties and/or
+ //expressions involving such properties. We don't support this yet
+ if (m_options != NULL)
+ {
+ MdfModel::AttributeRelate* relate = relates->GetAt(0);
+ STRING filterText = m_options->GetFilter();
+ STRING qualifiedName = relate->GetAttributeClass();
+ STRING schemaName;
+ STRING clsName;
+ MgUtil::ParseQualifiedClassName(qualifiedName, schemaName, clsName);
+ if (FilterContainsSecondaryProperties(featureSourceId, filterText, schemaName, clsName, relate->GetName()))
+ {
+ #ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] Filter contains secondary class properties")));
+ #endif
+ return false;
+ }
+ }
+
+ for (INT32 i = 0; i < relates->GetCount(); i++)
+ {
+ MdfModel::AttributeRelate* relate = relates->GetAt(i);
+ const MdfModel::MdfString& fsId = relate->GetResourceId();
+
+ //Different feature sources
+ if (featureSourceId->ToString() != fsId)
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] The extension does not join with another class from the same data store")));
+#endif
+ return false;
+ }
+
+ //TODO: Review when we lift the one join limit
+ if (NULL != (MgFeatureQueryOptions*)m_options)
+ {
+ //Is this costly? Should we cache it?
+ //FdoPtr<FdoFunctionDefinitionCollection> functions = FdoExpressionEngine::GetStandardFunctions();
+ Ptr<MgStringPropertyCollection> expressions = m_options->GetComputedProperties();
+ for (INT32 i = 0; i < expressions->GetCount(); i++)
+ {
+ Ptr<MgStringProperty> compProp = expressions->GetItem(i);
+ STRING exprText = compProp->GetValue();
+ FdoPtr<FdoExpression> fdoExpr = FdoExpression::Parse(exprText.c_str());
+
+ if (fdoExpr->GetExpressionType() == FdoExpressionItemType_Function)
+ {
+ FdoFunction* func = static_cast<FdoFunction*>(fdoExpr.p);
+ FdoString* funcName = func->GetName();
+
+ // We don't support functions on prefixed secondary properties (yet)
+ // so we have to check these functions to ensure they are only operating on the
+ // primary side properties
+ STRING primarySchemaName;
+ STRING primaryClassName;
+ MgUtil::ParseQualifiedClassName(extension->GetFeatureClass(), primarySchemaName, primaryClassName);
+ bool bValidFunction = IsFunctionOnPrimaryProperty(func, fdoConn, primarySchemaName, primaryClassName);
+ if (!bValidFunction)
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] FDO Function %W contains a non-primary or unrecognised identifier"), funcName));
+#endif
+ return false;
+ }
+ }
+ }
+ }
+
+ MdfModel::RelatePropertyCollection* relProps = relate->GetRelateProperties();
+
+ //Check if the join type is supported. Given FDO exposes more join types than
+ //the ones specified here, the chances are real good that we'll have a match
+ MdfModel::AttributeRelate::RelateType rtype = relate->GetRelateType();
+ switch(rtype)
+ {
+ case MdfModel::AttributeRelate::Inner:
+ if ((jtypes & FdoJoinType_Inner) != FdoJoinType_Inner)
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] The provider does not support Inner Join")));
+#endif
+ return false;
+ }
+ break;
+ case MdfModel::AttributeRelate::LeftOuter:
+ if ((jtypes & FdoJoinType_LeftOuter) != FdoJoinType_LeftOuter)
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] The provider does not support Left Outer Join")));
+#endif
+ return false;
+ }
+ break;
+ case MdfModel::AttributeRelate::RightOuter:
+ if ((jtypes & FdoJoinType_RightOuter) != FdoJoinType_RightOuter)
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] The provider does not support Right Outer Join")));
+#endif
+ return false;
+ }
+ break;
+ default:
+ {
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] The join type is not recognised")));
+#endif
+ return false;
+ }
+ }
+ }
+
+ //Still here? You pass the test
+ bSupported = true;
+ }
+
+ MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgServerSelectFeatures.SupportsFdoJoin")
+
+ return bSupported;
+}
+
+bool MgServerSelectFeatures::IsFunctionOnPrimaryProperty(FdoFunction* function, FdoIConnection* fdoConn, CREFSTRING schemaName, CREFSTRING className)
+{
+ FdoPtr<FdoIdentifierCollection> identifiers = MgServerFeatureUtil::ExtractIdentifiers(function);
+ if (identifiers->GetCount() == 0)
+ return true; //Inconsequential
+
+ FdoPtr<FdoIDescribeSchema> descSchema = dynamic_cast<FdoIDescribeSchema*>(fdoConn->CreateCommand(FdoCommandType_DescribeSchema));
+ CHECKNULL((FdoIDescribeSchema*)descSchema, L"MgServerSelectFeatures.SelectFdoJoin");
+
+ if (!schemaName.empty())
+ {
+ descSchema->SetSchemaName(schemaName.c_str());
+ }
+ if (!className.empty())
+ {
+ FdoPtr<FdoStringCollection> classNames = FdoStringCollection::Create();
+ classNames->Add(className.c_str());
+ descSchema->SetClassNames(classNames);
+ }
+
+ FdoPtr<FdoClassDefinition> classDef;
+ FdoPtr<FdoFeatureSchemaCollection> schemas = descSchema->Execute();
+ for (FdoInt32 i = 0; i < schemas->GetCount(); i++)
+ {
+ FdoPtr<FdoFeatureSchema> schema = schemas->GetItem(i);
+ if (wcscmp(schema->GetName(), schemaName.c_str()) == 0)
+ {
+ FdoPtr<FdoClassCollection> classes = schema->GetClasses();
+ for (FdoInt32 j = 0; j < classes->GetCount(); j++)
+ {
+ FdoPtr<FdoClassDefinition> klassDef = classes->GetItem(j);
+ if (wcscmp(klassDef->GetName(), className.c_str()) == 0)
+ {
+ classDef = SAFE_ADDREF(klassDef.p);
+ break;
+ }
+ }
+ }
+ }
+
+ if (NULL == (FdoClassDefinition*)classDef)
+ {
+ //TODO: Refine message if available
+ throw new MgClassNotFoundException(L"MgServerSelectFeatures.SelectFdoJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ FdoPtr<FdoPropertyDefinitionCollection> properties = classDef->GetProperties();
+ for (FdoInt32 i = 0; i < identifiers->GetCount(); i++)
+ {
+ FdoPtr<FdoIdentifier> identifier = identifiers->GetItem(i);
+ FdoString* name = identifier->GetName();
+ if (properties->IndexOf(name) < 0) //Not in primary class or not recognised
+ {
+#ifdef DEBUG_FDO_JOIN
+ FdoString* funcName = function->GetName();
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) [FDO Join Test] The aggregate function %W contains an unknown or non-primary identifier: %W"), funcName, name));
+#endif
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool MgServerSelectFeatures::FilterContainsSecondaryProperties(MgResourceIdentifier* featureSourceId, CREFSTRING filter, STRING secondarySchema, STRING secondaryClassName, STRING secondaryPrefix)
+{
+ if (filter.empty())
+ return false;
+
+ //TODO: There's probably a more efficient way to do this without needing to fetch the secondary
+ //class definition. But we're aiming for functionality and simplicity first.
+ Ptr<MgServerFeatureConnection> conn = new MgServerFeatureConnection(featureSourceId);
+ {
+ if (!conn->IsConnectionOpen())
+ {
+ throw new MgConnectionFailedException(L"MgServerSelectFeatures.SupportsFdoJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ FdoPtr<FdoIConnection> fdoConn = conn->GetConnection();
+
+ FdoPtr<FdoIDescribeSchema> descSchema = dynamic_cast<FdoIDescribeSchema*>(fdoConn->CreateCommand(FdoCommandType_DescribeSchema));
+ CHECKNULL((FdoIDescribeSchema*)descSchema, L"MgServerSelectFeatures.SelectFdoJoin");
+
+ if (!secondarySchema.empty())
+ {
+ descSchema->SetSchemaName(secondarySchema.c_str());
+ }
+ if (!secondaryClassName.empty())
+ {
+ FdoPtr<FdoStringCollection> classNames = FdoStringCollection::Create();
+ classNames->Add(secondaryClassName.c_str());
+ descSchema->SetClassNames(classNames);
+ }
+
+ FdoPtr<FdoClassDefinition> classDef;
+ FdoPtr<FdoFeatureSchemaCollection> schemas = descSchema->Execute();
+ for (FdoInt32 i = 0; i < schemas->GetCount(); i++)
+ {
+ FdoPtr<FdoFeatureSchema> schema = schemas->GetItem(i);
+ if (wcscmp(schema->GetName(), secondarySchema.c_str()) == 0)
+ {
+ FdoPtr<FdoClassCollection> classes = schema->GetClasses();
+ for (FdoInt32 j = 0; j < classes->GetCount(); j++)
+ {
+ FdoPtr<FdoClassDefinition> klassDef = classes->GetItem(j);
+ if (wcscmp(klassDef->GetName(), secondaryClassName.c_str()) == 0)
+ {
+ classDef = SAFE_ADDREF(klassDef.p);
+ break;
+ }
+ }
+ }
+ }
+
+ if (NULL == (FdoClassDefinition*)classDef)
+ {
+ //TODO: Refine message if available
+ throw new MgClassNotFoundException(L"MgServerSelectFeatures.SelectFdoJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ FdoPtr<FdoPropertyDefinitionCollection> propDefs = classDef->GetProperties();
+ for (INT32 i = 0; i < propDefs->GetCount(); i++)
+ {
+ FdoPtr<FdoPropertyDefinition> propDef = propDefs->GetItem(i);
+ STRING findStr = secondaryPrefix;
+ findStr += propDef->GetName();
+
+ if (filter.find(findStr) != STRING::npos)
+ return true; //The filter string contains this extended feature class property
+ }
+ }
+ return false;
+}
+
+MgReader* MgServerSelectFeatures::SelectFdoJoin(MgResourceIdentifier* featureSourceId, CREFSTRING extensionName, bool isAggregate)
+{
+ // TODO: This does not handle filters on the secondary side (yet)
+ // Can GwsQueryEngine do this?
+
+ Ptr<MgReader> ret;
+
+ MG_FEATURE_SERVICE_TRY()
+
+ //This could be qualified, so parse it to be sure
+ STRING schemaName;
+ STRING extName;
+ MgUtil::ParseQualifiedClassName(extensionName, schemaName, extName);
+
+ CHECKNULL(m_featureSourceCacheItem.p, L"MgServerSelectFeatures.SelectFdoJoin");
+ MdfModel::FeatureSource* featureSource = m_featureSourceCacheItem->Get();
+ MdfModel::ExtensionCollection* extensions = featureSource->GetExtensions();
+ CHECKNULL(extensions, L"MgServerSelectFeatures.SelectFdoJoin");
+
+ MdfModel::Extension* extension = NULL;
+ for (INT32 i = 0; i < extensions->GetCount(); i++)
+ {
+ MdfModel::Extension* ext = extensions->GetAt(i);
+ if (ext->GetName() == extName)
+ {
+ extension = ext;
+ break;
+ }
+ }
+
+ CHECKNULL(extension, L"MgServerSelectFeatures.SelectFdoJoin");
+ m_command->SetFeatureClassName(extension->GetFeatureClass().c_str());
+ MdfModel::AttributeRelateCollection* relates = extension->GetAttributeRelates();
+ CHECKNULL(relates, L"MgServerSelectFeatures.SelectFdoJoin");
+ MdfModel::AttributeRelate* relate = relates->GetAt(0);
+
+ const MdfModel::MdfString& prefix = relate->GetName();
+
+ STRING primaryAlias = L"primary";
+ STRING secondaryAlias = L"secondary";
+
+ FdoPtr<FdoJoinCriteriaCollection> joinCriteria;
+ if (isAggregate)
+ {
+ MgSelectAggregateCommand* cmd = static_cast<MgSelectAggregateCommand*>(m_command.p);
+ cmd->SetAlias(primaryAlias.c_str());
+ joinCriteria = cmd->GetJoinCriteria();
+ }
+ else
+ {
+ MgExtendedSelectCommand* cmd = static_cast<MgExtendedSelectCommand*>(m_command.p);
+ cmd->SetAlias(primaryAlias.c_str());
+ joinCriteria = cmd->GetJoinCriteria();
+ }
+
+ Ptr<MgStringCollection> idPropNames = new MgStringCollection();
+ Ptr<MgServerFeatureConnection> conn = new MgServerFeatureConnection(featureSourceId);
+ {
+ if (!conn->IsConnectionOpen())
+ {
+ throw new MgConnectionFailedException(L"MgServerSelectFeatures.SelectFdoJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ CHECKNULL(m_command, L"MgServerSelectFeatures.SelectFdoJoin");
+ FdoPtr<FdoIConnection> fdoConn = conn->GetConnection();
+
+ bool bAppliedProperties = false;
+ if (m_options != NULL)
+ {
+ //ApplyClassProperties();
+ ApplyComputedProperties();
+ // TODO: We need to find out if there are any filters involving the secondary side
+ ApplyFilter();
+ // ApplySpatialFilter();
+ ApplyOrderingOptions();
+ // We don't apply aggregate options here because these go through the FDO Expression Engine
+ ApplyAggregateOptions(isAggregate);
+ ApplyFetchSize();
+
+ //If an explicit list is specified, we assume the caller knows about prefixed
+ //extended feature class properties.
+ Ptr<MgStringCollection> props = m_options->GetClassProperties();
+ if (props->GetCount() > 0)
+ {
+ ApplyClassProperties();
+ bAppliedProperties = true;
+ }
+ }
+
+ //We need to fetch full class definitions of primary and secondary classes
+ if (!bAppliedProperties)
+ {
+ //Add primary class properties
+ STRING primaryClass = extension->GetFeatureClass();
+ STRING primarySchemaName;
+ STRING primaryClassName;
+ MgUtil::ParseQualifiedClassName(primaryClass, primarySchemaName, primaryClassName);
+
+ ApplyClassProperties(fdoConn, primarySchemaName, primaryClassName, idPropNames, primaryAlias);
+
+ if (!isAggregate)
+ {
+ //Add secondary class properties
+ STRING joinClass = relate->GetAttributeClass();
+ STRING joinSchemaName;
+ STRING joinClassName;
+ MgUtil::ParseQualifiedClassName(joinClass, joinSchemaName, joinClassName);
+
+ ApplyClassProperties(fdoConn, joinSchemaName, joinClassName, NULL, secondaryAlias, prefix);
+ }
+ }
+ }
+
+ //Set join type
+ FdoJoinType jtype = FdoJoinType_None;
+ switch(relate->GetRelateType())
+ {
+ case MdfModel::AttributeRelate::Inner:
+ jtype = FdoJoinType_Inner;
+ break;
+ case MdfModel::AttributeRelate::LeftOuter:
+ jtype = FdoJoinType_LeftOuter;
+ break;
+ case MdfModel::AttributeRelate::RightOuter:
+ jtype = FdoJoinType_RightOuter;
+ break;
+ }
+
+ bool bForceOneToOne = relate->GetForceOneToOne();
+ //Set filter for this join
+ STRING secondaryClass = relate->GetAttributeClass();
+ STRING filterText;
+ MdfModel::RelatePropertyCollection* relProps = relate->GetRelateProperties();
+ for (INT32 i = 0; i < relProps->GetCount(); i++)
+ {
+ MdfModel::RelateProperty* prop = relProps->GetAt(i);
+ if (!filterText.empty())
+ {
+ filterText += L" AND ";
+ }
+
+ //[primaryAlias].[PropertyName] = [secondaryAlias].[PropertyName]
+ filterText += primaryAlias;
+ filterText += L".";
+ filterText += prop->GetFeatureClassProperty();
+ filterText += L" = ";
+ filterText += secondaryAlias;
+ filterText += L".";
+ filterText += prop->GetAttributeClassProperty();
+ }
+
+ FdoPtr<FdoJoinCriteria> criteria;
+ FdoPtr<FdoIdentifier> idJoinClass = FdoIdentifier::Create(secondaryClass.c_str());
+ FdoPtr<FdoFilter> filter = FdoFilter::Parse(filterText.c_str());
+ if (prefix.empty())
+ criteria = FdoJoinCriteria::Create(idJoinClass, jtype, filter);
+ else
+ criteria = FdoJoinCriteria::Create(secondaryAlias.c_str(), idJoinClass, jtype, filter);
+
+ joinCriteria->Add(criteria);
+
+ if (isAggregate)
+ ret = ((MgSelectAggregateCommand*)m_command.p)->ExecuteJoined(idPropNames, bForceOneToOne);
+ else
+ ret = ((MgExtendedSelectCommand*)m_command.p)->ExecuteJoined(idPropNames, bForceOneToOne);
+
+ MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgServerSelectFeatures.SelectFdoJoin")
+
+ return ret.Detach();
+}
+
+void MgServerSelectFeatures::ApplyAggregateCommandJoinFilterAndCriteria(MgResourceIdentifier* featureSourceId, CREFSTRING extensionName)
+{
+#ifdef DEBUG_FDO_JOIN
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\n\t(%t) Applying FDO join criteria and filter to aggregate command")));
+#endif
+
+ //This could be qualified, so parse it to be sure
+ STRING schemaName;
+ STRING extName;
+ MgUtil::ParseQualifiedClassName(extensionName, schemaName, extName);
+
+ CHECKNULL(m_featureSourceCacheItem.p, L"MgServerSelectFeatures.SupportsFdoJoin");
+ MdfModel::FeatureSource* featureSource = m_featureSourceCacheItem->Get();
+ MdfModel::ExtensionCollection* extensions = featureSource->GetExtensions();
+ CHECKNULL(extensions, L"MgServerSelectFeatures.SupportsFdoJoin");
+
+ MdfModel::Extension* extension = NULL;
+ for (INT32 i = 0; i < extensions->GetCount(); i++)
+ {
+ MdfModel::Extension* ext = extensions->GetAt(i);
+ if (ext->GetName() == extName)
+ {
+ extension = ext;
+ break;
+ }
+ }
+
+ CHECKNULL(extension, L"MgServerSelectFeatures.SelectFdoJoin");
+ m_command->SetFeatureClassName(extension->GetFeatureClass().c_str());
+ MdfModel::AttributeRelateCollection* relates = extension->GetAttributeRelates();
+ CHECKNULL(relates, L"MgServerSelectFeatures.SelectFdoJoin");
+ MdfModel::AttributeRelate* relate = relates->GetAt(0);
+
+ const MdfModel::MdfString& prefix = relate->GetName();
+
+ STRING primaryAlias = L"primary";
+ STRING secondaryAlias = L"secondary";
+
+ MgSelectAggregateCommand* extSelect = static_cast<MgSelectAggregateCommand*>(m_command.p);
+ extSelect->SetAlias(primaryAlias.c_str());
+
+ FdoPtr<FdoJoinCriteriaCollection> joinCriteria = extSelect->GetJoinCriteria();
+
+ //Set join type
+ FdoJoinType jtype = FdoJoinType_None;
+ switch(relate->GetRelateType())
+ {
+ case MdfModel::AttributeRelate::Inner:
+ jtype = FdoJoinType_Inner;
+ break;
+ case MdfModel::AttributeRelate::LeftOuter:
+ jtype = FdoJoinType_LeftOuter;
+ break;
+ case MdfModel::AttributeRelate::RightOuter:
+ jtype = FdoJoinType_RightOuter;
+ break;
+ }
+
+ bool bForceOneToOne = relate->GetForceOneToOne();
+ //Set filter for this join
+ STRING secondaryClass = relate->GetAttributeClass();
+ STRING filterText;
+ MdfModel::RelatePropertyCollection* relProps = relate->GetRelateProperties();
+ for (INT32 i = 0; i < relProps->GetCount(); i++)
+ {
+ MdfModel::RelateProperty* prop = relProps->GetAt(i);
+ if (!filterText.empty())
+ {
+ filterText += L" AND ";
+ }
+
+ //[primaryAlias].[PropertyName] = [secondaryAlias].[PropertyName]
+ filterText += primaryAlias;
+ filterText += L".";
+ filterText += prop->GetFeatureClassProperty();
+ filterText += L" = ";
+ filterText += secondaryAlias;
+ filterText += L".";
+ filterText += prop->GetAttributeClassProperty();
+ }
+
+ FdoPtr<FdoJoinCriteria> criteria;
+ FdoPtr<FdoIdentifier> idJoinClass = FdoIdentifier::Create(secondaryClass.c_str());
+ FdoPtr<FdoFilter> filter = FdoFilter::Parse(filterText.c_str());
+ if (prefix.empty())
+ criteria = FdoJoinCriteria::Create(idJoinClass, jtype, filter);
+ else
+ criteria = FdoJoinCriteria::Create(secondaryAlias.c_str(), idJoinClass, jtype, filter);
+
+ joinCriteria->Add(criteria);
+}
+
+void MgServerSelectFeatures::ApplyClassProperties(FdoIConnection* fdoConn, CREFSTRING schemaName, CREFSTRING className, MgStringCollection* idPropNames, CREFSTRING alias, CREFSTRING prefix)
+{
+ FdoPtr<FdoIDescribeSchema> descSchema = dynamic_cast<FdoIDescribeSchema*>(fdoConn->CreateCommand(FdoCommandType_DescribeSchema));
+ CHECKNULL((FdoIDescribeSchema*)descSchema, L"MgServerSelectFeatures.SelectFdoJoin");
+
+ if (!schemaName.empty())
+ {
+ descSchema->SetSchemaName(schemaName.c_str());
+ }
+ if (!className.empty())
+ {
+ FdoPtr<FdoStringCollection> classNames = FdoStringCollection::Create();
+ classNames->Add(className.c_str());
+ descSchema->SetClassNames(classNames);
+ }
+
+ FdoPtr<FdoClassDefinition> classDef;
+ FdoPtr<FdoFeatureSchemaCollection> schemas = descSchema->Execute();
+ for (FdoInt32 i = 0; i < schemas->GetCount(); i++)
+ {
+ FdoPtr<FdoFeatureSchema> schema = schemas->GetItem(i);
+ if (wcscmp(schema->GetName(), schemaName.c_str()) == 0)
+ {
+ FdoPtr<FdoClassCollection> classes = schema->GetClasses();
+ for (FdoInt32 j = 0; j < classes->GetCount(); j++)
+ {
+ FdoPtr<FdoClassDefinition> klassDef = classes->GetItem(j);
+ if (wcscmp(klassDef->GetName(), className.c_str()) == 0)
+ {
+ classDef = SAFE_ADDREF(klassDef.p);
+ break;
+ }
+ }
+ }
+ }
+
+ if (NULL == (FdoClassDefinition*)classDef)
+ {
+ //TODO: Refine message if available
+ throw new MgClassNotFoundException(L"MgServerSelectFeatures.SelectFdoJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ FdoPtr<FdoIdentifierCollection> propNames = m_command->GetPropertyNames();
+
+ //Add primary class properties
+ FdoPtr<FdoPropertyDefinitionCollection> propDefs = classDef->GetProperties();
+ for (FdoInt32 i = 0; i < propDefs->GetCount(); i++)
+ {
+ FdoPtr<FdoPropertyDefinition> propDef = propDefs->GetItem(i);
+
+ //Skip ones that aren't data/geometry
+ if (propDef->GetPropertyType() != FdoPropertyType_DataProperty &&
+ propDef->GetPropertyType() != FdoPropertyType_GeometricProperty)
+ continue;
+
+ STRING exprText = alias + L".";
+ exprText += propDef->GetName();
+
+ FdoPtr<FdoExpression> expr = FdoExpression::Parse(exprText.c_str());
+ STRING idName = prefix + propDef->GetName();
+ //[alias].[propertyName] AS [prefix][propertyName]
+ FdoPtr<FdoComputedIdentifier> compId = FdoComputedIdentifier::Create(idName.c_str(), expr);
+
+ propNames->Add(compId);
+ }
+
+ if (NULL != idPropNames)
+ {
+ FdoPtr<FdoDataPropertyDefinitionCollection> idPropDefs = classDef->GetIdentityProperties();
+ for (FdoInt32 i = 0; i < idPropDefs->GetCount(); i++)
+ {
+ FdoPtr<FdoDataPropertyDefinition> dp = idPropDefs->GetItem(i);
+ STRING propName = L"";
+ propName += dp->GetName();
+ idPropNames->Add(propName);
+ }
+ }
+}
\ No newline at end of file
Modified: trunk/MgDev/Server/src/Services/Feature/ServerSelectFeatures.h
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerSelectFeatures.h 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/Services/Feature/ServerSelectFeatures.h 2011-11-21 11:00:25 UTC (rev 6239)
@@ -91,6 +91,14 @@
INT32 m_nJoinQueryBatchSize;
INT32 m_nDataCacheSize;
+
+ // FDO join optimization
+ bool IsFunctionOnPrimaryProperty(FdoFunction* function, FdoIConnection* conn, CREFSTRING schemaName, CREFSTRING className);
+ bool SupportsFdoJoin(MgResourceIdentifier* featureSourceId, CREFSTRING extension, bool isAggregate);
+ MgReader* SelectFdoJoin(MgResourceIdentifier* featureSourceId, CREFSTRING extension, bool isAggregate);
+ void ApplyAggregateCommandJoinFilterAndCriteria(MgResourceIdentifier* featureSourceId, CREFSTRING extension);
+ void ApplyClassProperties(FdoIConnection* fdoConn, CREFSTRING schemaName, CREFSTRING className, MgStringCollection* idPropNames, CREFSTRING alias, CREFSTRING prefix = L"");
+ bool FilterContainsSecondaryProperties(MgResourceIdentifier* featureSourceId, CREFSTRING filter, STRING secondarySchema, STRING secondaryClassName, STRING secondaryPrefix);
};
#endif
Modified: trunk/MgDev/Server/src/UnitTesting/TestFeatureService.cpp
===================================================================
--- trunk/MgDev/Server/src/UnitTesting/TestFeatureService.cpp 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/UnitTesting/TestFeatureService.cpp 2011-11-21 11:00:25 UTC (rev 6239)
@@ -82,14 +82,18 @@
MgResourceIdentifier resourceIdentifier5(L"Library://UnitTests/Data/TestChainedInner1ToManyJoin.FeatureSource");
MgResourceIdentifier resourceIdentifier6(L"Library://UnitTests/Data/Empty.FeatureSource");
MgResourceIdentifier resourceIdentifier7(L"Library://UnitTests/Data/SavePointTest.FeatureSource");
+ MgResourceIdentifier resourceIdentifier8(L"Library://UnitTests/Data/ParcelsJoinTestSQLite.FeatureSource");
+ MgResourceIdentifier resourceIdentifier9(L"Library://UnitTests/Data/FdoJoin.FeatureSource");
#ifdef _WIN32
STRING resourceContentFileName1 = L"..\\UnitTestFiles\\Sheboygan_Parcels.FeatureSource";
STRING resourceContentFileName2 = L"..\\UnitTestFiles\\Redding_Parcels.FeatureSource";
STRING resourceContentFileName3 = L"..\\UnitTestFiles\\Sheboygan_BuildingOutlines.FeatureSource";
STRING resourceContentFileName4 = L"..\\UnitTestFiles\\Sheboygan_VotingDistricts.FeatureSource";
- STRING resourceContentFileName5= L"..\\UnitTestFiles\\TESTChainedInner1ToManyJoin.FeatureSource";
- STRING resourceContentFileName6= L"..\\UnitTestFiles\\Empty.FeatureSource";
- STRING resourceContentFileName7= L"..\\UnitTestFiles\\SavePointTest.FeatureSource";
+ STRING resourceContentFileName5 = L"..\\UnitTestFiles\\TESTChainedInner1ToManyJoin.FeatureSource";
+ STRING resourceContentFileName6 = L"..\\UnitTestFiles\\Empty.FeatureSource";
+ STRING resourceContentFileName7 = L"..\\UnitTestFiles\\SavePointTest.FeatureSource";
+ STRING resourceContentFileName8 = L"..\\UnitTestFiles\\UT_Parcels_SQLite_Join.FeatureSource";
+ STRING resourceContentFileName9 = L"..\\UnitTestFiles\\UT_FdoJoin.FeatureSource";
STRING dataFileName1 = L"..\\UnitTestFiles\\Sheboygan_Parcels.sdf";
STRING dataFileName2 = L"..\\UnitTestFiles\\Redding_Parcels.shp";
STRING dataFileName3 = L"..\\UnitTestFiles\\Redding_Parcels.dbf";
@@ -98,6 +102,8 @@
STRING dataFileName6 = L"..\\UnitTestFiles\\Sheboygan_VotingDistricts.sdf";
STRING dataFileName7 = L"..\\UnitTestFiles\\Empty.sdf";
STRING dataFileName8 = L"..\\UnitTestFiles\\SavePointTest.sqlite";
+ STRING dataFileName9 = L"..\\UnitTestFiles\\JoinTest.sqlite";
+ STRING dataFileName10 = L"..\\UnitTestFiles\\ParcelsJoinTest.sqlite";
#else
STRING resourceContentFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.FeatureSource";
STRING resourceContentFileName2 = L"../UnitTestFiles/Redding_Parcels.FeatureSource";
@@ -106,6 +112,8 @@
STRING resourceContentFileName5 = L"../UnitTestFiles/TESTChainedInner1ToManyJoin.FeatureSource";
STRING resourceContentFileName6 = L"../UnitTestFiles/Empty.FeatureSource";
STRING resourceContentFileName7 = L"../UnitTestFiles/SavePointTest.FeatureSource";
+ STRING resourceContentFileName8 = L"../UnitTestFiles/UT_Parcels_SQLite_Join.FeatureSource";
+ STRING resourceContentFileName9 = L"../UnitTestFiles/UT_FdoJoin.FeatureSource";
STRING dataFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.sdf";
STRING dataFileName2 = L"../UnitTestFiles/Redding_Parcels.shp";
STRING dataFileName3 = L"../UnitTestFiles/Redding_Parcels.dbf";
@@ -113,7 +121,9 @@
STRING dataFileName5 = L"../UnitTestFiles/Sheboygan_BuildingOutlines.sdf";
STRING dataFileName6 = L"../UnitTestFiles/Sheboygan_VotingDistricts.sdf";
STRING dataFileName7 = L"../UnitTestFiles/Empty.sdf";
- STRING dataFileName8 = L"../UnitTestFiles/SavePointTest.sqlite";
+ STRING dataFileName8 = L"../UnitTestFiles/SavePointTest.sqlite";
+ STRING dataFileName9 = L"../UnitTestFiles/JoinTest.sqlite";
+ STRING dataFileName10 = L"../UnitTestFiles/ParcelsJoinTest.sqlite";
#endif
//Add a new resource
@@ -145,6 +155,14 @@
Ptr<MgByteReader> contentReader7 = contentSource7->GetReader();
pService->SetResource(&resourceIdentifier7, contentReader7, NULL);
+ Ptr<MgByteSource> contentSource8 = new MgByteSource(resourceContentFileName8);
+ Ptr<MgByteReader> contentReader8 = contentSource8->GetReader();
+ pService->SetResource(&resourceIdentifier8, contentReader8, NULL);
+
+ Ptr<MgByteSource> contentSource9 = new MgByteSource(resourceContentFileName9);
+ Ptr<MgByteReader> contentReader9 = contentSource9->GetReader();
+ pService->SetResource(&resourceIdentifier9, contentReader9, NULL);
+
//Set the resource data
Ptr<MgByteSource> dataSource1 = new MgByteSource(dataFileName1);
Ptr<MgByteReader> dataReader1 = dataSource1->GetReader();
@@ -181,6 +199,14 @@
Ptr<MgByteSource> dataSource9 = new MgByteSource(dataFileName8);
Ptr<MgByteReader> dataReader9 = dataSource9->GetReader();
pService->SetResourceData(&resourceIdentifier7, L"SavePointTest.sqlite", L"File", dataReader9);
+
+ Ptr<MgByteSource> dataSource10 = new MgByteSource(dataFileName9);
+ Ptr<MgByteReader> dataReader10 = dataSource10->GetReader();
+ pService->SetResourceData(&resourceIdentifier9, L"JoinTest.sqlite", L"File", dataReader10);
+
+ Ptr<MgByteSource> dataSource11 = new MgByteSource(dataFileName10);
+ Ptr<MgByteReader> dataReader11 = dataSource11->GetReader();
+ pService->SetResourceData(&resourceIdentifier8, L"ParcelsJoinTest.sqlite", L"File", dataReader11);
}
}
catch(MgException* e)
@@ -234,6 +260,12 @@
Ptr<MgResourceIdentifier> fsres6 = new MgResourceIdentifier(L"Library://UnitTests/Data/Empty.FeatureSource");
pService->DeleteResource(fsres6);
+ Ptr<MgResourceIdentifier> fsres7 = new MgResourceIdentifier(L"Library://UnitTests/Data/FdoJoin.FeatureSource");
+ pService->DeleteResource(fsres7);
+
+ Ptr<MgResourceIdentifier> fsres8 = new MgResourceIdentifier(L"Library://UnitTests/Data/ParcelsJoinTestSQLite.FeatureSource");
+ pService->DeleteResource(fsres8);
+
#ifdef _DEBUG
ACE_DEBUG((LM_INFO, ACE_TEXT("TestFeatureService::TestEnd()\n")));
MgFdoConnectionManager* pFdoConnectionManager = MgFdoConnectionManager::GetInstance();
@@ -2354,3 +2386,300 @@
throw;
}
}
+
+///----------------------------------------------------------------------------
+/// Test Case Description:
+///
+/// This test case exercises the FDO join optimization
+///----------------------------------------------------------------------------
+void TestFeatureService::TestCase_JoinFdoFeatures()
+{
+ try
+ {
+
+ MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+ if(serviceManager == 0)
+ {
+ throw new MgNullReferenceException(L"TestFeatureService.TestCase_JoinFdoFeatures", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ Ptr<MgFeatureService> pService = dynamic_cast<MgFeatureService*>(serviceManager->RequestService(MgServiceType::FeatureService));
+ if (pService == 0)
+ {
+ throw new MgServiceNotAvailableException(L"TestFeatureService.TestCase_JoinFdoFeatures", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ Ptr<MgResourceIdentifier> lFeatureSource = new MgResourceIdentifier(L"Library://UnitTests/Data/FdoJoin.FeatureSource");
+
+ Ptr<MgFeatureReader> reader = pService->SelectFeatures(lFeatureSource, L"CitiesCountries", NULL);
+
+ INT32 count1 = 0;
+ while(reader->ReadNext())
+ {
+ CPPUNIT_ASSERT(reader->IsNull(L"ID") == false);
+ CPPUNIT_ASSERT(reader->IsNull(L"CountryCode") == false);
+ CPPUNIT_ASSERT(reader->IsNull(L"StateCode") == false);
+ CPPUNIT_ASSERT(reader->IsNull(L"Name") == false);
+ CPPUNIT_ASSERT(reader->IsNull(L"Population") == false);
+ CPPUNIT_ASSERT(reader->IsNull(L"CNT_CountryCode") == false);
+ CPPUNIT_ASSERT(reader->IsNull(L"CNT_Name") == false);
+ count1++;
+ }
+ reader->Close();
+ CPPUNIT_ASSERT(10 == count1);
+
+ Ptr<MgFeatureReader> reader2 = pService->SelectFeatures(lFeatureSource, L"CitiesStates", NULL);
+
+ INT32 count2 = 0;
+ while(reader2->ReadNext())
+ {
+ CPPUNIT_ASSERT(reader2->IsNull(L"ID") == false);
+ CPPUNIT_ASSERT(reader2->IsNull(L"CountryCode") == false);
+ CPPUNIT_ASSERT(reader2->IsNull(L"StateCode") == false);
+ CPPUNIT_ASSERT(reader2->IsNull(L"Name") == false);
+ CPPUNIT_ASSERT(reader2->IsNull(L"Population") == false);
+ CPPUNIT_ASSERT(reader2->IsNull(L"ST_ID") == false);
+ CPPUNIT_ASSERT(reader2->IsNull(L"ST_CountryCode") == false);
+ CPPUNIT_ASSERT(reader2->IsNull(L"ST_StateCode") == false);
+ CPPUNIT_ASSERT(reader2->IsNull(L"ST_Name") == false);
+ count2++;
+ }
+ reader2->Close();
+ CPPUNIT_ASSERT(10 == count2);
+
+ Ptr<MgFeatureReader> reader3 = pService->SelectFeatures(lFeatureSource, L"CitiesStatesOneToOne", NULL);
+ INT32 count3 = 0;
+ while(reader3->ReadNext())
+ {
+ count3++;
+ }
+ reader3->Close();
+ CPPUNIT_ASSERT(10 == count3);
+ }
+ catch(MgException* e)
+ {
+ STRING message = e->GetDetails(TEST_LOCALE);
+ SAFE_RELEASE(e);
+ CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+ }
+ catch(FdoException* e)
+ {
+ STRING message = L"FdoException occurred: ";
+ message += e->GetExceptionMessage();
+ FDO_SAFE_RELEASE(e);
+ CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+ }
+ catch(...)
+ {
+ throw;
+ }
+}
+
+void TestFeatureService::TestCase_BenchmarkSqliteJoin()
+{
+ try
+ {
+ MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+ if(serviceManager == 0)
+ {
+ throw new MgNullReferenceException(L"TestFeatureService.TestCase_BenchmarkSqliteJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ Ptr<MgResourceService> pService = dynamic_cast<MgResourceService*>(serviceManager->RequestService(MgServiceType::ResourceService));
+ if (pService == 0)
+ {
+ throw new MgServiceNotAvailableException(L"TestFeatureService.TestCase_BenchmarkSqliteJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ Ptr<MgFeatureService> featSvc = dynamic_cast<MgFeatureService*>(serviceManager->RequestService(MgServiceType::FeatureService));
+ if (featSvc == 0)
+ {
+ throw new MgServiceNotAvailableException(L"TestFeatureService.TestCase_BenchmarkSqliteJoin",
+ __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ Ptr<MgResourceIdentifier> fsId = new MgResourceIdentifier(L"Library://UnitTests/Data/ParcelsJoinTestSQLite.FeatureSource");
+ CPPUNIT_ASSERT(featSvc->TestConnection(fsId));
+ Ptr<MgFeatureReader> reader;
+
+ // ----- Start the tests ------- //
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestFeatureService::TestCase_BenchmarkSqliteJoin() - Inner Join \n")));
+ long lStart = GetTickCount();
+ long total = 0L;
+
+ reader = featSvc->SelectFeatures(fsId, L"ParcelsInner", NULL);
+ while(reader->ReadNext())
+ {
+ total++;
+ }
+ reader->Close();
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT(" Execution Time (%d results): = %6.4f (s)\n"), total, ((GetTickCount()-lStart)/1000.0) ));
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestFeatureService::TestCase_BenchmarkSqliteJoin() - Left Outer Join \n")));
+ lStart = GetTickCount();
+ total = 0L;
+
+ reader = featSvc->SelectFeatures(fsId, L"ParcelsLeftOuter", NULL);
+ while(reader->ReadNext())
+ {
+ total++;
+ }
+ reader->Close();
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT(" Execution Time (%d results): = %6.4f (s)\n"), total, ((GetTickCount()-lStart)/1000.0) ));
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestFeatureService::TestCase_BenchmarkSqliteJoin() - Inner Join (Forced 1:1) \n")));
+ lStart = GetTickCount();
+ total = 0L;
+
+ reader = featSvc->SelectFeatures(fsId, L"ParcelsInnerOneToOne", NULL);
+ while(reader->ReadNext())
+ {
+ total++;
+ }
+ reader->Close();
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT(" Execution Time (%d results): = %6.4f (s)\n"), total, ((GetTickCount()-lStart)/1000.0) ));
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestFeatureService::TestCase_BenchmarkSqliteJoin() - Left Outer Join (Forced 1:1) \n")));
+ lStart = GetTickCount();
+ total = 0L;
+
+ reader = featSvc->SelectFeatures(fsId, L"ParcelsLeftOuterOneToOne", NULL);
+ while(reader->ReadNext())
+ {
+ total++;
+ }
+ reader->Close();
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT(" Execution Time (%d results): = %6.4f (s)\n"), total, ((GetTickCount()-lStart)/1000.0) ));
+ }
+ catch(MgException* e)
+ {
+ STRING message = e->GetDetails(TEST_LOCALE);
+ SAFE_RELEASE(e);
+ CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+ }
+ catch(FdoException* e)
+ {
+ STRING message = L"FdoException occurred: ";
+ message += e->GetExceptionMessage();
+ FDO_SAFE_RELEASE(e);
+ CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+ }
+ catch(...)
+ {
+ throw;
+ }
+}
+
+void TestFeatureService::TestCase_BenchmarkSqliteAggregateJoin()
+{
+ try
+ {
+ MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+ if(serviceManager == 0)
+ {
+ throw new MgNullReferenceException(L"TestFeatureService.TestCase_BenchmarkSqliteAggregateJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ Ptr<MgResourceService> pService = dynamic_cast<MgResourceService*>(serviceManager->RequestService(MgServiceType::ResourceService));
+ if (pService == 0)
+ {
+ throw new MgServiceNotAvailableException(L"TestFeatureService.TestCase_BenchmarkSqliteAggregateJoin", __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ Ptr<MgFeatureService> featSvc = dynamic_cast<MgFeatureService*>(serviceManager->RequestService(MgServiceType::FeatureService));
+ if (featSvc == 0)
+ {
+ throw new MgServiceNotAvailableException(L"TestFeatureService.TestCase_BenchmarkSqliteAggregateJoin",
+ __LINE__, __WFILE__, NULL, L"", NULL);
+ }
+
+ Ptr<MgResourceIdentifier> fsId = new MgResourceIdentifier(L"Library://UnitTests/Data/ParcelsJoinTestSQLite.FeatureSource");
+ CPPUNIT_ASSERT(featSvc->TestConnection(fsId));
+ Ptr<MgDataReader> reader;
+
+ // ----- Start the tests ------- //
+ Ptr<MgFeatureAggregateOptions> aggOpts = new MgFeatureAggregateOptions();
+ aggOpts->AddComputedProperty(L"Extents", L"SpatialExtents(Geometry)");
+ aggOpts->AddComputedProperty(L"TotalCount", L"Count(SdfId)");
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestFeatureService::TestCase_BenchmarkSqliteAggregateJoin() - Inner Join \n")));
+ long lStart = GetTickCount();
+ long total = 0L;
+ int iterations = 0;
+
+ reader = featSvc->SelectAggregate(fsId, L"ParcelsInner", aggOpts);
+ while(reader->ReadNext())
+ {
+ Ptr<MgByteReader> br = reader->GetGeometry(L"Extents");
+ total = reader->GetInt64(L"TotalCount");
+ iterations++;
+ }
+ reader->Close();
+ CPPUNIT_ASSERT(iterations == 1);
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT(" Execution Time (%d results): = %6.4f (s)\n"), total, ((GetTickCount()-lStart)/1000.0) ));
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestFeatureService::TestCase_BenchmarkSqliteAggregateJoin() - Left Outer Join \n")));
+ lStart = GetTickCount();
+ iterations = 0L;
+
+ reader = featSvc->SelectAggregate(fsId, L"ParcelsLeftOuter", aggOpts);
+ while(reader->ReadNext())
+ {
+ Ptr<MgByteReader> br = reader->GetGeometry(L"Extents");
+ total = reader->GetInt64(L"TotalCount");
+ iterations++;
+ }
+ reader->Close();
+ CPPUNIT_ASSERT(iterations == 1L);
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT(" Execution Time (%d results): = %6.4f (s)\n"), total, ((GetTickCount()-lStart)/1000.0) ));
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestFeatureService::TestCase_BenchmarkSqliteAggregateJoin() - Inner Join (Forced 1:1) \n")));
+ lStart = GetTickCount();
+ iterations = 0L;
+
+ reader = featSvc->SelectAggregate(fsId, L"ParcelsInnerOneToOne", aggOpts);
+ while(reader->ReadNext())
+ {
+ Ptr<MgByteReader> br = reader->GetGeometry(L"Extents");
+ total = reader->GetInt64(L"TotalCount");
+ iterations++;
+ }
+ reader->Close();
+ CPPUNIT_ASSERT(iterations == 1L);
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT(" Execution Time (%d results): = %6.4f (s)\n"), total, ((GetTickCount()-lStart)/1000.0) ));
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestFeatureService::TestCase_BenchmarkSqliteAggregateJoin() - Left Outer Join (Forced 1:1) \n")));
+ lStart = GetTickCount();
+ iterations = 0L;
+
+ reader = featSvc->SelectAggregate(fsId, L"ParcelsLeftOuterOneToOne", aggOpts);
+ while(reader->ReadNext())
+ {
+ Ptr<MgByteReader> br = reader->GetGeometry(L"Extents");
+ total = reader->GetInt64(L"TotalCount");
+ iterations++;
+ }
+ reader->Close();
+ CPPUNIT_ASSERT(iterations == 1L);
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT(" Execution Time (%d results): = %6.4f (s)\n"), total, ((GetTickCount()-lStart)/1000.0) ));
+ }
+ catch(MgException* e)
+ {
+ STRING message = e->GetDetails(TEST_LOCALE);
+ SAFE_RELEASE(e);
+ CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+ }
+ catch(FdoException* e)
+ {
+ STRING message = L"FdoException occurred: ";
+ message += e->GetExceptionMessage();
+ FDO_SAFE_RELEASE(e);
+ CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+ }
+ catch(...)
+ {
+ throw;
+ }
+}
\ No newline at end of file
Modified: trunk/MgDev/Server/src/UnitTesting/TestFeatureService.h
===================================================================
--- trunk/MgDev/Server/src/UnitTesting/TestFeatureService.h 2011-11-21 02:09:23 UTC (rev 6238)
+++ trunk/MgDev/Server/src/UnitTesting/TestFeatureService.h 2011-11-21 11:00:25 UTC (rev 6239)
@@ -58,6 +58,10 @@
CPPUNIT_TEST(TestCase_BenchmarkSelectFeatures);
CPPUNIT_TEST(TestCase_ConcurrentAccess);
CPPUNIT_TEST(TestCase_SavePoint);
+ CPPUNIT_TEST(TestCase_JoinFdoFeatures);
+ CPPUNIT_TEST(TestCase_BenchmarkSqliteJoin);
+
+ CPPUNIT_TEST(TestCase_BenchmarkSqliteAggregateJoin);
CPPUNIT_TEST(TestEnd); // This must be the very last unit test
CPPUNIT_TEST_SUITE_END();
@@ -101,6 +105,9 @@
void TestCase_BenchmarkSelectFeatures();
void TestCase_ConcurrentAccess();
void TestCase_SavePoint();
+ void TestCase_JoinFdoFeatures();
+ void TestCase_BenchmarkSqliteJoin();
+ void TestCase_BenchmarkSqliteAggregateJoin();
};
#endif // _TESTFEATURESERVICE_H
Added: trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/JoinTest.sqlite
===================================================================
(Binary files differ)
Property changes on: trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/JoinTest.sqlite
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/ParcelsJoinTest.sqlite
===================================================================
(Binary files differ)
Property changes on: trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/ParcelsJoinTest.sqlite
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/UT_FdoJoin.FeatureSource
===================================================================
--- trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/UT_FdoJoin.FeatureSource (rev 0)
+++ trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/UT_FdoJoin.FeatureSource 2011-11-21 11:00:25 UTC (rev 6239)
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<FeatureSource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd">
+ <Provider>OSGeo.SQLite</Provider>
+ <Parameter>
+ <Name>File</Name>
+ <Value>%MG_DATA_FILE_PATH%JoinTest.sqlite</Value>
+ </Parameter>
+ <Parameter>
+ <Name>UseFdoMetadata</Name>
+ <Value>FALSE</Value>
+ </Parameter>
+ <Extension>
+ <AttributeRelate>
+ <RelateProperty>
+ <FeatureClassProperty>CountryCode</FeatureClassProperty>
+ <AttributeClassProperty>CountryCode</AttributeClassProperty>
+ </RelateProperty>
+ <AttributeClass>Default:Countries</AttributeClass>
+ <ResourceId>Library://UnitTests/Data/FdoJoin.FeatureSource</ResourceId>
+ <Name>CNT_</Name>
+ <RelateType>Inner</RelateType>
+ <ForceOneToOne>false</ForceOneToOne>
+ </AttributeRelate>
+ <Name>CitiesCountries</Name>
+ <FeatureClass>Default:Cities</FeatureClass>
+ </Extension>
+ <Extension>
+ <AttributeRelate>
+ <RelateProperty>
+ <FeatureClassProperty>StateCode</FeatureClassProperty>
+ <AttributeClassProperty>StateCode</AttributeClassProperty>
+ </RelateProperty>
+ <AttributeClass>Default:States</AttributeClass>
+ <ResourceId>Library://UnitTests/Data/FdoJoin.FeatureSource</ResourceId>
+ <Name>ST_</Name>
+ <RelateType>Inner</RelateType>
+ <ForceOneToOne>false</ForceOneToOne>
+ </AttributeRelate>
+ <Name>CitiesStates</Name>
+ <FeatureClass>Default:Cities</FeatureClass>
+ </Extension>
+ <Extension>
+ <AttributeRelate>
+ <RelateProperty>
+ <FeatureClassProperty>StateCode</FeatureClassProperty>
+ <AttributeClassProperty>StateCode</AttributeClassProperty>
+ </RelateProperty>
+ <AttributeClass>Default:States</AttributeClass>
+ <ResourceId>Library://UnitTests/Data/FdoJoin.FeatureSource</ResourceId>
+ <Name>ST_</Name>
+ <RelateType>Inner</RelateType>
+ <ForceOneToOne>true</ForceOneToOne>
+ </AttributeRelate>
+ <Name>CitiesStatesOneToOne</Name>
+ <FeatureClass>Default:Cities</FeatureClass>
+ </Extension>
+</FeatureSource>
\ No newline at end of file
Added: trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/UT_Parcels_SQLite_Join.FeatureSource
===================================================================
--- trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/UT_Parcels_SQLite_Join.FeatureSource (rev 0)
+++ trunk/MgDev/UnitTest/TestData/FeatureService/SQLite/UT_Parcels_SQLite_Join.FeatureSource 2011-11-21 11:00:25 UTC (rev 6239)
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FeatureSource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd">
+ <Provider>OSGeo.SQLite</Provider>
+ <Parameter>
+ <Name>File</Name>
+ <Value>%MG_DATA_FILE_PATH%ParcelsJoinTest.sqlite</Value>
+ </Parameter>
+ <Parameter>
+ <Name>UseFdoMetadata</Name>
+ <Value>TRUE</Value>
+ </Parameter>
+ <Extension>
+ <AttributeRelate>
+ <RelateProperty>
+ <FeatureClassProperty>ID</FeatureClassProperty>
+ <AttributeClassProperty>ID</AttributeClassProperty>
+ </RelateProperty>
+ <AttributeClass>Default:Parcels</AttributeClass>
+ <ResourceId>Library://UnitTests/Data/ParcelsJoinTestSQLite.FeatureSource</ResourceId>
+ <Name>DATA</Name>
+ <RelateType>LeftOuter</RelateType>
+ <ForceOneToOne>true</ForceOneToOne>
+ </AttributeRelate>
+ <Name>ParcelsLeftOuterOneToOne</Name>
+ <FeatureClass>Default:ParcelFeatures</FeatureClass>
+ </Extension>
+ <Extension>
+ <AttributeRelate>
+ <RelateProperty>
+ <FeatureClassProperty>ID</FeatureClassProperty>
+ <AttributeClassProperty>ID</AttributeClassProperty>
+ </RelateProperty>
+ <AttributeClass>Default:Parcels</AttributeClass>
+ <ResourceId>Library://UnitTests/Data/ParcelsJoinTestSQLite.FeatureSource</ResourceId>
+ <Name>DATA</Name>
+ <RelateType>Inner</RelateType>
+ <ForceOneToOne>true</ForceOneToOne>
+ </AttributeRelate>
+ <Name>ParcelsInnerOneToOne</Name>
+ <FeatureClass>Default:ParcelFeatures</FeatureClass>
+ </Extension>
+ <Extension>
+ <AttributeRelate>
+ <RelateProperty>
+ <FeatureClassProperty>ID</FeatureClassProperty>
+ <AttributeClassProperty>ID</AttributeClassProperty>
+ </RelateProperty>
+ <AttributeClass>Default:Parcels</AttributeClass>
+ <ResourceId>Library://UnitTests/Data/ParcelsJoinTestSQLite.FeatureSource</ResourceId>
+ <Name>DATA</Name>
+ <RelateType>LeftOuter</RelateType>
+ <ForceOneToOne>false</ForceOneToOne>
+ </AttributeRelate>
+ <Name>ParcelsLeftOuter</Name>
+ <FeatureClass>Default:ParcelFeatures</FeatureClass>
+ </Extension>
+ <Extension>
+ <AttributeRelate>
+ <RelateProperty>
+ <FeatureClassProperty>ID</FeatureClassProperty>
+ <AttributeClassProperty>ID</AttributeClassProperty>
+ </RelateProperty>
+ <AttributeClass>Default:Parcels</AttributeClass>
+ <ResourceId>Library://UnitTests/Data/ParcelsJoinTestSQLite.FeatureSource</ResourceId>
+ <Name>DATA</Name>
+ <RelateType>Inner</RelateType>
+ <ForceOneToOne>false</ForceOneToOne>
+ </AttributeRelate>
+ <Name>ParcelsInner</Name>
+ <FeatureClass>Default:ParcelFeatures</FeatureClass>
+ </Extension>
+</FeatureSource>
\ No newline at end of file
More information about the mapguide-commits
mailing list