~pbms-core/pbms/5.11-beta

« back to all changes in this revision

Viewing changes to mybs/java/src/com/mysql/jdbc/EscapeProcessor.java

  • Committer: paul-mccullagh
  • Date: 2008-03-26 11:35:17 UTC
  • Revision ID: paul-mccullagh-afb1610c21464a577ae428d72fc725eb986c05a5
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 Copyright (C) 2002-2007 MySQL AB
 
3
 
 
4
 This program is free software; you can redistribute it and/or modify
 
5
 it under the terms of version 2 of the GNU General Public License as 
 
6
 published by the Free Software Foundation.
 
7
 
 
8
 There are special exceptions to the terms and conditions of the GPL 
 
9
 as it is applied to this software. View the full text of the 
 
10
 exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
 
11
 software distribution.
 
12
 
 
13
 This program is distributed in the hope that it will be useful,
 
14
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 GNU General Public License for more details.
 
17
 
 
18
 You should have received a copy of the GNU General Public License
 
19
 along with this program; if not, write to the Free Software
 
20
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
21
 
 
22
 
 
23
 
 
24
 */
 
25
 
 
26
/**
 
27
 * EscapeProcessor performs all escape code processing as outlined in the JDBC
 
28
 * spec by JavaSoft.
 
29
 */
 
30
package com.mysql.jdbc;
 
31
 
 
32
import java.sql.SQLException;
 
33
import java.sql.Time;
 
34
import java.sql.Timestamp;
 
35
 
 
36
import java.util.Calendar;
 
37
import java.util.Collections;
 
38
import java.util.GregorianCalendar;
 
39
import java.util.HashMap;
 
40
import java.util.Locale;
 
41
import java.util.Map;
 
42
import java.util.StringTokenizer;
 
43
import java.util.TimeZone;
 
