/src/gdal/ogr/ogr_schema_override.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /******************************************************************************  | 
2  |  |  * Project:  OGR_SCHEMA open options handling  | 
3  |  |  * Purpose:  Class for representing a layer schema override.  | 
4  |  |  * Author:   Alessandro Pasotti, elpaso@itopen.it  | 
5  |  |  *  | 
6  |  |  ******************************************************************************  | 
7  |  |  * Copyright (c) 2024, Alessandro Pasotti <elpaso at itopen dot it>  | 
8  |  |  *  | 
9  |  |  * SPDX-License-Identifier: MIT  | 
10  |  |  ****************************************************************************/  | 
11  |  |  | 
12  |  | //! @cond Doxygen_Suppress  | 
13  |  |  | 
14  |  | #include "ogr_schema_override.h"  | 
15  |  |  | 
16  |  | bool OGRSchemaOverride::LoadFromJSON(const std::string &osJSON)  | 
17  | 0  | { | 
18  | 0  |     std::string osFieldsSchemaOverride;  | 
19  | 0  |     bool bFieldsSchemaOverrideIsFilePath{false}; | 
20  |  |  | 
21  |  |     // Try to load the content of the file  | 
22  | 0  |     GByte *pabyRet = nullptr;  | 
23  | 0  |     if (VSIIngestFile(nullptr, osJSON.c_str(), &pabyRet, nullptr, -1) == TRUE)  | 
24  | 0  |     { | 
25  | 0  |         bFieldsSchemaOverrideIsFilePath = true;  | 
26  | 0  |         osFieldsSchemaOverride = std::string(reinterpret_cast<char *>(pabyRet));  | 
27  | 0  |         VSIFree(pabyRet);  | 
28  | 0  |     }  | 
29  |  | 
  | 
30  | 0  |     if (!bFieldsSchemaOverrideIsFilePath)  | 
31  | 0  |     { | 
32  | 0  |         osFieldsSchemaOverride = osJSON;  | 
33  | 0  |     }  | 
34  |  | 
  | 
35  | 0  |     CPLJSONDocument oSchemaDoc;  | 
36  | 0  |     if (oSchemaDoc.LoadMemory(osFieldsSchemaOverride))  | 
37  | 0  |     { | 
38  | 0  |         const CPLJSONObject oRoot = oSchemaDoc.GetRoot();  | 
39  | 0  |         if (oRoot.IsValid())  | 
40  | 0  |         { | 
41  | 0  |             const auto aoLayers = oRoot.GetArray("layers"); | 
42  |  |             // Loop through layer names and get the field details for each field.  | 
43  | 0  |             for (const auto &oLayer : aoLayers)  | 
44  | 0  |             { | 
45  | 0  |                 if (oLayer.IsValid())  | 
46  | 0  |                 { | 
47  | 0  |                     const auto oLayerFields = oLayer.GetArray("fields"); | 
48  |  |                     // Parse fields  | 
49  | 0  |                     const auto osLayerName = oLayer.GetString("name"); | 
50  | 0  |                     const auto osSchemaType = oLayer.GetString("schemaType"); | 
51  |  |                     // Default schemaType is "Patch"  | 
52  | 0  |                     const auto bSchemaFullOverride =  | 
53  | 0  |                         CPLString(osSchemaType).tolower() == "full";  | 
54  | 0  |                     OGRLayerSchemaOverride oLayerOverride;  | 
55  | 0  |                     oLayerOverride.SetLayerName(osLayerName);  | 
56  | 0  |                     oLayerOverride.SetFullOverride(bSchemaFullOverride);  | 
57  |  | 
  | 
58  | 0  |                     if (oLayerFields.Size() > 0 && !osLayerName.empty())  | 
59  | 0  |                     { | 
60  | 0  |                         for (const auto &oField : oLayerFields)  | 
61  | 0  |                         { | 
62  | 0  |                             const auto osFieldName = oField.GetString("name"); | 
63  | 0  |                             if (osFieldName.empty())  | 
64  | 0  |                             { | 
65  | 0  |                                 CPLError(CE_Warning, CPLE_AppDefined,  | 
66  | 0  |                                          "Field name is missing");  | 
67  | 0  |                                 return false;  | 
68  | 0  |                             }  | 
69  | 0  |                             OGRFieldDefnOverride oFieldOverride;  | 
70  |  | 
  | 
71  | 0  |                             const CPLString oType(  | 
72  | 0  |                                 CPLString(oField.GetString("type")).tolower()); | 
73  | 0  |                             const CPLString oSubType(  | 
74  | 0  |                                 CPLString(oField.GetString("subType")) | 
75  | 0  |                                     .tolower());  | 
76  | 0  |                             const CPLString osNewName(  | 
77  | 0  |                                 CPLString(oField.GetString("newName")) | 
78  | 0  |                                     .tolower());  | 
79  | 0  |                             const auto nWidth = oField.GetInteger("width", 0); | 
80  | 0  |                             const auto nPrecision =  | 
81  | 0  |                                 oField.GetInteger("precision", 0); | 
82  |  | 
  | 
83  | 0  |                             if (!osNewName.empty())  | 
84  | 0  |                             { | 
85  | 0  |                                 oFieldOverride.SetFieldName(osNewName);  | 
86  | 0  |                             }  | 
87  |  | 
  | 
88  | 0  |                             if (!oType.empty())  | 
89  | 0  |                             { | 
90  | 0  |                                 const OGRFieldType eType =  | 
91  | 0  |                                     OGRFieldDefn::GetFieldTypeByName(  | 
92  | 0  |                                         oType.c_str());  | 
93  |  |                                 // Check if the field type is valid  | 
94  | 0  |                                 if (eType == OFTString && oType != "string")  | 
95  | 0  |                                 { | 
96  | 0  |                                     CPLError(CE_Failure, CPLE_AppDefined,  | 
97  | 0  |                                              "Unsupported field type: %s "  | 
98  | 0  |                                              "for field %s",  | 
99  | 0  |                                              oType.c_str(),  | 
100  | 0  |                                              osFieldName.c_str());  | 
101  | 0  |                                     return false;  | 
102  | 0  |                                 }  | 
103  | 0  |                                 oFieldOverride.SetFieldType(eType);  | 
104  | 0  |                             }  | 
105  |  |  | 
106  | 0  |                             if (!oSubType.empty())  | 
107  | 0  |                             { | 
108  | 0  |                                 const OGRFieldSubType eSubType =  | 
109  | 0  |                                     OGRFieldDefn::GetFieldSubTypeByName(  | 
110  | 0  |                                         oSubType.c_str());  | 
111  |  |                                 // Check if the field subType is valid  | 
112  | 0  |                                 if (eSubType == OFSTNone && oSubType != "none")  | 
113  | 0  |                                 { | 
114  | 0  |                                     CPLError(CE_Failure, CPLE_AppDefined,  | 
115  | 0  |                                              "Unsupported field subType: "  | 
116  | 0  |                                              "%s for field %s",  | 
117  | 0  |                                              oSubType.c_str(),  | 
118  | 0  |                                              osFieldName.c_str());  | 
119  | 0  |                                     return false;  | 
120  | 0  |                                 }  | 
121  | 0  |                                 oFieldOverride.SetFieldSubType(eSubType);  | 
122  | 0  |                             }  | 
123  |  |  | 
124  | 0  |                             if (nWidth != 0)  | 
125  | 0  |                             { | 
126  | 0  |                                 oFieldOverride.SetFieldWidth(nWidth);  | 
127  | 0  |                             }  | 
128  |  | 
  | 
129  | 0  |                             if (nPrecision != 0)  | 
130  | 0  |                             { | 
131  | 0  |                                 oFieldOverride.SetFieldPrecision(nPrecision);  | 
132  | 0  |                             }  | 
133  |  | 
  | 
134  | 0  |                             if (bSchemaFullOverride || oFieldOverride.IsValid())  | 
135  | 0  |                             { | 
136  | 0  |                                 oLayerOverride.AddFieldOverride(osFieldName,  | 
137  | 0  |                                                                 oFieldOverride);  | 
138  | 0  |                             }  | 
139  | 0  |                             else  | 
140  | 0  |                             { | 
141  | 0  |                                 CPLError(CE_Failure, CPLE_AppDefined,  | 
142  | 0  |                                          "Field %s has no valid overrides "  | 
143  | 0  |                                          "and schemaType is not \"Full\"",  | 
144  | 0  |                                          osFieldName.c_str());  | 
145  | 0  |                                 return false;  | 
146  | 0  |                             }  | 
147  | 0  |                         }  | 
148  | 0  |                     }  | 
149  |  |  | 
150  | 0  |                     if (oLayerOverride.IsValid())  | 
151  | 0  |                     { | 
152  | 0  |                         AddLayerOverride(osLayerName, oLayerOverride);  | 
153  | 0  |                     }  | 
154  | 0  |                     else  | 
155  | 0  |                     { | 
156  | 0  |                         CPLError(CE_Failure, CPLE_AppDefined,  | 
157  | 0  |                                  "Layer %s has no valid overrides",  | 
158  | 0  |                                  osLayerName.c_str());  | 
159  | 0  |                         return false;  | 
160  | 0  |                     }  | 
161  | 0  |                 }  | 
162  | 0  |                 else  | 
163  | 0  |                 { | 
164  | 0  |                     CPLError(CE_Failure, CPLE_AppDefined,  | 
165  | 0  |                              "SCHEMA info is invalid JSON");  | 
166  | 0  |                     return false;  | 
167  | 0  |                 }  | 
168  | 0  |             }  | 
169  | 0  |             return true;  | 
170  | 0  |         }  | 
171  | 0  |         else  | 
172  | 0  |         { | 
173  | 0  |             CPLError(CE_Failure, CPLE_AppDefined,  | 
174  | 0  |                      "SCHEMA info is invalid JSON");  | 
175  | 0  |             return false;  | 
176  | 0  |         }  | 
177  | 0  |     }  | 
178  | 0  |     else  | 
179  | 0  |     { | 
180  | 0  |         CPLError(CE_Failure, CPLE_AppDefined, "SCHEMA info is invalid JSON");  | 
181  | 0  |         return false;  | 
182  | 0  |     }  | 
183  | 0  | }  | 
184  |  |  | 
185  |  | bool OGRSchemaOverride::IsValid() const  | 
186  | 0  | { | 
187  | 0  |     bool isValid = !m_moLayerOverrides.empty();  | 
188  | 0  |     for (const auto &oLayerOverride : m_moLayerOverrides)  | 
189  | 0  |     { | 
190  | 0  |         isValid &= oLayerOverride.second.IsValid();  | 
191  | 0  |     }  | 
192  | 0  |     return isValid;  | 
193  | 0  | }  | 
194  |  |  | 
195  |  | bool OGRLayerSchemaOverride::IsValid() const  | 
196  | 0  | { | 
197  | 0  |     bool isValid = !m_osLayerName.empty() && !m_moFieldOverrides.empty();  | 
198  | 0  |     for (const auto &oFieldOverride : m_moFieldOverrides)  | 
199  | 0  |     { | 
200  | 0  |         isValid &= !oFieldOverride.first.empty();  | 
201  |  |         // When schemaType is "full" override we don't need to check if the field  | 
202  |  |         // overrides are valid: a list of fields to keep is enough.  | 
203  | 0  |         if (!m_bIsFullOverride)  | 
204  | 0  |         { | 
205  | 0  |             isValid &= oFieldOverride.second.IsValid();  | 
206  | 0  |         }  | 
207  | 0  |     }  | 
208  | 0  |     return isValid;  | 
209  | 0  | }  | 
210  |  |  | 
211  |  | bool OGRFieldDefnOverride::IsValid() const  | 
212  | 0  | { | 
213  | 0  |     return m_osName.has_value() || m_eType.has_value() ||  | 
214  | 0  |            m_eSubType.has_value() || m_nWidth.has_value() ||  | 
215  | 0  |            m_nPrecision.has_value();  | 
216  | 0  | }  | 
217  |  |  | 
218  |  | //! @endcond  |