ThinkGeo.com    |     Documentation    |     Premium Support

Npgsql.NpgsqlException

Hi,


I got the Npgsql.NpgsqlException , the detailed information is as follows:


 BaseMessage="stack depth limit exceeded"

  Code="54001"


This is my scenario, I have a PostgreFeatureLayer, and need to create a thematic map based on user's selection, for example two of the columns are Type and Sub_Type, the values in the Type column could be 0-9, and the values in the Sub_Type column could be 0-5 or 0-10 depending on the value in Type column. The user may want to create a thematic map for Type 3 only.


Here is how I did, I retrieved the data with Type=3  using QueryTools.ExecuteQuery method, as the QueryTool.ExecuteQuery method returns a datatable, so I retrieved the IDs from the datatable, used the FeatureSource.GetFeaturesByIDs() method  to retrieve all the features, then created an InMemoryFeatureLayer using the retrieved features, and created thematic map for the InMemeoryFeatureLayer.


The exceptions throws  when there are thousands of data at this line of code: postgreLyr.FeatureSource.GetFeaturesByIDs.


Is this the proper way to create a thematic map for a SQL query result?


Thanks


Rose



Rose, 
  
 We couldn’t recreate it with a postgre Database having 190000 features within it. We will have more test on X64 bit system later. here is our test code if you are interested. 
  
 
private void Form1_Load(object sender, EventArgs e)
        {
            winformsMap1.MapUnit = GeographyUnit.DecimalDegree;

            winformsMap1.CurrentExtent = new RectangleShape(-126.4, 48.8, -67.0, 19.0);
            winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.GeographicColors.ShallowOcean);

            // This server is in Deng’s virtual machine.
            string connectString = “Server=192.168.0.212;User Id=postgres;Password=thinkgeo;DataBase=postgis;”;
            PostgreSqlFeatureLayer postgreLayer = new PostgreSqlFeatureLayer(connectString, “rail”, “oid”);
            postgreLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle = LineStyles.Railway1;
            postgreLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

            LayerOverlay staticOverlay = new LayerOverlay();
            staticOverlay.Layers.Add(“PostgreLayer”, postgreLayer);
            winformsMap1.Overlays.Add(staticOverlay);

            LayerOverlay dynamicOverlay = new LayerOverlay();
            InMemoryFeatureLayer dynamicLayer = new InMemoryFeatureLayer();
            dynamicLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle = new LineStyle(new GeoPen(new GeoSolidBrush(GeoColor.SimpleColors.Blue), 5));
            dynamicLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
            dynamicOverlay.Layers.Add(“DynamicLayer”, dynamicLayer);
            winformsMap1.Overlays.Add(dynamicOverlay);

            winformsMap1.Refresh();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            PostgreSqlFeatureLayer layer = (PostgreSqlFeatureLayer)((LayerOverlay)(winformsMap1.Overlays[0])).Layers[0];
            Collection<Feature> features = layer.FeatureSource.GetFeaturesByIds(new string[] { “1000”, “1002” }, ReturningColumnsType.NoColumns);
            InMemoryFeatureLayer dynamicLayer = (InMemoryFeatureLayer)winformsMap1.FindFeatureLayer(“DynamicLayer”);

            try
            {

                winformsMap1.Overlays[1].Lock.EnterWriteLock();
                dynamicLayer.InternalFeatures.Clear();
                foreach (Feature feature in features)
                {
                    dynamicLayer.InternalFeatures.Add(feature);
                }
            }
            finally
            {
                winformsMap1.Overlays[1].Lock.ExitWriteLock();
            }
            dynamicLayer.Open();
            winformsMap1.CurrentExtent = dynamicLayer.GetBoundingBox();
            dynamicLayer.Close();
            winformsMap1.Refresh();
        }
 
  
 Thanks, 
  
 Ben

Ben,


Thanks for your reply.