44
 
 
45
class EscapeProcessor {
 
46
        private static Map JDBC_CONVERT_TO_MYSQL_TYPE_MAP;
 
47
 
 
48
        private static Map JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP;
 
49
 
 
50
        static {
 
51
                Map tempMap = new HashMap();
 
52
 
 
53
                tempMap.put("BIGINT", "0 + ?");
 
54
                tempMap.put("BINARY", "BINARY");
 
55
                tempMap.put("BIT", "0 + ?");
 
56
                tempMap.put("CHAR", "CHAR");
 
57
                tempMap.put("DATE", "DATE");
 
58
                tempMap.put("DECIMAL", "0.0 + ?");
 
59
                tempMap.put("DOUBLE", "0.0 + ?");
 
60
                tempMap.put("FLOAT", "0.0 + ?");
 
61
                tempMap.put("INTEGER", "0 + ?");
 
62
                tempMap.put("LONGVARBINARY", "BINARY");
 
63
                tempMap.put("LONGVARCHAR", "CONCAT(?)");
 
64
                tempMap.put("REAL", "0.0 + ?");
 
65
                tempMap.put("SMALLINT", "CONCAT(?)");
 
66
                tempMap.put("TIME", "TIME");
 
67
                tempMap.put("TIMESTAMP", "DATETIME");
 
68
                tempMap.put("TINYINT", "CONCAT(?)");
 
69
                tempMap.put("VARBINARY", "BINARY");
 
70
                tempMap.put("VARCHAR", "CONCAT(?)");
 
71
 
 
72
                JDBC_CONVERT_TO_MYSQL_TYPE_MAP = Collections.unmodifiableMap(tempMap);
 
73
 
 
74
                tempMap = new HashMap(JDBC_CONVERT_TO_MYSQL_TYPE_MAP);
 
75
 
 
76
                tempMap.put("BINARY", "CONCAT(?)");
 
77
                tempMap.put("CHAR", "CONCAT(?)");
 
78
                tempMap.remove("DATE");
 
79
                tempMap.put("LONGVARBINARY", "CONCAT(?)");
 
80
                tempMap.remove("TIME");
 
81
                tempMap.remove("TIMESTAMP");
 
82
                tempMap.put("VARBINARY", "CONCAT(?)");
 
83
 
 
84
                JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP = Collections
 
85
                                .unmodifiableMap(tempMap);
 
86
 
 
87
        }
 
88
 
 
89
        /**
 
90
         * Escape process one string
 
91
         * 
 
92
         * @param sql
 
93
         *            the SQL to escape process.
 
94
         * 
 
95
         * @return the SQL after it has been escape processed.
 
96
         * 
 
97
         * @throws java.sql.SQLException
 
98
         *             DOCUMENT ME!
 
99
         * @throws SQLException
 
100
         *             DOCUMENT ME!
 
101
         */
 
102
        public static final Object escapeSQL(String sql,
 
103
                        boolean serverSupportsConvertFn, 
 
104
                        Connection conn) throws java.sql.SQLException {
 
105
                boolean replaceEscapeSequence = false;
 
106
                String escapeSequence = null;
 
107
 
 
108
                if (sql == null) {
 
109
                        return null;
 
110
                }
 
111
 
 
112
                /*
 
113
                 * Short circuit this code if we don't have a matching pair of "{}". -
 
114
                 * Suggested by Ryan Gustafason
 
115
                 */
 
116
                int beginBrace = sql.indexOf('{');
 
117
                int nextEndBrace = (beginBrace == -1) ? (-1) : sql.indexOf('}',
 
118
                                beginBrace);
 
119
 
 
120
                if (nextEndBrace == -1) {
 
121
                        return sql;
 
122
                }
 
123
 
 
124
                StringBuffer newSql = new StringBuffer();
 
125
 
 
126
                EscapeTokenizer escapeTokenizer = new EscapeTokenizer(sql);
 
127
 
 
128
                byte usesVariables = Statement.USES_VARIABLES_FALSE;
 
129
                boolean callingStoredFunction = false;
 
130
 
 
131
                while (escapeTokenizer.hasMoreTokens()) {
 
132
                        String token = escapeTokenizer.nextToken();
 
133
 
 
134
                        if (token.length() != 0) {
 
135
                                if (token.charAt(0) == '{') { // It's an escape code
 
136
 
 
137
                                        if (!token.endsWith("}")) {
 
138
                                                throw SQLError.createSQLException("Not a valid escape sequence: "
 
139
                                                                + token);
 
140
                                        }
 
141
 
 
142
                                        if (token.length() > 2) {
 
143
                                                int nestedBrace = token.indexOf('{', 2);
 
144
 
 
145
                                                if (nestedBrace != -1) {
 
146
                                                        StringBuffer buf = new StringBuffer(token
 
147
                                                                        .substring(0, 1));
 
148
 
 
149
                                                        Object remainingResults = escapeSQL(token
 
150
                                                                        .substring(1, token.length() - 1),
 
151
                                                                        serverSupportsConvertFn, conn);
 
152
 
 
153
                                                        String remaining = null;
 
154
 
 
155
                                                        if (remainingResults instanceof String) {
 
156
                                                                remaining = (String) remainingResults;
 
157
                                                        } else {
 
158
                                                                remaining = ((EscapeProcessorResult) remainingResults).escapedSql;
 
159
 
 
160
                                                                if (usesVariables != Statement.USES_VARIABLES_TRUE) {
 
161
                                                                        usesVariables = ((EscapeProcessorResult) remainingResults).usesVariables;
 
162
                                                                }
 
163
                                                        }
 
164
 
 
165
                                                        buf.append(remaining);
 
166
 
 
167
                                                        buf.append('}');
 
168
 
 
169
                                                        token = buf.toString();
 
170
                                                }
 
171
                                        }
 
172
 
 
173
                                        // nested escape code
 
174
                                        // Compare to tokens with _no_ whitespace
 
175
                                        String collapsedToken = removeWhitespace(token);
 
176
 
 
177
                                        /*
 
178
                                         * Process the escape code
 
179
                                         */
 
180
                                        if (StringUtils.startsWithIgnoreCase(collapsedToken,
 
181
                                                        "{escape")) {
 
182
                                                try {
 
183
                                                        StringTokenizer st = new StringTokenizer(token,
 
184
                                                                        " '");
 
185
                                                        st.nextToken(); // eat the "escape" token
 
186
                                                        escapeSequence = st.nextToken();
 
187
 
 
188
                                                        if (escapeSequence.length() < 3) {
 
189
                                                                newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
 
190
                                                        } else {
 
191
                                                        
 
192
 
 
193
                                                                escapeSequence = escapeSequence.substring(1,
 
194
                                                                        escapeSequence.length() - 1);
 
195
                                                                replaceEscapeSequence = true;
 
196
                                                        }
 
197
                                                } catch (java.util.NoSuchElementException e) {
 
198
                                                        newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
 
199
                                                }
 
200
                                        } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 
201
                                                        "{fn")) {
 
202
                                                int startPos = token.toLowerCase().indexOf("fn ") + 3;
 
203
                                                int endPos = token.length() - 1; // no }
 
204
 
 
205
                                                String fnToken = token.substring(startPos, endPos);
 
206
 
 
207
                                                // We need to handle 'convert' by ourselves
 
208
 
 
209
                                                if (StringUtils.startsWithIgnoreCaseAndWs(fnToken,
 
210
                                                                "convert")) {
 
211
                                                        newSql.append(processConvertToken(fnToken,
 
212
                                                                        serverSupportsConvertFn));
 
213
                                                } else {
 
214
                                                        // just pass functions right to the DB
 
215
                                                        newSql.append(fnToken);
 
216
                                                }
 
217
                                        } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 
218
                                                        "{d")) {
 
219
                                                int startPos = token.indexOf('\'') + 1;
 
220
                                                int endPos = token.lastIndexOf('\''); // no }
 
221
 
 
222
                                                if ((startPos == -1) || (endPos == -1)) {
 
223
                                                        newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
 
224
                                                } else {
 
225
        
 
226
                                                        String argument = token.substring(startPos, endPos);
 
227
        
 
228
                                                        try {
 
229
                                                                StringTokenizer st = new StringTokenizer(argument,
 
230
                                                                                " -");
 
231
                                                                String year4 = st.nextToken();
 
232
                                                                String month2 = st.nextToken();
 
233
                                                                String day2 = st.nextToken();
 
234
                                                                String dateString = "'" + year4 + "-" + month2
 
235
                                                                                + "-" + day2 + "'";
 
236
                                                                newSql.append(dateString);
 
237
                                                        } catch (java.util.NoSuchElementException e) {
 
238
                                                                throw SQLError.createSQLException(
 
239
                                                                                "Syntax error for DATE escape sequence '"
 
240
                                                                                                + argument + "'", "42000");
 
241
                                                        }
 
242
                                                }
 
243
                                        } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 
244
                                                        "{ts")) {
 
245
                                                int startPos = token.indexOf('\'') + 1;
 
246
                                                int endPos = token.lastIndexOf('\''); // no }
 
247
 
 
248
                                                if ((startPos == -1) || (endPos == -1)) {
 
249
                                                        newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
 
250
                                                } else {
 
251
 
 
252
                                                        String argument = token.substring(startPos, endPos);
 
253
        
 
254
                                                        try {
 
255
                                                                StringTokenizer st = new StringTokenizer(argument,
 
256
                                                                                " .-:");
 
257
                                                                String year4 = st.nextToken();
 
258
                                                                String month2 = st.nextToken();
 
259
                                                                String day2 = st.nextToken();
 
260
                                                                String hour = st.nextToken();
 
261
                                                                String minute = st.nextToken();
 
262
                                                                String second = st.nextToken();
 
263
        
 
264
                                                                /*
 
265
                                                                 * For now, we get the fractional seconds part, but
 
266
                                                                 * we don't use it, as MySQL doesn't support it in
 
267
                                                                 * it's TIMESTAMP data type
 
268
                                                                 * 
 
269
                                                                 * String fractionalSecond = "";
 
270
                                                                 * 
 
271
                                                                 * if (st.hasMoreTokens()) { fractionalSecond =
 
272
                                                                 * st.nextToken(); }
 
273
                                                                 */
 
274
                                                                /*
 
275
                                                                 * Use the full format because number format will
 
276
                                                                 * not work for "between" clauses.
 
277
                                                                 * 
 
278
                                                                 * Ref. Mysql Docs
 
279
                                                                 * 
 
280
                                                                 * You can specify DATETIME, DATE and TIMESTAMP
 
281
                                                                 * values using any of a common set of formats:
 
282
                                                                 * 
 
283
                                                                 * As a string in either 'YYYY-MM-DD HH:MM:SS' or
 
284
                                                                 * 'YY-MM-DD HH:MM:SS' format.
 
285
                                                                 * 
 
286
                                                                 * Thanks to Craig Longman for pointing out this bug
 
287
                                                                 */
 
288
                                                                if (!conn.getUseTimezone() && !conn.getUseJDBCCompliantTimezoneShift()) {
 
289
                                                                        newSql.append("'").append(year4).append("-")
 
290
                                                                                .append(month2).append("-").append(day2)
 
291
                                                                                .append(" ").append(hour).append(":")
 
292
                                                                                .append(minute).append(":").append(second)
 
293
                                                                                .append("'");
 
294
                                                                } else {
 
295
                                                                        Calendar sessionCalendar;
 
296
                                                                        
 
297
                                                                        if (conn != null) {
 
298
                                                                                sessionCalendar = conn.getCalendarInstanceForSessionOrNew();
 
299
                                                                        } else {
 
300
                                                                                sessionCalendar = new GregorianCalendar();
 
301
                                                                                sessionCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
 
302
                                                                        }
 
303
                                                                        
 
304
                                                                        try {
 
305
                                                                                int year4Int = Integer.parseInt(year4);
 
306
                                                                                int month2Int = Integer.parseInt(month2);
 
307
                                                                                int day2Int = Integer.parseInt(day2);
 
308
                                                                                int hourInt = Integer.parseInt(hour);
 
309
                                                                                int minuteInt = Integer.parseInt(minute);
 
310
                                                                                int secondInt = Integer.parseInt(second);
 
311
                                                                                
 
312
                                                                                synchronized (sessionCalendar) {
 
313
                                                                                        boolean useGmtMillis = conn.getUseGmtMillisForDatetimes();
 
314
                                                                                        
 
315
                                                                                        Timestamp toBeAdjusted = TimeUtil.fastTimestampCreate(useGmtMillis,
 
316
                                                                                                        useGmtMillis ? Calendar.getInstance(TimeZone.getTimeZone("GMT")): null,
 
317
                                                                                                sessionCalendar,
 
318
                                                                                                year4Int,
 
319
                                                                                                month2Int,
 
320
                                                                                                day2Int,
 
321
                                                                                                hourInt,
 
322
                                                                                                minuteInt,
 
323
                                                                                                secondInt,
 
324
                                                                                                0);
 
325
                                                                                
 
326
                                                                                        Timestamp inServerTimezone = TimeUtil.changeTimezone(
 
327
                                                                                                        conn,
 
328
                                                                                                        sessionCalendar,
 
329
                                                                                                        null,
 
330
                                                                                                        toBeAdjusted,
 
331
                                                                                                        sessionCalendar.getTimeZone(),
 
332
                                                                                                        conn.getServerTimezoneTZ(),
 
333
                                                                                                        false);
 
334
                                                                                        
 
335
                                                                                        
 
336
                                                                                        newSql.append("'");
 
337
                                                                                        
 
338
                                                                                        String timezoneLiteral = inServerTimezone.toString();
 
339
                                                                                        
 
340
                                                                                        int indexOfDot = timezoneLiteral.indexOf(".");
 
341
                                                                                        
 
342
                                                                                        if (indexOfDot != -1) {
 
343
                                                                                                timezoneLiteral = timezoneLiteral.substring(0, indexOfDot);
 
344
                                                                                        }
 
345
                                                                                        
 
346
                                                                                        newSql.append(timezoneLiteral);
 
347
                                                                                }
 
348
                                                                                
 
349
                                                                                newSql.append("'");     
 
350
                                                                                
 
351
                                                                        
 
352
                                                                        } catch (NumberFormatException nfe) {
 
353
                                                                                throw SQLError.createSQLException("Syntax error in TIMESTAMP escape sequence '" 
 
354
                                                                                        + token + "'.",
 
355
                                                                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 
356
                                                                        }
 
357
                                                                }
 
358
                                                        } catch (java.util.NoSuchElementException e) {
 
359
                                                                throw SQLError.createSQLException(
 
360
                                                                                "Syntax error for TIMESTAMP escape sequence '"
 
361
                                                                                                + argument + "'", "42000");
 
362
                                                        }
 
363
                                                }
 
364
                                        } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 
365
                                                        "{t")) {
 
366
                                                int startPos = token.indexOf('\'') + 1;
 
367
                                                int endPos = token.lastIndexOf('\''); // no }
 
368
 
 
369
                                                if ((startPos == -1) || (endPos == -1)) {
 
370
                                                        newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
 
371
                                                } else {
 
372
 
 
373
                                                        String argument = token.substring(startPos, endPos);
 
374
        
 
375
                                                        try {
 
376
                                                                StringTokenizer st = new StringTokenizer(argument,
 
377
                                                                                " :");
 
378
                                                                String hour = st.nextToken();
 
379
                                                                String minute = st.nextToken();
 
380
                                                                String second = st.nextToken();
 
381
                                                                
 
382
                                                                if (!conn.getUseTimezone()) {
 
383
                                                                        String timeString = "'" + hour + ":" + minute + ":"
 
384
                                                                                + second + "'";
 
385
                                                                        newSql.append(timeString);
 
386
                                                                } else {
 
387
                                                                        Calendar sessionCalendar = null;
 
388
                                                                        
 
389
                                                                        if (conn != null) {
 
390
                                                                                sessionCalendar = conn.getCalendarInstanceForSessionOrNew();
 
391
                                                                        } else {
 
392
                                                                                sessionCalendar = new GregorianCalendar();
 
393
                                                                        }
 
394
        
 
395
                                                                        try {
 
396
                                                                                int hourInt = Integer.parseInt(hour);
 
397
                                                                                int minuteInt = Integer.parseInt(minute);
 
398
                                                                                int secondInt = Integer.parseInt(second);
 
399
                                                                                
 
400
                                                                                synchronized (sessionCalendar) {
 
401
                                                                                        Time toBeAdjusted = TimeUtil.fastTimeCreate(
 
402
                                                                                                        sessionCalendar,
 
403
                                                                                                        hourInt,
 
404
                                                                                                        minuteInt,
 
405
                                                                                                        secondInt);
 
406
                                                                                        
 
407
                                                                                        Time inServerTimezone = TimeUtil.changeTimezone(
 
408
                                                                                                        conn,
 
409
                                                                                                        sessionCalendar,
 
410
                                                                                                        null,
 
411
                                                                                                        toBeAdjusted,
 
412
                                                                                                        sessionCalendar.getTimeZone(),
 
413
                                                                                                        conn.getServerTimezoneTZ(),
 
414
                                                                                                        false);
 
415
                                                                                        
 
416
                                                                                        newSql.append("'");
 
417
                                                                                        newSql.append(inServerTimezone.toString());
 
418
                                                                                        newSql.append("'");             
 
419
                                                                                }
 
420
                                                                        
 
421
                                                                        } catch (NumberFormatException nfe) {
 
422
                                                                                throw SQLError.createSQLException("Syntax error in TIMESTAMP escape sequence '" 
 
423
                                                                                        + token + "'.",
 
424
                                                                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 
425
                                                                        }
 
426
                                                                }
 
427
                                                        } catch (java.util.NoSuchElementException e) {
 
428
                                                                throw SQLError.createSQLException(
 
429
                                                                                "Syntax error for escape sequence '"
 
430
                                                                                                + argument + "'", "42000");
 
431
                                                        }
 
432
                                                }
 
433
                                        } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 
434
                                                        "{call")
 
