8bde2dcde36ac7507c517e36919b30329e800a96
[controller.git] / opendaylight / md-sal / sal-dom-xsql / src / main / java / org / opendaylight / controller / md / sal / dom / xsql / jdbc / JDBCServer.java
1 /*
2  * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.md.sal.dom.xsql.jdbc;
10
11 import java.net.ServerSocket;
12 import java.net.Socket;
13 import java.sql.SQLException;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.StringTokenizer;
19 import java.util.concurrent.ConcurrentHashMap;
20 import org.opendaylight.controller.md.sal.dom.xsql.XSQLAdapter;
21 import org.opendaylight.controller.md.sal.dom.xsql.XSQLBluePrint;
22 import org.opendaylight.controller.md.sal.dom.xsql.XSQLBluePrintNode;
23 import org.opendaylight.controller.md.sal.dom.xsql.XSQLColumn;
24 import org.opendaylight.controller.md.sal.dom.xsql.XSQLCriteria;
25
26 public class JDBCServer extends Thread {
27     private ServerSocket socket = null;
28     private XSQLAdapter adapter = null;
29
30     public JDBCServer(XSQLAdapter a) {
31         super("JDBC Server");
32         this.adapter = a;
33         start();
34     }
35
36     public void run() {
37         try {
38             socket = new ServerSocket(40004);
39             while (!adapter.stopped) {
40                 Socket s = socket.accept();
41                 new JDBCConnection(s, adapter);
42             }
43         } catch (Exception err) {
44             err.printStackTrace();
45         }
46     }
47
48     public void connectToClient(String addr) {
49         try {
50             Socket s = new Socket(addr, 50003);
51             new JDBCConnection(s, adapter);
52         } catch (Exception err) {
53             err.printStackTrace();
54         }
55     }
56
57     public static void execute(JDBCResultSet rs, XSQLAdapter adapter)throws SQLException {
58         if(rs.getSQL().toLowerCase().trim().equals("select 1")){
59             rs.setFinished(true);
60             return;
61         }
62         checkAndBreakSubQueries(rs, adapter);
63         if (rs.getSubQueries().size() == 0) {
64             parseTables(rs, adapter.getBluePrint());
65             parseFields(rs, adapter.getBluePrint());
66             parseCriteria(rs, adapter.getBluePrint());
67             try {
68                 adapter.execute(rs);
69             } catch (Exception err) {
70                 throw new SQLException("Error", err);
71             }
72         } else {
73             parseExternalQuery(rs);
74         }
75     }
76
77     public static void parseExternalQuery(JDBCResultSet rs) throws SQLException {
78         String sql = rs.getSQL();
79         for (Map.Entry<String, JDBCResultSet> entry : rs.getSubQueries()
80                 .entrySet()) {
81             int index = sql.toLowerCase().indexOf(entry.getValue().getSQL());
82             String extSql = sql.substring(0, index);
83             index = extSql.lastIndexOf("(");
84             extSql = extSql.substring(0, index);
85             System.out.println("External SQL=" + extSql);
86             parseLogicalFields(extSql, rs);
87         }
88     }
89
90     public static void parseLogicalFields(String sql, JDBCResultSet rs)
91             throws SQLException {
92         if(sql.trim().toLowerCase().equals("select * from")){
93             for (Map.Entry<String, JDBCResultSet> entry : rs.getSubQueries().entrySet()) {
94                 for(XSQLBluePrintNode node:entry.getValue().getTables()){
95                     rs.addTableToQuery(node);
96                 }
97                 rs.getFields().addAll(entry.getValue().getFields());
98                 while (entry.getValue().next()) {
99                     Map<String, Object> rec = entry.getValue().getCurrent();
100                     Map<String, Object> newRec = new HashMap<>();
101                     newRec.putAll(rec);
102                     rs.addRecord(newRec);
103                 }
104             }
105             rs.setFinished(true);
106             return;
107         }
108
109         Map<String, XSQLBluePrintNode> logicalNameToNode = new HashMap<String, XSQLBluePrintNode>();
110         Map<String, String> origNameToName = new HashMap<String, String>();
111         List<XSQLColumn> columnOrder = new ArrayList<>();
112         int nextLogField = addNextLogicalField(sql, 0,
113                 logicalNameToNode, origNameToName,columnOrder);
114         int next = sql.toLowerCase().indexOf(" as ", nextLogField);
115         while (next != -1) {
116             nextLogField = addNextLogicalField(sql, nextLogField + 1,
117                     logicalNameToNode, origNameToName,columnOrder);
118             next = sql.toLowerCase().indexOf(" as ", nextLogField + 1);
119         }
120
121         for (XSQLBluePrintNode node : logicalNameToNode.values()) {
122             rs.addTableToQuery(node);
123         }
124         rs.getFields().addAll(columnOrder);
125         for (Map.Entry<String, JDBCResultSet> entry : rs.getSubQueries().entrySet()) {
126             while (entry.getValue().next()) {
127                 Map<String, Object> rec = entry.getValue().getCurrent();
128                 Map<String, Object> newRec = new HashMap<>();
129                 for (Map.Entry<String, Object> e : rec.entrySet()) {
130                     Object value = e.getValue();
131                     String logicalKey = origNameToName.get(e.getKey());
132                     if (value != null && logicalKey != null) {
133                         newRec.put(logicalKey, value);
134                     }
135                 }
136                 rs.addRecord(newRec);
137             }
138         }
139         rs.setFinished(true);
140     }
141
142     public static void main(String args[]) {
143         String sql = "SELECT DISTINCT"
144                 + "\"LOGICAL_TABLE_1\".\"nodes/node.id\" AS \"COL0\"\n"
145                 + ",\"LOGICAL_TABLE_1\".\"nodes/node.id\" AS \"COL1\"\n"
146                 + ",\"LOGICAL_TABLE_1\".\"nodes/node.id\" AS \"COL2\"\n"
147                 + "FROM\n"
148                 + "(select * from nodes/node;) \"LOGICAL_TABLE_1\"\n";
149         JDBCResultSet rs = new JDBCResultSet(sql);
150         try {
151             parseLogicalFields(sql, rs);
152         } catch (Exception err) {
153             err.printStackTrace();
154         }
155     }
156
157     public static int addNextLogicalField(String sql, int startIndex,
158             Map<String, XSQLBluePrintNode> logicalNameToNode,
159             Map<String, String> origNameToName, List<XSQLColumn> columnOrder) {
160         int index1 = sql.indexOf("\"", startIndex);
161         int index2 = sql.indexOf("\".\"", index1);
162         int index3 = sql.indexOf("\"", index2 + 3);
163         int index4 = sql.toLowerCase().indexOf(" as ", startIndex);
164         int index5 = sql.indexOf("\"", index4);
165         int index6 = sql.indexOf("\"", index5 + 1);
166
167         String tblName = sql.substring(index1 + 1, index2);
168         String origFieldNameFull = sql.substring(index2 + 3, index3);
169         String origTableName = "";
170         String origFieldName = "";
171         if (origFieldNameFull.indexOf(".") != -1) {
172             origTableName = origFieldNameFull.substring(0,origFieldNameFull.indexOf("."));
173             origFieldName = origFieldNameFull.substring(origFieldNameFull.indexOf(".") + 1);
174         }
175         String logicalFieldName = sql.substring(index5 + 1, index6);
176         XSQLBluePrintNode node = logicalNameToNode.get(tblName);
177         if (node == null) {
178             node = new XSQLBluePrintNode(tblName, origTableName, 0);
179             logicalNameToNode.put(tblName, node);
180         }
181         columnOrder.add(node.addColumn(logicalFieldName, tblName, origFieldName, origTableName));
182         origNameToName.put(origFieldNameFull, tblName + "." + logicalFieldName);
183         return index6;
184     }
185
186     public static void checkAndBreakSubQueries(JDBCResultSet rs,XSQLAdapter adapter) throws SQLException {
187         String sql = rs.getSQL().toLowerCase();
188         int index = sql.indexOf("select");
189         if (index == -1)
190             throw new SQLException("Select statement is missing...");
191         int index2 = sql.indexOf("select", index + 6);
192         if (index2 != -1) {
193             int startSubQuery = index2;
194             for (int i = startSubQuery; i >= 0; i--) {
195                 if (sql.charAt(i) == '(') {
196                     startSubQuery = i;
197                     break;
198                 }
199             }
200             int braketCount = 0;
201             int endSubQuery = startSubQuery;
202             do {
203                 if (sql.charAt(endSubQuery) == '(') {
204                     braketCount++;
205                 }
206                 else if (sql.charAt(endSubQuery) == ')') {
207                     braketCount--;
208                 }
209                 endSubQuery++;
210             } while (braketCount > 0 || endSubQuery == sql.length());
211             String subQuerySQL = sql.substring(startSubQuery + 1,endSubQuery - 1);
212             if(rs.getSQL().toLowerCase().substring(0,startSubQuery).trim().equals("select * from")){
213                 rs.setSQL(subQuerySQL);
214                 return;
215             }
216             index = sql.indexOf("\"", endSubQuery);
217             index2 = sql.indexOf("\"", index + 1);
218             if(index==-1){
219                 index = endSubQuery;
220                 index2 = sql.length();
221             }
222             String logicalName = rs.getSQL().substring(index + 1, index2).trim();
223             JDBCResultSet subRS = rs.addSubQuery(subQuerySQL, logicalName);
224             JDBCServer.execute(subRS, adapter);
225         }
226     }
227
228     public static void parseTables(JDBCResultSet rs, XSQLBluePrint bp)
229             throws SQLException {
230         String lowSQL = rs.getSQL().toLowerCase();
231         int from = lowSQL.indexOf("from");
232         int where = lowSQL.indexOf("where");
233         int subQuery = lowSQL.indexOf("select", 2);
234         int fromTo = lowSQL.indexOf(";");
235
236         if (where != -1 && subQuery != -1 && where < subQuery) {
237             fromTo = where;
238         } else if (where != -1 && subQuery != -1 && where > subQuery) {
239             fromTo = subQuery;
240         } else if (where != -1) {
241             fromTo = where;
242         } else if (subQuery != -1) {
243             fromTo = subQuery;
244         }
245
246         if (from == -1) {
247             throw new SQLException("Missing \"from\" statement.");
248         }
249
250         if (fromTo == -1) {
251             throw new SQLException("Missing terminating \";\".");
252         }
253
254         String tableNames = rs.getSQL().substring(from + 4, fromTo).trim();
255         StringTokenizer tokens = new StringTokenizer(tableNames, ",");
256         while (tokens.hasMoreTokens()) {
257             String tableName = tokens.nextToken().trim();
258             XSQLBluePrintNode table = bp.getBluePrintNodeByTableName(tableName);
259             if (table == null) {
260                 throw new SQLException("Unknown table name \"" + tableName
261                         + "\"");
262             }
263             rs.addTableToQuery(table);
264         }
265     }
266
267     public static void addCriteria(XSQLColumn col, XSQLCriteria c,
268             JDBCResultSet rs) {
269         Map<XSQLColumn, List<XSQLCriteria>> tblCriteria = rs.getCriteria().get(
270                 col.getTableName());
271         if (tblCriteria == null) {
272             tblCriteria = new ConcurrentHashMap<XSQLColumn, List<XSQLCriteria>>();
273             rs.getCriteria().put(col.getTableName(), tblCriteria);
274         }
275         List<XSQLCriteria> lstCriteria = tblCriteria.get(col);
276         if (lstCriteria == null) {
277             lstCriteria = new ArrayList<XSQLCriteria>();
278             tblCriteria.put(col, lstCriteria);
279         }
280         lstCriteria.add(c);
281     }
282
283     public static void parseFields(JDBCResultSet rs, XSQLBluePrint bp)
284             throws SQLException {
285         String lowSQL = rs.getSQL().toLowerCase();
286         if (!lowSQL.startsWith("select")) {
287             throw new SQLException("Missing 'select' statement.");
288         }
289         int from = lowSQL.indexOf("from");
290         if (from == -1) {
291             throw new SQLException("Missing 'from' statement.");
292         }
293         String fields = rs.getSQL().substring(6, from).trim();
294         StringTokenizer tokens = new StringTokenizer(fields, ",");
295         while (tokens.hasMoreTokens()) {
296             String token = tokens.nextToken().trim();
297             if (token.equals("*")) {
298                 for (XSQLBluePrintNode table : rs.getTables()) {
299                     rs.getFields().addAll(table.getColumns());
300                 }
301                 return;
302             }
303             if (token.indexOf(".") != -1) {
304                 XSQLBluePrintNode tbl = bp.getBluePrintNodeByTableName(token
305                         .substring(0, token.indexOf(".")).trim());
306                 String p = token.substring(token.indexOf(".") + 1);
307                 if (p.equals("*")) {
308                     for (XSQLColumn c : tbl.getColumns()) {
309                         rs.getFields().add(c);
310                     }
311                 } else {
312                     XSQLColumn col = tbl.findColumnByName(p);
313                     rs.getFields().add(col);
314                 }
315             } else {
316                 XSQLColumn col = null;
317                 for (XSQLBluePrintNode table : rs.getTables()) {
318                     try {
319                         col = table.findColumnByName(token);
320                     } catch (Exception err) {
321                     }
322                     if (col != null) {
323                         break;
324                     }
325                 }
326                 if (col == null) {
327                     throw new SQLException("Unknown field name '" + token
328                             + "'.");
329                 }
330
331                 rs.getFields().add(col);
332             }
333         }
334     }
335
336     public static void parseCriteria(JDBCResultSet rs, XSQLBluePrint bp) {
337         String lowSQL = rs.getSQL().toLowerCase();
338         int where = lowSQL.indexOf("where");
339         int order = lowSQL.indexOf("order");
340         int whereTo = lowSQL.indexOf(";");
341
342         if (where == -1) {
343             return;
344         }
345
346         if (order != -1) {
347             whereTo = order;
348         }
349
350         if(whereTo==-1) {
351             whereTo=lowSQL.length();
352         }
353
354         String whereStatement = rs.getSQL().substring(where + 5, whereTo)
355                 .trim();
356         XSQLCriteria cr = new XSQLCriteria(whereStatement, -1);
357         for (XSQLBluePrintNode tbl : rs.getTables()) {
358             for (XSQLColumn col : tbl.getColumns()) {
359                 String colCriteria = cr.getCriteriaForProperty(col);
360                 if (colCriteria != null && !colCriteria.trim().equals("")) {
361                     addCriteria(col, new XSQLCriteria(colCriteria, -1), rs);
362                 }
363             }
364         }
365     }
366 }