89638cc8b08696e0cc13fff3447d5a26d9e0a9d8
[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 import io.netty.util.internal.StringUtil;
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(StringUtil.join(" AND ", filters2));
83         }
84         return sb.toString();
85     }
86
87     private static String getFilterExpression(Filter filter) {
88         String property = filter.getProperty();
89         List<String> values = collectValues(filter.getFiltervalue(), filter.getFiltervalues()).stream()
90                 .filter(e -> !"*".equals(e)).collect(Collectors.toList());
91         if (values.size() == 1) {
92             return getFilterExpression(property, values.get(0));
93         } else if (values.size() > 1) {
94             StringBuilder sb = new StringBuilder();
95             sb.append(getFilterExpression(property, values.get(0)));
96             for (int i = 1; i < values.size(); i++) {
97                 sb.append(" OR ");
98                 sb.append(getFilterExpression(property, values.get(i)));
99             }
100             return sb.toString();
101         }
102         return null;
103     }
104
105     private static String getFilterExpression(String property, String value) {
106         String filter = null;
107         if (DbFilter.hasSearchParams(value)) {
108             if (TIMESTAMPPROPERTYNAMES.contains(property.toLowerCase())) {
109                 if (DbFilter.isComparisonValid(value)) {
110                     filter = getComparisonFilter(property, value, true);
111                 } else {
112                     filter = fromTimestampSearchFilter(property, value);
113                 }
114                 if (filter != null) {
115                     return filter;
116                 }
117             }
118             return new RegexSqlDBFilter(property, value).getFilterExpression();
119         } else if (DbFilter.isComparisonValid(value)) {
120             filter = getComparisonFilter(property, value, TIMESTAMPPROPERTYNAMES.contains(property.toLowerCase()));
121             if (filter != null) {
122                 return filter;
123             }
124         }
125         return new DBFilterKeyValuePair(property, value).getFilterExpression();
126     }
127
128     private static List<String> collectValues(String filtervalue, Set<String> filtervalues) {
129         if (filtervalues == null) {
130             return Arrays.asList(filtervalue);
131         }
132         List<String> values = new ArrayList<>();
133         if (filtervalue != null) {
134             values.add(filtervalue);
135         }
136         values.addAll(filtervalues);
137         return values;
138     }
139
140     private static String getComparisonFilter(String property, String filtervalue, boolean asTimeStamp) {
141         filtervalue = filtervalue.trim();
142         String comparator = null;
143         Object value;
144         if (filtervalue.startsWith(">=")) {
145             comparator = ">=";
146             filtervalue = filtervalue.substring(2).trim();
147             if (asTimeStamp) {
148                 filtervalue = netconfToMariaDBTimestamp(fillTimeStamp(
149                         filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"), MARIADB_TIMESTAMP_REPLACER));
150             }
151         } else if (filtervalue.startsWith(">")) {
152             comparator = ">";
153             filtervalue = filtervalue.substring(1).trim();
154             if (asTimeStamp) {
155                 if (isFullTimestamp(filtervalue)) {
156                     filtervalue = netconfToMariaDBTimestamp(filtervalue);
157                 } else {
158                     comparator = ">=";
159                     filtervalue = netconfToMariaDBTimestamp(
160                             fillTimeStamp(filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"),
161                                     NETCONF_TIMESTAMP_REPLACER, true));
162                 }
163             }
164         } else if (filtervalue.startsWith("<=")) {
165             comparator = "<=";
166             filtervalue = filtervalue.substring(2).trim();
167             if (asTimeStamp) {
168                 if (isFullTimestamp(filtervalue)) {
169                     filtervalue = netconfToMariaDBTimestamp(filtervalue);
170                 } else {
171                     comparator = "<";
172                     filtervalue = netconfToMariaDBTimestamp(
173                             fillTimeStamp(filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"),
174                                     NETCONF_TIMESTAMP_REPLACER, true));
175                 }
176             }
177         } else if (filtervalue.startsWith("<")) {
178             comparator = "<";
179             filtervalue = filtervalue.substring(1).trim();
180             if (asTimeStamp) {
181                 filtervalue = netconfToMariaDBTimestamp(fillTimeStamp(
182                         filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"), MARIADB_TIMESTAMP_REPLACER));
183             }
184         } else {
185             return null;
186         }
187         value = filtervalue;
188         return new RangeSqlDBFilter(property, value, comparator).getFilterExpression();
189     }
190
191     static boolean isFullTimestamp(String v) {
192         return v.length() >= MARIADB_TIMESTAMP_REPLACER_MIN_LENGTH;
193     }
194
195     /**
196      * Convert timestamp beginning filter expression like 2017* to a full qualified timestamp like '2017-01-01
197      * 00:00:00'.
198      *
199      * @param value filter input value
200      * @return fully qualified timestamp
201      */
202     private static String fillTimeStamp(String value) {
203         return fillTimeStamp(value, NETCONF_TIMESTAMP_REPLACER);
204     }
205
206     private static String fillTimeStamp(String value, String replacer) {
207         return fillTimeStamp(value, replacer, false);
208     }
209
210     private static String fillTimeStamp(String value, String replacer, boolean useUpperEnd) {
211         int idx = value.lastIndexOf("*");
212         String s = null;
213         if (idx > replacer.length()) {
214             s = value.substring(0, replacer.length());
215         } else {
216             s = value.substring(0, idx) + replacer.substring(idx);
217         }
218         //if month is zero => set to 1
219         if (Integer.parseInt(s.substring(5, 7)) == 0) {
220             s = s.substring(0, 5) + "01-" + s.substring(8);
221         }
222         //if day is zero => set to 1
223         if (Integer.parseInt(s.substring(8, 10)) == 0) {
224             s = s.substring(0, 8) + "01" + s.substring(10);
225         }
226         if (useUpperEnd) {
227             s = getTimestampUpperLimit(s, idx);
228         }
229         return s;
230     }
231
232     /**
233      * convert timestamp with ending placeholder in filter to elasticsearch filter e.g. 2017* => gte: 2017-01-01
234      * 00:00:00, lt:2018-01-01 00:00:00Z
235      *
236      * 201* => 2010-01... 2020 .. 2018-* => 2018-01... <=> 2019-01
237      *
238      */
239     private static @Nullable String fromTimestampSearchFilter(String property, String value) {
240         if (!value.endsWith("*")) {
241             return null;
242         }
243         int idx = value.lastIndexOf("*");
244         String lowerEnd = fillTimeStamp(value);
245         String upperEnd = getTimestampUpperLimit(fillTimeStamp(value, "0000-00-00T00:00:00.0Z"), idx);
246         return RangeSqlDBFilter.between(property, netconfToMariaDBTimestamp(lowerEnd), true,
247                 netconfToMariaDBTimestamp(upperEnd), false);
248     }
249
250     private static String netconfToMariaDBTimestamp(String ts) {
251         if (ts == null) {
252             return null;
253         }
254         String v = ts.replace("T", " ").replace("Z", "");
255         return v.length() > MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH
256                 ? v.substring(0, MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH)
257                 : v;
258     }
259
260     private static String getTimestampUpperLimit(String lowerEnd, int idx) {
261
262         String upperEnd = null;
263         NetconfTimeStamp converter = NetconfTimeStampImpl.getConverter();
264         Date dt = null;
265         try {
266             dt = converter.getDateFromNetconf(lowerEnd);
267         } catch (Exception e) {
268
269         }
270         if (dt == null) {
271             return null;
272         }
273
274         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
275         c.setTime(dt);
276         int tmpvalue;
277         switch (idx) {
278             case 1: // (2*)
279                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1000);
280                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
281                 break;
282             case 2: // (20*)
283                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 100);
284                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
285                 break;
286             case 3: // (200*)
287                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 10);
288                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
289                 break;
290             case 4: // (2000*)
291             case 5: // (2000-*)
292                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
293                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
294                 break;
295             case 6: // switch 10 months (2000-0* or 2000-1*)
296                 tmpvalue = c.get(Calendar.MONTH);
297                 if (tmpvalue < 9) {
298                     c.set(Calendar.MONTH, 9);
299                 } else {
300                     c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
301                     c.set(Calendar.MONTH, 0);
302                 }
303                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
304
305                 break;
306             case 7: // switch one month (2018-01* or 2018-01-*)
307             case 8:
308                 c.add(Calendar.MONTH, 1);
309                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
310                 break;
311             case 9: // (2018-01-0*)
312                 tmpvalue = c.get(Calendar.DAY_OF_MONTH);
313                 if (tmpvalue == 1) {
314                     c.set(Calendar.DAY_OF_MONTH, 10);
315                 } else if (tmpvalue == 10) {
316                     c.set(Calendar.DAY_OF_MONTH, 20);
317                 } else if (tmpvalue == 20) {
318                     if (c.getActualMaximum(Calendar.DAY_OF_MONTH) < 30) {
319                         c.set(Calendar.DAY_OF_MONTH, 1);
320                         c.add(Calendar.MONTH, 1);
321                     } else {
322                         c.set(Calendar.DAY_OF_MONTH, 30);
323                     }
324                 } else if (tmpvalue == 30) {
325                     c.set(Calendar.DAY_OF_MONTH, 1);
326                     c.add(Calendar.MONTH, 1);
327                 } else {
328                     break;
329                 }
330                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
331                 break;
332             case 10: // (2018-01-01*)
333             case 11: // (2018-01-01T*)
334                 c.add(Calendar.DAY_OF_MONTH, 1);
335                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
336                 break;
337             case 12: // (2018-01-01T1*)
338                 tmpvalue = c.get(Calendar.HOUR_OF_DAY);
339                 if (tmpvalue == 20) {
340                     c.set(Calendar.HOUR_OF_DAY, 0);
341                     c.add(Calendar.DAY_OF_MONTH, 1);
342                 } else {
343                     c.add(Calendar.HOUR_OF_DAY, 10);
344                 }
345                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
346                 break;
347             case 13: // (2018-01-01T11*)
348             case 14: // (2018-01-01T11-*)
349                 c.add(Calendar.HOUR_OF_DAY, 1);
350                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
351                 break;
352             case 15: // (2018-01-01T11-3*)
353                 c.add(Calendar.MINUTE, 10);
354                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
355                 break;
356             case 16: // (2018-01-01T11-32*)
357             case 17: // (2018-01-01T11-32-*)
358                 c.add(Calendar.MINUTE, 1);
359                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
360                 break;
361             case 18: // (2018-01-01T11-32-1*)
362                 c.add(Calendar.SECOND, 10);
363                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
364                 break;
365             case 19: // (2018-01-01T11-32-11*)
366             case 20: // (2018-01-01T11-32-11.*)
367                 c.add(Calendar.SECOND, 1);
368                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
369                 break;
370
371             default:
372                 break;
373         }
374         return upperEnd;
375     }
376
377
378     private static boolean isFilterEmpty(Filter filter) {
379         @Nullable Set<String> filtervalues = filter.getFiltervalues();
380         @Nullable String filtervalue = filter.getFiltervalue();
381
382         List<String> allValues = filtervalues == null ? new ArrayList<>() : new ArrayList<>(filtervalues);
383         if (filtervalue != null) {
384             allValues.add(filtervalue);
385         }
386
387         return allValues.isEmpty() || allValues.contains("*");
388     }
389
390
391 }