435
                                                        || StringUtils.startsWithIgnoreCase(collapsedToken,
 
436
                                                                        "{?=call")) {
 
437
 
 
438
                                                int startPos = StringUtils.indexOfIgnoreCase(token,
 
439
                                                                "CALL") + 5;
 
440
                                                int endPos = token.length() - 1;
 
441
                
 
442
                                                if (StringUtils.startsWithIgnoreCase(collapsedToken,
 
443
                                                                "{?=call")) {
 
444
                                                        callingStoredFunction = true;
 
445
                                                        newSql.append("SELECT ");
 
446
                                                        newSql.append(token.substring(startPos, endPos));
 
447
                                                } else {
 
448
                                                        callingStoredFunction = false;
 
449
                                                        newSql.append("CALL ");
 
450
                                                        newSql.append(token.substring(startPos, endPos));
 
451
                                                }
 
452
                                                
 
453
                                                for (int i = endPos - 1; i >= startPos; i--) {
 
454
                                                        char c = token.charAt(i);
 
455
                                                        
 
456
                                                        if (Character.isWhitespace(c)) {
 
457
                                                                continue;
 
458
                                                        }
 
459
                                                        
 
460
                                                        if (c != ')') {
 
461
                                                                newSql.append("()");  // handle no-parenthesis no-arg call not supported
 
462
                                                                 // by MySQL parser
 
463
                                                        }
 
464
                                                        
 
465
                                                        break;
 
466
                                                }
 
467
                                        } else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 
