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