My code is similar with yours, the only difference is when I call the postgreLayer.FeatureSource.GetFeaturesbyIDs() method, I passed in thousands of IDs instead of only two.


The postgreFeatureLayer.QueryTools.ExecuteQuery() returns a datatable, I extracted the IDs from the datatable and stored in a Collection, then passed this collection to the postgreFeatureLayer.FeatureSource.GetFeaturesByIDs() method.


Is this the best way to create an InMemoryFeatureLayer from a SQL query?


Thanks


Rose


 



Rose,


Thanks for your post.


First I think it is a very proper way to create a InmemoryFeatyreLayer from a SQL query.


Also we tested it with a Database of 190000 records, and passed in 2000 Ids for the API GetFeaturesByIds() and it did not crashed.


Also we tested it with a Database of 2000000 records,  and passed in 4000 Ids for the API GetFeaturesByIds() and it did not crashed, but when I passed in 180000Ids for the API it will crashed, but seems we different exception. And the reason for this crash is that the Postgre do not support so long “text” SQL statement. We will try to fix this problem in next release. If you want , you can have a solution to go around this problem by split the “Ids” passed in into many “small” groups and passed in to avoid this problem, of course , it will cause some performance penalty.


Following is a snapshot which shows my exception.


 



We will continue to do more investigation about this issue.


Yale.

 



Yale:


The workaround  you mentioned to do the query in a couple of steps will be too slow.


Is it possible to make the SQL query  to return a collection of features instead of a datatable , just like a spatial query? In this case, we don't need to run queries to get the selected features displayed in an InMemoryFeatureLayer?


Rose



Rose,


Eventually, we recreated the problem and figure out what is going on here.
 
2 things I want to mention here:
1) You can change the value of max_statck_depth value to make it larger to accept more query value in the Postgre Server. We tested if we change the value from 2000 to 3000, it will delay the exception happening.
 
See the attachment below.
 
 
2) There is some enhancement room for our postgre extension codes.
Currently, when we do query from Postgre , we use a sql statement as following:
“select * from table where oid=’1’ or oid=’2’
This statement will be very long when querying a huge number of features Ids and also when the name of “Id” column is very long, in our sample it is only “oid”, 3 characters.
 
If you use the following paten, it will shorten the sql statement very much; I think this enhancement will be included in next release.
 
“select * from table where oid in (‘1’, ‘2’,…)”
 
Any more questions just let me know.
 
Thanks for your attention.
Yale.

Yale:


FeatureSource.getFeaturesByIDs() method only accepts an IEnumerable( of String ) for the IDs, how to make sure it uses the "IN" operator?


I am asking again, Is it possible to make the SQL query  to return a collection of features instead of a datatable , just like a spatial query? In this case, we don't need to run FeatureSource.getFeaturesByIDs() to get the selected features displayed in an InMemoryFeatureLayer?


We are already far behind our schedule, any help is greatly appreciated.


Thanks


Rose


 



Rose, 
  
 Sorry for forgetting one of your questions. 
  
 We can fix somehow about this problem and I think it will not happen in your application if you can stop it from growing forever. 
 Because we use a very robust third party Npgsql to operate the Postgre, We can support whatever it can support. For some reason, we did not generate a very decent sql statement  for the API  GetFeaturesByIds, we already fix this problem. I suggest you wait another release or we can send you another temporary version for this problem. 
  
 About the returning a collection of features instead of data table problem, My answer is Yes. But even though if we want to do this, we have to do exactly the same thing as you did, just extract the features from the dataTable, so we currently just do the most common way: return a DataTable and the user can do whatever they want based on this. 
  
 Sorry for the inconvenience, thanks. 
  
 Yale. 


Yale:


I can't afford the time to wait for next release. Please send me the temporary version your mentioned.


Thanks


Rose



Rose 
  
 We will send you a Temporary build tomorrow. Sorry for the inconvenience. 
  
 Hope to get your feedback after try on this version. 
  
 Thanks. 
  
 Yale.