468
                                                        "{oj")) {
 
469
                                                // MySQL already handles this escape sequence
 
470
                                                // because of ODBC. Cool.
 
471
                                                newSql.append(token);
 
472
                                        }
 
473
                                } else {
 
474
                                        newSql.append(token); // it's just part of the query
 
475
                                }
 
476
                        }
 
477
                }
 
478
 
 
479
                String escapedSql = newSql.toString();
 
480
 
 
481
                //
 
482
                // FIXME: Let MySQL do this, however requires
 
483
                // lightweight parsing of statement
 
484
                //
 
485
                if (replaceEscapeSequence) {
 
486
                        String currentSql = escapedSql;
 
487
 
 
488
                        while (currentSql.indexOf(escapeSequence) != -1) {
 
489
                                int escapePos = currentSql.indexOf(escapeSequence);
 
490
                                String lhs = currentSql.substring(0, escapePos);
 
491
                                String rhs = currentSql.substring(escapePos + 1, currentSql
 
492
                                                .length());
 
493
                                currentSql = lhs + "\\" + rhs;
 
494
                        }
 
495
 
 
496
                        escapedSql = currentSql;
 
497
                }
 
498
 
 
499
                EscapeProcessorResult epr = new EscapeProcessorResult();
 
500
                epr.escapedSql = escapedSql;
 
