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