1
2
3
4
5
6
7
8
9
10
11 package org.astrogrid.tableserver.jdbc;
12
13 import java.io.FileInputStream;
14 import java.io.File;
15 import java.io.IOException;
16 import java.io.FileNotFoundException;
17 import java.io.InputStream;
18 import java.io.StringWriter;
19 import javax.xml.parsers.ParserConfigurationException;
20 import javax.xml.transform.Transformer;
21 import javax.xml.transform.TransformerConfigurationException;
22 import javax.xml.transform.TransformerException;
23 import javax.xml.transform.TransformerFactory;
24 import javax.xml.transform.dom.DOMSource;
25 import javax.xml.transform.stream.StreamResult;
26 import javax.xml.transform.stream.StreamSource;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.astrogrid.cfg.ConfigFactory;
30 import org.astrogrid.query.Query;
31 import org.astrogrid.query.QueryException;
32 import org.astrogrid.xml.DomHelper;
33 import org.w3c.dom.Document;
34 import org.w3c.dom.Element;
35 import org.xml.sax.SAXException;
36
37 /***
38 * A translator that extracts SQL from a query.
39 * This uses functionality supplied by the Query instance to translate itself
40 * to SQL, using a stylesheet specified by a DSA configuration property.
41 * The AdqlSqlMaker should therefore be a fairly generic RDBMS plugin,
42 * flavoured for particular systems by choice of stylesheet.
43 *
44 * For weird RDBMS SQL flavours (where extra tweaking is required that
45 * can't be done in XSLT), this class can be subclassed.
46 *
47 * @author M Hill
48 * @author K Andrews
49 */
50 public class AdqlSqlMaker implements SqlMaker {
51
52 private static final Log log = LogFactory.getLog(AdqlSqlMaker.class);
53
54 /***
55 * Constructs an SQL statement for the given Query. Uses the ADQL generator
56 * and XSLT style sheets - there may be a better way of doing this!
57 */
58 public String makeSql(Query query) throws QueryException {
59
60 String sql = doXsltTransformation(query);
61
62 return sql;
63 }
64
65 protected String doXsltTransformation(Query query) throws QueryException
66 {
67 InputStream xsltIn = null;
68 String key = "datacenter.sqlmaker.xslt";
69 String whereIsDoc = "";
70
71 String xsltDoc = ConfigFactory.getCommonConfig().getString(key, null);
72 if ((xsltDoc == null) || (xsltDoc.trim().equals(""))) {
73 throw new QueryException(
74 "Property 'datacenter.sqlmaker.xslt' is not configured,"+
75 " cannot translate to sql.");
76 }
77
78
79 log.debug("Trying to load XSLT as filesystem file: " + xsltDoc);
80 File file = new File(xsltDoc);
81 if (file.exists()) {
82 try {
83 xsltIn = new FileInputStream(xsltDoc);
84 }
85 catch (FileNotFoundException e) {
86 }
87 }
88
89 if (xsltIn == null) {
90
91 xsltIn = AdqlSqlMaker.class.getResourceAsStream("./xslt/"+xsltDoc);
92 whereIsDoc = AdqlSqlMaker.class+" resource ./xslt/"+xsltDoc;
93 log.debug("Trying to load XSLT as resource of class: " +whereIsDoc);
94 }
95
96
97 if (xsltIn == null) {
98
99
100
101
102
103
104
105
106
107 String path =
108 AdqlSqlMaker.class.getPackage().getName().replace('.', '/') +
109 "/xslt/" + xsltDoc;
110 xsltIn = AdqlSqlMaker.class.getClassLoader().getResourceAsStream(path);
111 log.debug("Trying to load XSLT via classloader : " +path);
112 }
113
114
115
116
117 if (xsltIn == null) {
118 log.warn("Could not find builtin ADQL->SQL transformer doc '"
119 +whereIsDoc+"', looking in classpath...");
120
121 xsltIn = this.getClass().getClassLoader().getResourceAsStream(
122 "xslt/"+xsltDoc);
123 whereIsDoc = "xslt/" + xsltDoc+" in classpath at "+
124 this.getClass().getClassLoader().getResource(xsltDoc);
125 log.debug("Trying to load XSLT via classpath : " +whereIsDoc);
126 }
127
128 if (xsltIn == null) {
129 throw new QueryException(
130 "Could not find ADQL->SQL transformer doc "+xsltDoc);
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 return query.convertWithXslt(xsltIn);
148 }
149
150 /***
151 * Constructs an SQL count statement for the given Query.
152 */
153
154 /***
155 * Constructs an SQL count statement for the given Query.
156 * @TOFIX KEA SAYS: This is a truly disgusting hack which won't
157 * work with ADQL queries with complex expression selections as
158 * aliased columns. It may also fail if "select" or "from" appears
159 * in unexpected places (e.g. as part of column / table names).
160 * However, this method is intended for administrative/testing
161 * use only (I'm going to remove it from the public Datacenter
162 * API) so I will leave it here for now.
163 * @deprecated Because it's horrible/fragile
164 */
165 public String makeCountSql(Query query) throws QueryException {
166
167
168 String sql = makeSql(query);
169 String lowercaseSql = sql.toLowerCase();
170
171
172
173 int selectIdx = lowercaseSql.indexOf("select");
174 int fromIdx = lowercaseSql.indexOf("from");
175 String countSql = sql.substring(0,selectIdx+6)+" COUNT(*) "+sql.substring(fromIdx);
176 return countSql;
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 }