501
                epr.callingStoredFunction = callingStoredFunction;
 
502
 
 
503
                if (usesVariables != Statement.USES_VARIABLES_TRUE) {
 
504
                        if (escapeTokenizer.sawVariableUse()) {
 
505
                                epr.usesVariables = Statement.USES_VARIABLES_TRUE;
 
506
                        } else {
 
507
                                epr.usesVariables = Statement.USES_VARIABLES_FALSE;
 
508
                        }
 
509
                }
 
510
 
 
511
                return epr;
 
512
        }
 
513
 
 
514
        /**
 
515
         * Re-writes {fn convert (expr, type)} as cast(expr AS type)
 
516
         * 
 
517
         * @param functionToken
 
518
         * @return
 
519
         * @throws SQLException
 
520
         */
 
521
        private static String processConvertToken(String functionToken,
 
522
                        boolean serverSupportsConvertFn) throws SQLException {
 
523
                // The JDBC spec requires these types:
 
524
                //
 
525
                // BIGINT
 
526
                // BINARY
 
527
                // BIT
 
528
                // CHAR
 
529
                // DATE
 
530
                // DECIMAL
 
531
                // DOUBLE
 
532
                // FLOAT
 
533
                // INTEGER
 
534
                // LONGVARBINARY
 
535
                // LONGVARCHAR
 
536
                // REAL
 
537
                // SMALLINT
 
538
                // TIME
 
539
                // TIMESTAMP
 
540
                // TINYINT
 
541
                // VARBINARY
 
542
                // VARCHAR
 
543
 
 
544
                // MySQL supports these types:
 
545
                //
 
546
                // BINARY
 
547
                // CHAR
 
548
                // DATE
 
549
                // DATETIME
 
550
                // SIGNED (integer)
 
551
                // UNSIGNED (integer)
 
552
                // TIME
 
553
 
 
554
                int firstIndexOfParen = functionToken.indexOf("(");
 
555
 
 
556
                if (firstIndexOfParen == -1) {
 
557
                        throw SQLError.createSQLException(
 
558
                                        "Syntax error while processing {fn convert (... , ...)} token, missing opening parenthesis in token '"
 
559
                                                        + functionToken + "'.",
 
560
                                        SQLError.SQL_STATE_SYNTAX_ERROR);
 
561
                }
 
562
 
 
563
                int tokenLength = functionToken.length();
 
564
 
 
565
                int indexOfComma = functionToken.lastIndexOf(",");
 
566
 
 
567
                if (indexOfComma == -1) {
 
568
                        throw SQLError.createSQLException(
 
569
                                        "Syntax error while processing {fn convert (... , ...)} token, missing comma in token '"
 
570
                                                        + functionToken + "'.",
 
571
                                        SQLError.SQL_STATE_SYNTAX_ERROR);
 
572
                }
 
573
 
 
574
                int indexOfCloseParen = functionToken.indexOf(')', indexOfComma);
 
575
 
 
576
                if (indexOfCloseParen == -1) {
 
577
                        throw SQLError.createSQLException(
 
578
                                        "Syntax error while processing {fn convert (... , ...)} token, missing closing parenthesis in token '"
 
579
                                                        + functionToken + "'.",
 
580
                                        SQLError.SQL_STATE_SYNTAX_ERROR);
 
581
 
 
582
                }
 
583
 
 
584
                String expression = functionToken.substring(firstIndexOfParen + 1,
 
585
                                indexOfComma);
 
586
                String type = functionToken.substring(indexOfComma + 1,
 
587
                                indexOfCloseParen);
 
588
 
 
589
                String newType = null;
 
590
 
 
591
                String trimmedType = type.trim();
 
592
 
 
593
                if (StringUtils.startsWithIgnoreCase(trimmedType, "SQL_")) {
 
594
                        trimmedType = trimmedType.substring(4, trimmedType.length());
 
595
                }
 
596
 
 
597
                if (serverSupportsConvertFn) {
 
598
                        newType = (String) JDBC_CONVERT_TO_MYSQL_TYPE_MAP.get(trimmedType
 
599
                                        .toUpperCase(Locale.ENGLISH));
 
600
                } else {
 
601
                        newType = (String) JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP
 
602
                                        .get(trimmedType.toUpperCase(Locale.ENGLISH));
 
603
 
 
604
                        // We need a 'special' check here to give a better error message. If
 
605
                        // we're in this
 
606
                        // block, the version of MySQL we're connected to doesn't support
 
607
                        // CAST/CONVERT,
 
608
                        // so we can't re-write some data type conversions
 
609
                        // (date,time,timestamp, datetime)
 
610
 
 
611
                        if (newType == null) {
 
612
                                throw SQLError.createSQLException(
 
613
                                                "Can't find conversion re-write for type '"
 
614
                                                                + type
 
615
                                                                + "' that is applicable for this server version while processing escape tokens.",
 
616
                                                SQLError.SQL_STATE_GENERAL_ERROR);
 
617
                        }
 
618
                }
 
619
 
 
620
                if (newType == null) {
 
621
                        throw SQLError.createSQLException("Unsupported conversion type '"
 
622
                                        + type.trim() + "' found while processing escape token.",
 
623
                                        SQLError.SQL_STATE_GENERAL_ERROR);
 
624
                }
 
625
 
 
626
                int replaceIndex = newType.indexOf("?");
 
627
 
 
628
                if (replaceIndex != -1) {
 
629
                        StringBuffer convertRewrite = new StringBuffer(newType.substring(0,
 
630
                                        replaceIndex));
 
631
                        convertRewrite.append(expression);
 
632
                        convertRewrite.append(newType.substring(replaceIndex + 1, newType
 
633
                                        .length()));
 
634
 
 
635
                        return convertRewrite.toString();
 
636
                } else {
 
637
 
 
638
                        StringBuffer castRewrite = new StringBuffer("CAST(");
 
639
                        castRewrite.append(expression);
 
640
                        castRewrite.append(" AS ");
 
641
                        castRewrite.append(newType);
 
642
                        castRewrite.append(")");
 
643
 
 
644
                        return castRewrite.toString();
 
645
                }
 
646
        }
 
647
 
 
648
        /**
 
649
         * Removes all whitespace from the given String. We use this to make escape
 
650
         * token comparison white-space ignorant.
 
651
         * 
 
652
         * @param toCollapse
 
653
         *            the string to remove the whitespace from
 
654
         * 
 
655
         * @return a string with _no_ whitespace.
 
656
         */
 
657
        private static String removeWhitespace(String toCollapse) {
 
658
                if (toCollapse == null) {
 
659
                        return null;
 
660
                }
 
661
 
 
662
                int length = toCollapse.length();
 
663
 
 
664
                StringBuffer collapsed = new StringBuffer(length);
 
665
 
 
666
                for (int i = 0; i < length; i++) {
 
667
                        char c = toCollapse.charAt(i);
 
668
 
 
669
                        if (!Character.isWhitespace(c)) {
 
670
                                collapsed.append(c);
 
671
                        }
 
672
                }
 
673
 
 
674
                return collapsed.toString();
 
675
        }
 
676
}