70543387d3905811f4a9aa131cf96d50cd504225
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query;
23
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Calendar;
27 import java.util.Collection;
28 import java.util.Date;
29 import java.util.List;
30 import java.util.Set;
31 import java.util.TimeZone;
32 import java.util.stream.Collectors;
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.onap.ccsdk.features.sdnr.wt.common.database.data.DbFilter;
35 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.database.SqlDBMapper;
36 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.DBFilterKeyValuePair;
37 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.RangeSqlDBFilter;
38 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.RegexSqlDBFilter;
39 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.SqlDBSearchFilter;
40 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.NetconfTimeStamp;
41 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.types.NetconfTimeStampImpl;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.entity.input.Filter;
43
44
45 public interface SqlQuery {
46
47     String toSql();
48
49     static final List<String> TIMESTAMPPROPERTYNAMES = Arrays.asList("timestamp", "time-stamp", "start", "end");
50     static final String MARIADB_TIMESTAMP_REPLACER = "0000-00-00 00:00:00.000";
51     static final String NETCONF_TIMESTAMP_REPLACER = "0000-00-00T00:00:00.000Z";
52     static final String MARIADB_TIMESTAMP_REPLACER_MIN = "0000-00-00 00:00:00";
53     static final int MARIADB_TIMESTAMP_REPLACER_MIN_LENGTH = MARIADB_TIMESTAMP_REPLACER_MIN.length();
54     static final int MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH = MARIADB_TIMESTAMP_REPLACER.length();
55     static final boolean DEFAULT_IGNORE_CONTROLLERID = false;
56     static final boolean DEFAULT_IGNORE_ID_FIELD = false;
57
58     public static String getWhereExpression(Collection<Filter> filters) {
59         return getWhereExpression(filters, null);
60     }
61     public static String getWhereExpression(Collection<Filter> filters, String controllerId) {
62         return getWhereExpression(filters, controllerId,  null);
63     }
64     public static String getWhereExpression(Collection<Filter> filters, String controllerId, SqlDBSearchFilter allPropertyFilter) {
65         if (filters == null && controllerId == null) {
66             return "";
67         }
68         StringBuilder sb = new StringBuilder();
69         List<String> filters2 =
70                 filters != null
71                         ? filters.stream().filter(e -> !isFilterEmpty(e)).map(e -> getFilterExpression(e))
72                                 .collect(Collectors.toList())
73                         : new ArrayList<>();
74         if (controllerId != null) {
75             filters2.add(getFilterExpression(SqlDBMapper.ODLID_DBCOL, controllerId));
76         }
77         if(allPropertyFilter!=null){
78             filters2.add(allPropertyFilter.getFilterExpression(true));
79         }
80         if (!filters2.isEmpty()) {
81             sb.append(" WHERE ");
82             sb.append(String.join(" AND ", filters2));
83
84         }
85         return sb.toString();
86     }
87
88     private static String getFilterExpression(Filter filter) {
89         String property = filter.getProperty();
90         List<String> values = collectValues(filter.getFiltervalue(), filter.getFiltervalues()).stream()
91                 .filter(e -> !"*".equals(e)).collect(Collectors.toList());
92         if (values.size() == 1) {
93             return getFilterExpression(property, values.get(0));
94         } else if (values.size() > 1) {
95             StringBuilder sb = new StringBuilder();
96             sb.append(getFilterExpression(property, values.get(0)));
97             for (int i = 1; i < values.size(); i++) {
98                 sb.append(" OR ");
99                 sb.append(getFilterExpression(property, values.get(i)));
100             }
101             return sb.toString();
102         }
103         return null;
104     }
105
106     private static String getFilterExpression(String property, String value) {
107         String filter = null;
108         if (DbFilter.hasSearchParams(value)) {
109             if (TIMESTAMPPROPERTYNAMES.contains(property.toLowerCase())) {
110                 if (DbFilter.isComparisonValid(value)) {
111                     filter = getComparisonFilter(property, value, true);
112                 } else {
113                     filter = fromTimestampSearchFilter(property, value);
114                 }
115                 if (filter != null) {
116                     return filter;
117                 }
118             }
119             return new RegexSqlDBFilter(property, value).getFilterExpression();
120         } else if (DbFilter.isComparisonValid(value)) {
121             filter = getComparisonFilter(property, value, TIMESTAMPPROPERTYNAMES.contains(property.toLowerCase()));
122             if (filter != null) {
123                 return filter;
124             }
125         }
126         return new DBFilterKeyValuePair(property, value).getFilterExpression();
127     }
128
129     private static List<String> collectValues(String filtervalue, Set<String> filtervalues) {
130         if (filtervalues == null) {
131             return Arrays.asList(filtervalue);
132         }
133         List<String> values = new ArrayList<>();
134         if (filtervalue != null) {
135             values.add(filtervalue);
136         }
137         values.addAll(filtervalues);
138         return values;
139     }
140
141     private static String getComparisonFilter(String property, String filtervalue, boolean asTimeStamp) {
142         filtervalue = filtervalue.trim();
143         String comparator = null;
144         Object value;
145         if (filtervalue.startsWith(">=")) {
146             comparator = ">=";
147             filtervalue = filtervalue.substring(2).trim();
148             if (asTimeStamp) {
149                 filtervalue = netconfToMariaDBTimestamp(fillTimeStamp(
150                         filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"), MARIADB_TIMESTAMP_REPLACER));
151             }
152         } else if (filtervalue.startsWith(">")) {
153             comparator = ">";
154             filtervalue = filtervalue.substring(1).trim();
155             if (asTimeStamp) {
156                 if (isFullTimestamp(filtervalue)) {
157                     filtervalue = netconfToMariaDBTimestamp(filtervalue);
158                 } else {
159                     comparator = ">=";
160                     filtervalue = netconfToMariaDBTimestamp(
161                             fillTimeStamp(filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"),
162                                     NETCONF_TIMESTAMP_REPLACER, true));
163                 }
164             }
165         } else if (filtervalue.startsWith("<=")) {
166             comparator = "<=";
167             filtervalue = filtervalue.substring(2).trim();
168             if (asTimeStamp) {
169                 if (isFullTimestamp(filtervalue)) {
170                     filtervalue = netconfToMariaDBTimestamp(filtervalue);
171                 } else {
172                     comparator = "<";
173                     filtervalue = netconfToMariaDBTimestamp(
174                             fillTimeStamp(filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"),
175                                     NETCONF_TIMESTAMP_REPLACER, true));
176                 }
177             }
178         } else if (filtervalue.startsWith("<")) {
179             comparator = "<";
180             filtervalue = filtervalue.substring(1).trim();
181             if (asTimeStamp) {
182                 filtervalue = netconfToMariaDBTimestamp(fillTimeStamp(
183                         filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"), MARIADB_TIMESTAMP_REPLACER));
184             }
185         } else {
186             return null;
187         }
188         value = filtervalue;
189         return new RangeSqlDBFilter(property, value, comparator).getFilterExpression();
190     }
191
192     static boolean isFullTimestamp(String v) {
193         return v.length() >= MARIADB_TIMESTAMP_REPLACER_MIN_LENGTH;
194     }
195
196     /**
197      * Convert timestamp beginning filter expression like 2017* to a full qualified timestamp like '2017-01-01
198      * 00:00:00'.
199      *
200      * @param value filter input value
201      * @return fully qualified timestamp
202      */
203     private static String fillTimeStamp(String value) {
204         return fillTimeStamp(value, NETCONF_TIMESTAMP_REPLACER);
205     }
206
207     private static String fillTimeStamp(String value, String replacer) {
208         return fillTimeStamp(value, replacer, false);
209     }
210
211     private static String fillTimeStamp(String value, String replacer, boolean useUpperEnd) {
212         int idx = value.lastIndexOf("*");
213         String s = null;
214         if (idx > replacer.length()) {
215             s = value.substring(0, replacer.length());
216         } else {
217             s = value.substring(0, idx) + replacer.substring(idx);
218         }
219         //if month is zero => set to 1
220         if (Integer.parseInt(s.substring(5, 7)) == 0) {
221             s = s.substring(0, 5) + "01-" + s.substring(8);
222         }
223         //if day is zero => set to 1
224         if (Integer.parseInt(s.substring(8, 10)) == 0) {
225             s = s.substring(0, 8) + "01" + s.substring(10);
226         }
227         if (useUpperEnd) {
228             s = getTimestampUpperLimit(s, idx);
229         }
230         return s;
231     }
232
233     /**
234      * convert timestamp with ending placeholder in filter to elasticsearch filter e.g. 2017* => gte: 2017-01-01
235      * 00:00:00, lt:2018-01-01 00:00:00Z
236      *
237      * 201* => 2010-01... 2020 .. 2018-* => 2018-01... <=> 2019-01
238      *
239      */
240     private static @Nullable String fromTimestampSearchFilter(String property, String value) {
241         if (!value.endsWith("*")) {
242             return null;
243         }
244         int idx = value.lastIndexOf("*");
245         String lowerEnd = fillTimeStamp(value);
246         String upperEnd = getTimestampUpperLimit(fillTimeStamp(value, "0000-00-00T00:00:00.0Z"), idx);
247         return RangeSqlDBFilter.between(property, netconfToMariaDBTimestamp(lowerEnd), true,
248                 netconfToMariaDBTimestamp(upperEnd), false);
249     }
250
251     private static String netconfToMariaDBTimestamp(String ts) {
252         if (ts == null) {
253             return null;
254         }
255         String v = ts.replace("T", " ").replace("Z", "");
256         return v.length() > MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH
257                 ? v.substring(0, MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH)
258                 : v;
259     }
260
261     private static String getTimestampUpperLimit(String lowerEnd, int idx) {
262
263         String upperEnd = null;
264         NetconfTimeStamp converter = NetconfTimeStampImpl.getConverter();
265         Date dt = null;
266         try {
267             dt = converter.getDateFromNetconf(lowerEnd);
268         } catch (Exception e) {
269
270         }
271         if (dt == null) {
272             return null;
273         }
274
275         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
276         c.setTime(dt);
277         int tmpvalue;
278         switch (idx) {
279             case 1: // (2*)
280                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1000);
281                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
282                 break;
283             case 2: // (20*)
284                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 100);
285                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
286                 break;
287             case 3: // (200*)
288                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 10);
289                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
290                 break;
291             case 4: // (2000*)
292             case 5: // (2000-*)
293                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
294                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
295                 break;
296             case 6: // switch 10 months (2000-0* or 2000-1*)
297                 tmpvalue = c.get(Calendar.MONTH);
298                 if (tmpvalue < 9) {
299                     c.set(Calendar.MONTH, 9);
300                 } else {
301                     c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
302                     c.set(Calendar.MONTH, 0);
303                 }
304                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
305
306                 break;
307             case 7: // switch one month (2018-01* or 2018-01-*)
308             case 8:
309                 c.add(Calendar.MONTH, 1);
310                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
311                 break;
312             case 9: // (2018-01-0*)
313                 tmpvalue = c.get(Calendar.DAY_OF_MONTH);
314                 if (tmpvalue == 1) {
315                     c.set(Calendar.DAY_OF_MONTH, 10);
316                 } else if (tmpvalue == 10) {
317                     c.set(Calendar.DAY_OF_MONTH, 20);
318                 } else if (tmpvalue == 20) {
319                     if (c.getActualMaximum(Calendar.DAY_OF_MONTH) < 30) {
320                         c.set(Calendar.DAY_OF_MONTH, 1);
321                         c.add(Calendar.MONTH, 1);
322                     } else {
323                         c.set(Calendar.DAY_OF_MONTH, 30);
324                     }
325                 } else if (tmpvalue == 30) {
326                     c.set(Calendar.DAY_OF_MONTH, 1);
327                     c.add(Calendar.MONTH, 1);
328                 } else {
329                     break;
330                 }
331                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
332                 break;
333             case 10: // (2018-01-01*)
334             case 11: // (2018-01-01T*)
335                 c.add(Calendar.DAY_OF_MONTH, 1);
336                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
337                 break;
338             case 12: // (2018-01-01T1*)
339                 tmpvalue = c.get(Calendar.HOUR_OF_DAY);
340                 if (tmpvalue == 20) {
341                     c.set(Calendar.HOUR_OF_DAY, 0);
342                     c.add(Calendar.DAY_OF_MONTH, 1);
343                 } else {
344                     c.add(Calendar.HOUR_OF_DAY, 10);
345                 }
346                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
347                 break;
348             case 13: // (2018-01-01T11*)
349             case 14: // (2018-01-01T11-*)
350                 c.add(Calendar.HOUR_OF_DAY, 1);
351                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
352                 break;
353             case 15: // (2018-01-01T11-3*)
354                 c.add(Calendar.MINUTE, 10);
355                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
356                 break;
357             case 16: // (2018-01-01T11-32*)
358             case 17: // (2018-01-01T11-32-*)
359                 c.add(Calendar.MINUTE, 1);
360                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
361                 break;
362             case 18: // (2018-01-01T11-32-1*)
363                 c.add(Calendar.SECOND, 10);
364                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
365                 break;
366             case 19: // (2018-01-01T11-32-11*)
367             case 20: // (2018-01-01T11-32-11.*)
368                 c.add(Calendar.SECOND, 1);
369                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
370                 break;
371
372             default:
373                 break;
374         }
375         return upperEnd;
376     }
377
378
379     private static boolean isFilterEmpty(Filter filter) {
380         @Nullable Set<String> filtervalues = filter.getFiltervalues();
381         @Nullable String filtervalue = filter.getFiltervalue();
382
383         List<String> allValues = filtervalues == null ? new ArrayList<>() : new ArrayList<>(filtervalues);
384         if (filtervalue != null) {
385             allValues.add(filtervalue);
386         }
387
388         return allValues.isEmpty() || allValues.contains("*");
389     }
390
391
392 }