1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.ejb.plugins.cmp.jdbc;
23
24 import java.sql.Connection;
25 import java.sql.DatabaseMetaData;
26 import javax.sql.DataSource;
27 import java.sql.SQLException;
28 import java.sql.Statement;
29 import java.sql.ResultSet;
30 import java.util.zip.CRC32;
31 import java.util.ArrayList;
32
33 import org.jboss.deployment.DeploymentException;
34 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
35 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
36 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
37 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractCMRFieldBridge;
38 import org.jboss.logging.Logger;
39
40 import java.util.Vector;
41
42 /**
43 * SQLUtil helps with building sql statements.
44 *
45 * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
46 * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a>
47 * @author <a href="joachim@cabsoft.be">Joachim Van der Auwera</a>
48 * @version $Revision: 67395 $
49 */
50 public final class SQLUtil
51 {
52 public static final String EMPTY_STRING = "";
53 public static final String INSERT_INTO = "INSERT INTO ";
54 public static final String VALUES = " VALUES ";
55 public static final String SELECT = "SELECT ";
56 public static final String DISTINCT = "DISTINCT ";
57 public static final String FROM = " FROM ";
58 public static final String WHERE = " WHERE ";
59 public static final String ORDERBY = " ORDER BY ";
60 public static final String DELETE_FROM = "DELETE FROM ";
61 public static final String AND = " AND ";
62 public static final String OR = " OR ";
63 public static final String NOT = " NOT ";
64 public static final String EXISTS = "EXISTS ";
65 public static final String COMMA = ", ";
66 public static final String LEFT_JOIN = " LEFT JOIN ";
67 public static final String LEFT_OUTER_JOIN = " LEFT OUTER JOIN ";
68 public static final String ON = " ON ";
69 public static final String NOT_EQUAL = "<>";
70 public static final String CREATE_TABLE = "CREATE TABLE ";
71 public static final String DROP_TABLE = "DROP TABLE ";
72 public static final String CREATE_INDEX = "CREATE INDEX ";
73 public static final String NULL = "NULL";
74 public static final String IS = " IS ";
75 public static final String IN = " IN ";
76 public static final String EMPTY = "EMPTY";
77 public static final String BETWEEN = " BETWEEN ";
78 public static final String LIKE = " LIKE ";
79 public static final String MEMBER_OF = " MEMBER OF ";
80 public static final String CONCAT = "CONCAT";
81 public static final String SUBSTRING = "SUBSTRING";
82 public static final String LCASE = "LCASE";
83 public static final String UCASE = "UCASE";
84 public static final String LENGTH = "LENGTH";
85 public static final String LOCATE = "LOCATE";
86 public static final String ABS = "ABS";
87 public static final String MOD = "MOD";
88 public static final String SQRT = "SQRT";
89 public static final String COUNT = "COUNT";
90 public static final String MAX = "MAX";
91 public static final String MIN = "MIN";
92 public static final String AVG = "AVG";
93 public static final String SUM = "SUM";
94 public static final String ASC = " ASC";
95 public static final String DESC = " DESC";
96 public static final String OFFSET = " OFFSET ";
97 public static final String LIMIT = " LIMIT ";
98 public static final String UPDATE = "UPDATE ";
99 public static final String SET = " SET ";
100 public static final String TYPE = " TYPE ";
101 private static final String DOT = ".";
102
103 private static final String EQ_QUESTMARK = "=?";
104
105 private static final Vector rwords = new Vector();
106
107 public static String getTableNameWithoutSchema(String tableName)
108 {
109 final int dot = tableName.indexOf('.');
110 if(dot != -1)
111 {
112 char firstChar = tableName.charAt(0);
113 tableName = tableName.substring(dot + 1);
114 if(firstChar == '"' || firstChar == '\'')
115 {
116 tableName = firstChar + tableName;
117 }
118 }
119 return tableName;
120 }
121
122 public static String getSchema(String tableName)
123 {
124 String schema = null;
125 final int dot = tableName.indexOf('.');
126 if(dot != -1)
127 {
128 char firstChar = tableName.charAt(0);
129 final boolean quoted = firstChar == '"' || firstChar == '\'';
130 schema = tableName.substring(quoted ? 1 : 0, dot);
131 }
132 return schema;
133 }
134
135 public static String fixTableName(String tableName, DataSource dataSource)
136 throws DeploymentException
137 {
138 // don't fix the quited table name
139 char firstChar = tableName.charAt(0);
140 if(firstChar == '"' || firstChar == '\'')
141 {
142 return tableName;
143 }
144
145 // Separate schema name and table name
146 String strSchema = "";
147 int iIndex;
148 if((iIndex = tableName.indexOf('.')) != -1)
149 {
150 strSchema = tableName.substring(0, iIndex);
151 tableName = tableName.substring(iIndex + 1);
152 }
153
154 // check for SQL reserved word and escape it with prepending a "X"
155 // IMHO one should reject reserved words and throw a
156 // DeploymentException - pilhuhn
157 if(rwords != null)
158 {
159 for(int i = 0; i < rwords.size(); i++)
160 {
161 if(((String)rwords.elementAt(i)).equalsIgnoreCase(tableName))
162 {
163 tableName = "X" + tableName;
164 break;
165 }
166 }
167 }
168
169 Connection con = null;
170 try
171 {
172 con = dataSource.getConnection();
173 DatabaseMetaData dmd = con.getMetaData();
174
175 // fix length
176 int maxLength = dmd.getMaxTableNameLength();
177 if(maxLength > 0 && tableName.length() > maxLength)
178 {
179 CRC32 crc = new CRC32();
180 crc.update(tableName.getBytes());
181 String nameCRC = Long.toString(crc.getValue(), 36);
182
183 tableName = tableName.substring(
184 0,
185 maxLength - nameCRC.length() - 2);
186 tableName += "_" + nameCRC;
187 }
188
189 // fix case
190 if(dmd.storesLowerCaseIdentifiers())
191 {
192 tableName = tableName.toLowerCase();
193 }
194 else if(dmd.storesUpperCaseIdentifiers())
195 {
196 tableName = tableName.toUpperCase();
197 }
198 // now put the schema name back on the table name
199 if(strSchema.length() > 0)
200 {
201 tableName = strSchema + "." + tableName;
202 }
203 return tableName;
204 }
205 catch(SQLException e)
206 {
207 // This should not happen. A J2EE compatiable JDBC driver is
208 // required fully support metadata.
209 throw new DeploymentException("Error while fixing table name", e);
210 }
211 finally
212 {
213 JDBCUtil.safeClose(con);
214 }
215 }
216
217 public static void addToRwords(String word)
218 {
219 if(!rwords.contains(word))
220 rwords.add(word);
221 }
222
223
224 public static String fixConstraintName(String name, DataSource dataSource)
225 throws DeploymentException
226 {
227 return fixTableName(name, dataSource).replace('.', '_');
228 }
229
230 // =======================================================================
231 // Create Table Columns Clause
232 // columnName0 sqlType0
233 // [, columnName1 sqlType0
234 // [, columnName2 sqlType0 [...]]]
235 // =======================================================================
236 public static String getCreateTableColumnsClause(JDBCFieldBridge[] fields)
237 {
238 StringBuffer buf = new StringBuffer(100);
239 boolean comma = false;
240 for(int i = 0; i < fields.length; ++i)
241 {
242 JDBCType type = getJDBCType(fields[i]);
243 if(type != null)
244 {
245 if(comma)
246 buf.append(COMMA);
247 else
248 comma = true;
249 buf.append(getCreateTableColumnsClause(type));
250 }
251 }
252 return buf.toString();
253 }
254
255 /**
256 * Returns columnName0 sqlType0
257 * [, columnName1 sqlType0
258 * [, columnName2 sqlType0 [...]]]
259 */
260 public static String getCreateTableColumnsClause(JDBCType type)
261 {
262 String[] columnNames = type.getColumnNames();
263 String[] sqlTypes = type.getSQLTypes();
264 boolean[] notNull = type.getNotNull();
265
266 StringBuffer buf = new StringBuffer();
267 for(int i = 0; i < columnNames.length; i++)
268 {
269 if(i != 0)
270 buf.append(COMMA);
271 buf.append(columnNames[i]).append(' ').append(sqlTypes[i]);
272 if(notNull[i])
273 buf.append(NOT).append(NULL);
274 }
275 return buf.toString();
276 }
277
278 // =======================================================================
279 // Column Names Clause
280 // columnName0 [, columnName1 [AND columnName2 [...]]]
281 // =======================================================================
282
283 /**
284 * Returns columnName0 [, columnName1 [AND columnName2 [...]]]
285 */
286 public static StringBuffer getColumnNamesClause(JDBCFieldBridge[] fields, StringBuffer sb)
287 {
288 return getColumnNamesClause(fields, "", sb);
289 }
290
291 /**
292 * Returns columnName0 [, columnName1 [AND columnName2 [...]]]
293 */
294 public static StringBuffer getColumnNamesClause(JDBCFieldBridge[] fields,
295 String identifier,
296 StringBuffer buf)
297 {
298 boolean comma = false;
299 for(int i = 0; i < fields.length; ++i)
300 {
301 JDBCType type = getJDBCType(fields[i]);
302 if(type != null)
303 {
304 if(comma)
305 buf.append(COMMA);
306 else
307 comma = true;
308 getColumnNamesClause(type, identifier, buf);
309 }
310 }
311 return buf;
312 }
313
314 /**
315 * Returns columnName0 [, columnName1 [AND columnName2 [...]]]
316 */
317 public static StringBuffer getSearchableColumnNamesClause(JDBCFieldBridge[] fields,
318 String identifier,
319 StringBuffer buf)
320 {
321 boolean comma = false;
322 for(int i = 0; i < fields.length; ++i)
323 {
324 JDBCType type = getJDBCType(fields[i]);
325 if(type != null && type.isSearchable())
326 {
327 if(comma)
328 buf.append(COMMA);
329 else
330 comma = true;
331 getColumnNamesClause(type, identifier, buf);
332 }
333 }
334 return buf;
335 }
336
337 /**
338 * Returns columnName0 [, columnName1 [AND columnName2 [...]]]
339 */
340 public static StringBuffer getColumnNamesClause(JDBCEntityBridge.FieldIterator loadIter, StringBuffer sb)
341 {
342 if(loadIter.hasNext())
343 getColumnNamesClause(loadIter.next(), sb);
344 while(loadIter.hasNext())
345 {
346 sb.append(COMMA);
347 getColumnNamesClause(loadIter.next(), sb);
348 }
349 return sb;
350 }
351
352 /**
353 * Returns columnName0 [, columnName1 [, columnName2 [...]]]
354 */
355 public static StringBuffer getColumnNamesClause(JDBCFieldBridge field, StringBuffer sb)
356 {
357 return getColumnNamesClause(field.getJDBCType(), sb);
358 }
359
360 /**
361 * Returns identifier.columnName0
362 * [, identifier.columnName1
363 * [, identifier.columnName2 [...]]]
364 */
365 public static StringBuffer getColumnNamesClause(JDBCFieldBridge field, String identifier, StringBuffer sb)
366 {
367 return getColumnNamesClause(field.getJDBCType(), identifier, sb);
368 }
369
370 /**
371 * Returns identifier.columnName0
372 * [, identifier.columnName1
373 * [, identifier.columnName2 [...]]]
374 */
375 private static StringBuffer getColumnNamesClause(JDBCType type, String identifier, StringBuffer buf)
376 {
377 String[] columnNames = type.getColumnNames();
378 boolean hasIdentifier = identifier.length() > 0;
379 if(hasIdentifier)
380 buf.append(identifier).append(DOT);
381 buf.append(columnNames[0]);
382 int i = 1;
383 while(i < columnNames.length)
384 {
385 buf.append(COMMA);
386 if(hasIdentifier)
387 buf.append(identifier).append(DOT);
388 buf.append(columnNames[i++]);
389 }
390 return buf;
391 }
392
393 /**
394 * Returns ', columnName0 [, columnName1 [AND columnName2 [...]]]'
395 */
396 public static StringBuffer appendColumnNamesClause(JDBCAbstractEntityBridge entity, String eagerLoadGroup, StringBuffer sb)
397 {
398 return appendColumnNamesClause(entity, eagerLoadGroup, "", sb);
399 }
400
401 /**
402 * Returns ', columnName0 [, columnName1 [AND columnName2 [...]]]'
403 */
404 public static StringBuffer appendColumnNamesClause(JDBCAbstractEntityBridge entity,
405 String eagerLoadGroup,
406 String alias,
407 StringBuffer sb)
408 {
409 return appendColumnNamesClause(entity.getTableFields(), entity.getLoadGroupMask(eagerLoadGroup), alias, sb);
410 }
411
412 /**
413 * Returns ', columnName0 [, columnName1 [AND columnName2 [...]]]'
414 */
415 public static StringBuffer appendColumnNamesClause(JDBCFieldBridge[] fields,
416 boolean[] mask,
417 String identifier,
418 StringBuffer buf)
419 {
420 for(int i = 0; i < fields.length; ++i)
421 {
422 if(mask[i])
423 {
424 JDBCType type = getJDBCType(fields[i]);
425 if(type != null)
426 {
427 buf.append(COMMA);
428 getColumnNamesClause(type, identifier, buf);
429 }
430 }
431 }
432 return buf;
433 }
434
435 public static StringBuffer appendSearchableColumnNamesClause(JDBCFieldBridge[] fields,
436 boolean[] mask,
437 String identifier,
438 StringBuffer buf)
439 {
440 for(int i = 0; i < fields.length; ++i)
441 {
442 if(mask[i])
443 {
444 JDBCType type = getJDBCType(fields[i]);
445 if(type != null && type.isSearchable())
446 {
447 buf.append(COMMA);
448 getColumnNamesClause(type, identifier, buf);
449 }
450 }
451 }
452 return buf;
453 }
454
455 /**
456 * Returns ', columnName0 [, columnName1 [AND columnName2 [...]]]'
457 */
458 public static StringBuffer appendColumnNamesClause(JDBCFieldBridge[] fields,
459 String identifier,
460 StringBuffer buf)
461 {
462 for(int i = 0; i < fields.length; ++i)
463 {
464 JDBCType type = getJDBCType(fields[i]);
465 if(type != null)
466 {
467 buf.append(COMMA);
468 getColumnNamesClause(type, identifier, buf);
469 }
470 }
471 return buf;
472 }
473
474 /**
475 * Returns identifier.columnName0
476 * [, identifier.columnName1
477 * [, identifier.columnName2 [...]]]
478 */
479 private static StringBuffer getColumnNamesClause(JDBCType type, StringBuffer buf)
480 {
481 String[] columnNames = type.getColumnNames();
482 buf.append(columnNames[0]);
483 int i = 1;
484 while(i < columnNames.length)
485 {
486 buf.append(COMMA).append(columnNames[i++]);
487 }
488 return buf;
489 }
490
491 // =======================================================================
492 // Set Clause
493 // columnName0=? [, columnName1=? [, columnName2=? [...]]]
494 // =======================================================================
495
496 /**
497 * Returns columnName0=? [, columnName1=? [, columnName2=? [...]]]
498 */
499 public static StringBuffer getSetClause(JDBCEntityBridge.FieldIterator fieldsIter,
500 StringBuffer buf)
501 {
502 JDBCType type = getJDBCType(fieldsIter.next());
503 getSetClause(type, buf);
504 while(fieldsIter.hasNext())
505 {
506 type = getJDBCType(fieldsIter.next());
507 buf.append(COMMA);
508 getSetClause(type, buf);
509 }
510 return buf;
511 }
512
513 /**
514 * Returns columnName0=? [, columnName1=? [, columnName2=? [...]]]
515 */
516 private static StringBuffer getSetClause(JDBCType type, StringBuffer buf)
517 {
518 String[] columnNames = type.getColumnNames();
519 buf.append(columnNames[0]).append(EQ_QUESTMARK);
520 int i = 1;
521 while(i < columnNames.length)
522 {
523 buf.append(COMMA).append(columnNames[i++]).append(EQ_QUESTMARK);
524 }
525 return buf;
526 }
527
528 // =======================================================================
529 // Values Clause
530 // ? [, ? [, ? [...]]]
531 // =======================================================================
532
533 /**
534 * Returns ? [, ? [, ? [...]]]
535 */
536 public static StringBuffer getValuesClause(JDBCFieldBridge[] fields, StringBuffer buf)
537 {
538 boolean comma = false;
539 for(int i = 0; i < fields.length; ++i)
540 {
541 JDBCType type = getJDBCType(fields[i]);
542 if(type != null)
543 {
544 if(comma)
545 buf.append(COMMA);
546 else
547 comma = true;
548 getValuesClause(type, buf);
549 }
550 }
551 return buf;
552 }
553
554 /**
555 * Returns ? [, ? [, ? [...]]]
556 */
557 private static StringBuffer getValuesClause(JDBCType type, StringBuffer buf)
558 {
559 int columnCount = type.getColumnNames().length;
560 buf.append('?');
561 int i = 1;
562 while(i++ < columnCount)
563 buf.append(COMMA).append('?');
564 return buf;
565 }
566
567 // =======================================================================
568 // Where Clause
569 // columnName0=? [AND columnName1=? [AND columnName2=? [...]]]
570 // =======================================================================
571
572 /**
573 * Returns columnName0=? [AND columnName1=? [AND columnName2=? [...]]]
574 */
575 public static StringBuffer getWhereClause(JDBCFieldBridge[] fields, StringBuffer buf)
576 {
577 return getWhereClause(fields, "", buf);
578 }
579
580 /**
581 * Returns identifier.columnName0=?
582 * [AND identifier.columnName1=?
583 * [AND identifier.columnName2=? [...]]]
584 */
585 public static StringBuffer getWhereClause(JDBCFieldBridge[] fields, String identifier, StringBuffer buf)
586 {
587 boolean and = false;
588 for(int i = 0; i < fields.length; ++i)
589 {
590 JDBCType type = getJDBCType(fields[i]);
591 if(type != null)
592 {
593 if(and)
594 buf.append(AND);
595 else
596 and = true;
597 getWhereClause(type, identifier, buf);
598 }
599 }
600 return buf;
601 }
602
603 /**
604 * Returns columnName0=? [AND columnName1=? [AND columnName2=? [...]]]
605 */
606 public static StringBuffer getWhereClause(JDBCFieldBridge[] fields,
607 long mask,
608 StringBuffer buf)
609 {
610 return getWhereClause(fields, mask, "", buf);
611 }
612
613 /**
614 * Returns columnName0=? [AND columnName1=? [AND columnName2=? [...]]]
615 */
616 private static StringBuffer getWhereClause(JDBCFieldBridge[] fields,
617 long mask,
618 String identifier,
619 StringBuffer buf)
620 {
621 boolean and = false;
622 long fieldMask = 1;
623 for(int i = 0; i < fields.length; ++i)
624 {
625 if((fieldMask & mask) > 0)
626 {
627 JDBCType type = getJDBCType(fields[i]);
628 if(type != null)
629 {
630 if(and)
631 buf.append(AND);
632 else
633 and = true;
634 getWhereClause(type, identifier, buf);
635 }
636 }
637 fieldMask <<= 1;
638 }
639 return buf;
640 }
641
642 /**
643 * Returns columnName0=? [AND columnName1=? [AND columnName2=? [...]]]
644 */
645 public static StringBuffer getWhereClause(JDBCFieldBridge field, StringBuffer buf)
646 {
647 return getWhereClause(field.getJDBCType(), "", buf);
648 }
649
650 /**
651 * Returns identifier.columnName0=?
652 * [AND identifier.columnName1=?
653 * [AND identifier.columnName2=? [...]]]
654 */
655 public static StringBuffer getWhereClause(JDBCType type, String identifier, StringBuffer buf)
656 {
657 if(identifier.length() > 0)
658 {
659 identifier += '.';
660 }
661
662 String[] columnNames = type.getColumnNames();
663 buf.append(identifier).append(columnNames[0]).append(EQ_QUESTMARK);
664 int i = 1;
665 while(i < columnNames.length)
666 {
667 buf.append(AND).append(identifier).append(columnNames[i++]).append(EQ_QUESTMARK);
668 }
669 return buf;
670 }
671
672 /**
673 * Returns identifier.columnName0{comparison}?
674 * [AND identifier.columnName1{comparison}?
675 * [AND identifier.columnName2{comparison}? [...]]]
676 */
677 public static StringBuffer getWhereClause(JDBCType type, String identifier, String comparison, StringBuffer buf)
678 {
679 if(identifier.length() > 0)
680 {
681 identifier += '.';
682 }
683
684 String[] columnNames = type.getColumnNames();
685 buf.append(identifier).append(columnNames[0]).append(comparison).append('?');
686 int i = 1;
687 while(i < columnNames.length)
688 {
689 buf.append(AND).append(identifier).append(columnNames[i++]).append(comparison).append('?');
690 }
691 return buf;
692 }
693
694
695 // =======================================================================
696 // Is [Not] Null Clause
697 // columnName0 IS [NOT] NULL [AND columnName1 IS [NOT] NULL [...]]
698 // =======================================================================
699
700 /**
701 * Returns identifier.columnName0 IS [NOT] NULL
702 * [AND identifier.columnName1 IS [NOT] NULL
703 * [AND identifier.columnName2 IS [NOT] NULL [...]]]
704 */
705 public static StringBuffer getIsNullClause(boolean not,
706 JDBCFieldBridge[] fields,
707 String identifier,
708 StringBuffer buf)
709 {
710 boolean and = false;
711 for(int i = 0; i < fields.length; ++i)
712 {
713 JDBCType type = getJDBCType(fields[i]);
714 if(type != null)
715 {
716 if(and)
717 buf.append(AND);
718 else
719 and = true;
720 getIsNullClause(not, type, identifier, buf);
721 }
722 }
723 return buf;
724 }
725
726 /**
727 * Returns identifier.columnName0 IS [NOT] NULL
728 * [AND identifier.columnName1 IS [NOT] NULL
729 * [AND identifier.columnName2 IS [NOT] NULL [...]]]
730 */
731 public static StringBuffer getIsNullClause(boolean not,
732 JDBCFieldBridge field,
733 String identifier,
734 StringBuffer buf)
735 {
736 return getIsNullClause(not, field.getJDBCType(), identifier, buf);
737 }
738
739 /**
740 * Returns identifier.columnName0 IS [NOT] NULL
741 * [AND identifier.columnName1 IS [NOT] NULL
742 * [AND identifier.columnName2 IS [NOT] NULL [...]]]
743 */
744 private static StringBuffer getIsNullClause(boolean not,
745 JDBCType type,
746 String identifier,
747 StringBuffer buf)
748 {
749 if(identifier.length() > 0)
750 {
751 identifier += '.';
752 }
753
754 String[] columnNames = type.getColumnNames();
755
756 buf.append(identifier).append(columnNames[0]).append(IS);
757 (not ? buf.append(NOT) : buf).append(NULL);
758 int i = 1;
759 while(i < columnNames.length)
760 {
761 buf.append(AND).append(identifier).append(columnNames[i++]).append(IS);
762 (not ? buf.append(NOT) : buf).append(NULL);
763 }
764 return buf;
765 }
766
767 // =======================================================================
768 // Join Clause
769 // parent.pkColumnName0=child.fkColumnName0
770 // [AND parent.pkColumnName1=child.fkColumnName1
771 // [AND parent.pkColumnName2=child.fkColumnName2 [...]]]
772 // =======================================================================
773
774 public static StringBuffer getJoinClause(JDBCAbstractCMRFieldBridge cmrField,
775 String parentAlias,
776 String childAlias,
777 StringBuffer buf)
778 {
779 JDBCAbstractEntityBridge parentEntity = cmrField.getEntity();
780 JDBCAbstractEntityBridge childEntity = (JDBCAbstractEntityBridge)cmrField.getRelatedEntity();
781
782 JDBCFieldBridge parentField;
783 JDBCFieldBridge childField;
784
785 if(cmrField.hasForeignKey())
786 {
787 // parent has the foreign keys
788 JDBCFieldBridge[] parentFkFields = cmrField.getForeignKeyFields();
789 int i = 0;
790 while(i < parentFkFields.length)
791 {
792 parentField = parentFkFields[i++];
793 childField = (JDBCFieldBridge)childEntity.getFieldByName(parentField.getFieldName());
794 getJoinClause(parentField, parentAlias, childField, childAlias, buf);
795 if(i < parentFkFields.length)
796 buf.append(AND);
797 }
798 }
799 else
800 {
801 // child has the foreign keys
802 JDBCFieldBridge[] childFkFields = cmrField.getRelatedCMRField().getForeignKeyFields();
803 int i = 0;
804 while(i < childFkFields.length)
805 {
806 childField = childFkFields[i++];
807 parentField = (JDBCFieldBridge)parentEntity.getFieldByName(childField.getFieldName());
808
809 // add the sql
810 getJoinClause(parentField, parentAlias, childField, childAlias, buf);
811 if(i < childFkFields.length)
812 {
813 buf.append(AND);
814 }
815 }
816 }
817 return buf;
818 }
819
820 public static StringBuffer getRelationTableJoinClause(JDBCAbstractCMRFieldBridge cmrField,
821 String parentAlias,
822 String relationTableAlias,
823 StringBuffer buf)
824 {
825 JDBCAbstractEntityBridge parentEntity = cmrField.getEntity();
826 JDBCFieldBridge parentField;
827 JDBCFieldBridge relationField;
828
829 // parent to relation table join
830 JDBCFieldBridge[] parentFields = cmrField.getTableKeyFields();
831 int i = 0;
832 while(i < parentFields.length)
833 {
834 relationField = parentFields[i++];
835 parentField = (JDBCFieldBridge)parentEntity.getFieldByName(relationField.getFieldName());
836 getJoinClause(parentField, parentAlias, relationField, relationTableAlias, buf);
837 if(i < parentFields.length)
838 buf.append(AND);
839 }
840 return buf;
841 }
842
843 /**
844 * Returns parent.pkColumnName0=child.fkColumnName0
845 * [AND parent.pkColumnName1=child.fkColumnName1
846 * [AND parent.pkColumnName2=child.fkColumnName2 [...]]]
847 */
848 private static StringBuffer getJoinClause(JDBCFieldBridge pkField,
849 String parent,
850 JDBCFieldBridge fkField,
851 String child,
852 StringBuffer buf)
853 {
854 return getJoinClause(pkField.getJDBCType(), parent, fkField.getJDBCType(), child, buf);
855 }
856
857 public static StringBuffer getJoinClause(JDBCFieldBridge[] pkFields,
858 String parent,
859 JDBCFieldBridge[] fkFields,
860 String child,
861 StringBuffer buf)
862 {
863 if(pkFields.length != fkFields.length)
864 {
865 throw new IllegalArgumentException(
866 "Error createing theta join clause:" +
867 " pkField.size()=" + pkFields.length +
868 " fkField.size()=" + fkFields.length);
869 }
870
871 boolean and = false;
872 for(int i = 0; i < pkFields.length; ++i)
873 {
874 // these types should not be null
875 JDBCType pkType = getJDBCType(pkFields[i]);
876 JDBCType fkType = getJDBCType(fkFields[i]);
877 if(and)
878 buf.append(AND);
879 else
880 and = true;
881 getJoinClause(pkType, parent, fkType, child, buf);
882 }
883 return buf;
884 }
885
886 /**
887 * Returns parent.pkColumnName0=child.fkColumnName0
888 * [AND parent.pkColumnName1=child.fkColumnName1
889 * [AND parent.pkColumnName2=child.fkColumnName2 [...]]]
890 */
891 private static StringBuffer getJoinClause(JDBCType pkType,
892 String parent,
893 JDBCType fkType,
894 String child,
895 StringBuffer buf)
896 {
897 if(parent.length() > 0)
898 {
899 parent += '.';
900 }
901 if(child.length() > 0)
902 {
903 child += '.';
904 }
905
906 String[] pkColumnNames = pkType.getColumnNames();
907 String[] fkColumnNames = fkType.getColumnNames();
908 if(pkColumnNames.length != fkColumnNames.length)
909 {
910 throw new IllegalArgumentException("PK and FK have different number of columns");
911 }
912
913 buf.append(parent).append(pkColumnNames[0]).append('=').append(child).append(fkColumnNames[0]);
914 int i = 1;
915 while(i < pkColumnNames.length)
916 {
917 buf.append(AND)
918 .append(parent)
919 .append(pkColumnNames[i])
920 .append('=')
921 .append(child)
922 .append(fkColumnNames[i++]);
923 }
924 return buf;
925 }
926
927 // =======================================================================
928 // Self Compare Where Clause
929 // fromIdentifier.pkColumnName0=toIdentifier.fkColumnName0
930 // [AND fromIdentifier.pkColumnName1=toIdentifier.fkColumnName1
931 // [AND fromIdentifier.pkColumnName2=toIdentifier.fkColumnName2 [...]]]
932 // =======================================================================
933
934 public static StringBuffer getSelfCompareWhereClause(JDBCFieldBridge[] fields,
935 String fromIdentifier,
936 String toIdentifier,
937 StringBuffer buf)
938 {
939 boolean and = false;
940 for(int i = 0; i < fields.length; ++i)
941 {
942 JDBCType type = getJDBCType(fields[i]);
943 if(type != null)
944 {
945 if(and)
946 buf.append(AND);
947 else
948 and = true;
949 getSelfCompareWhereClause(type, fromIdentifier, toIdentifier, buf);
950 }
951 }
952 return buf;
953 }
954
955 private static StringBuffer getSelfCompareWhereClause(JDBCType type,
956 String fromIdentifier,
957 String toIdentifier,
958 StringBuffer buf)
959 {
960 if(fromIdentifier.length() > 0)
961 fromIdentifier += '.';
962 if(toIdentifier.length() > 0)
963 toIdentifier += '.';
964
965 String[] columnNames = type.getColumnNames();
966
967 buf.append(fromIdentifier)
968 .append(columnNames[0])
969 .append('=')
970 .append(toIdentifier)
971 .append(columnNames[0]);
972 int i = 1;
973 while(i < columnNames.length)
974 {
975 buf.append(AND)
976 .append(fromIdentifier)
977 .append(columnNames[i])
978 .append('=')
979 .append(toIdentifier)
980 .append(columnNames[i++]);
981 }
982 return buf;
983 }
984
985 public static StringBuffer getSelfCompareWhereClause(JDBCFieldBridge fromField,
986 JDBCFieldBridge toField,
987 String fromIdentifier,
988 String toIdentifier,
989 String comparison,
990 StringBuffer buf)
991 {
992 return getSelfCompareWhereClause(
993 fromField.getJDBCType(), toField.getJDBCType(), fromIdentifier, toIdentifier, comparison, buf
994 );
995 }
996
997 private static StringBuffer getSelfCompareWhereClause(JDBCType fromType,
998 JDBCType toType,
999 String fromIdentifier,
1000 String toIdentifier,
1001 String comparison,
1002 StringBuffer buf)
1003 {
1004 if(fromIdentifier.length() > 0)
1005 fromIdentifier += '.';
1006 if(toIdentifier.length() > 0)
1007 toIdentifier += '.';
1008
1009 String[] fromColumnNames = fromType.getColumnNames();
1010 String[] toColumnNames = toType.getColumnNames();
1011
1012 buf.append(fromIdentifier)
1013 .append(fromColumnNames[0])
1014 .append(comparison)
1015 .append(toIdentifier)
1016 .append(toColumnNames[0]);
1017 int i = 1;
1018 while(i < fromColumnNames.length)
1019 {
1020 buf.append(AND)
1021 .append(fromIdentifier)
1022 .append(fromColumnNames[i])
1023 .append(comparison)
1024 .append(toIdentifier)
1025 .append(toColumnNames[i++]);
1026 }
1027 return buf;
1028 }
1029
1030 public static boolean tableExists(String tableName, DataSource dataSource)
1031 throws DeploymentException
1032 {
1033 Connection con = null;
1034 ResultSet rs = null;
1035 try
1036 {
1037 con = dataSource.getConnection();
1038
1039 // (a j2ee spec compatible jdbc driver has to fully
1040 // implement the DatabaseMetaData)
1041 DatabaseMetaData dmd = con.getMetaData();
1042 String catalog = con.getCatalog();
1043 String schema = null;
1044 String quote = dmd.getIdentifierQuoteString();
1045 if(tableName.startsWith(quote))
1046 {
1047 if(tableName.endsWith(quote) == false)
1048 {
1049 throw new DeploymentException("Mismatched quote in table name: " + tableName);
1050 }
1051 int quoteLength = quote.length();
1052 tableName = tableName.substring(quoteLength, tableName.length() - quoteLength);
1053 if(dmd.storesLowerCaseQuotedIdentifiers())
1054 tableName = tableName.toLowerCase();
1055 else if(dmd.storesUpperCaseQuotedIdentifiers())
1056 tableName = tableName.toUpperCase();
1057 }
1058 else
1059 {
1060 if(dmd.storesLowerCaseIdentifiers())
1061 tableName = tableName.toLowerCase();
1062 else if(dmd.storesUpperCaseIdentifiers())
1063 tableName = tableName.toUpperCase();
1064 }
1065
1066 // Patch #927759: Split tablename into "schema" and "table" separated by '.'
1067 int dotIndex;
1068 if ((dotIndex = tableName.indexOf('.')) != -1)
1069 {
1070 // Yank out schema name ...
1071 schema = tableName.substring(0, dotIndex);
1072 tableName = tableName.substring(dotIndex + 1);
1073 }
1074
1075 rs = dmd.getTables(catalog, schema, tableName, null);
1076 return rs.next();
1077 }
1078 catch(SQLException e)
1079 {
1080 // This should not happen. A J2EE compatiable JDBC driver is
1081 // required fully support metadata.
1082 throw new DeploymentException("Error while checking if table aleady exists "+tableName, e);
1083 }
1084 finally
1085 {
1086 JDBCUtil.safeClose(rs);
1087 JDBCUtil.safeClose(con);
1088 }
1089 }
1090
1091 public static OldColumns getOldColumns(String tableName, DataSource dataSource)
1092 throws DeploymentException
1093 {
1094 Connection con = null;
1095 ResultSet rs = null;
1096 ArrayList columnNames = new ArrayList();
1097 ArrayList typeNames = new ArrayList();
1098 ArrayList columnSizes = new ArrayList();
1099 try
1100 {
1101 con = dataSource.getConnection();
1102
1103 // (a j2ee spec compatible jdbc driver has to fully
1104 // implement the DatabaseMetaData)
1105 DatabaseMetaData dmd = con.getMetaData();
1106 String catalog = con.getCatalog();
1107 String schema = null;
1108 String quote = dmd.getIdentifierQuoteString();
1109 if (tableName.startsWith(quote))
1110 {
1111 if (tableName.endsWith(quote) == false)
1112 {
1113 throw new DeploymentException("Mismatched quote in table name: " + tableName);
1114 }
1115 int quoteLength = quote.length();
1116 tableName = tableName.substring(quoteLength, tableName.length() - quoteLength);
1117 if (dmd.storesLowerCaseQuotedIdentifiers())
1118 tableName = tableName.toLowerCase();
1119 else if (dmd.storesUpperCaseQuotedIdentifiers())
1120 tableName = tableName.toUpperCase();
1121 }
1122 else
1123 {
1124 if (dmd.storesLowerCaseIdentifiers())
1125 tableName = tableName.toLowerCase();
1126 else if (dmd.storesUpperCaseIdentifiers())
1127 tableName = tableName.toUpperCase();
1128 }
1129
1130 // Patch #927759: Split tablename into "schema" and "table" separated by '.'
1131 int dotIndex;
1132 if ((dotIndex = tableName.indexOf('.')) != -1)
1133 {
1134 // Yank out schema name ...
1135 schema = tableName.substring(0, dotIndex);
1136 tableName = tableName.substring(dotIndex + 1);
1137 }
1138
1139 rs = dmd.getColumns(catalog, schema, tableName, null);
1140 while (rs.next())
1141 {
1142 String columnName = rs.getString("COLUMN_NAME");
1143 columnNames.add(columnName == null ? null : columnName.toUpperCase());
1144 typeNames.add(rs.getString("TYPE_NAME"));
1145 columnSizes.add(new Integer(rs.getInt("COLUMN_SIZE")));
1146 }
1147 return new OldColumns(columnNames, typeNames, columnSizes);
1148
1149 }
1150 catch (SQLException e)
1151 {
1152 // This should not happen. A J2EE compatiable JDBC driver is
1153 // required fully support metadata.
1154 throw new DeploymentException("Error while geting column names", e);
1155 }
1156 finally
1157 {
1158 JDBCUtil.safeClose(rs);
1159 JDBCUtil.safeClose(con);
1160 }
1161 }
1162
1163 public static OldIndexes getOldIndexes(String tableName, DataSource dataSource)
1164 throws DeploymentException
1165 {
1166 tableName = unquote(tableName, dataSource);
1167
1168 Connection con = null;
1169 ResultSet rs = null;
1170 ArrayList indexNames = new ArrayList();
1171 ArrayList columnNames = new ArrayList();
1172 ArrayList ascDesc = new ArrayList();
1173 try
1174 {
1175 con = dataSource.getConnection();
1176
1177 // (a j2ee spec compatible jdbc driver has to fully
1178 // implement the DatabaseMetaData)
1179 DatabaseMetaData dmd = con.getMetaData();
1180 String catalog = con.getCatalog();
1181 String schema = null;
1182 if (dmd.storesLowerCaseIdentifiers())
1183 tableName = tableName.toLowerCase();
1184 else if (dmd.storesUpperCaseIdentifiers())
1185 tableName = tableName.toUpperCase();
1186
1187 // Patch #927759: Split tablename into "schema" and "table" separated by '.'
1188 int dotIndex;
1189 if ((dotIndex = tableName.indexOf('.')) != -1)
1190 {
1191 // Yank out schema name ...
1192 schema = tableName.substring(0, dotIndex);
1193 tableName = tableName.substring(dotIndex + 1);
1194 }
1195
1196 rs = dmd.getIndexInfo(catalog, schema, tableName, false, false);
1197 while (rs.next())
1198 {
1199 indexNames.add(rs.getString("INDEX_NAME"));
1200 columnNames.add(rs.getString("COLUMN_NAME"));
1201 ascDesc.add(rs.getString("ASC_OR_DESC"));
1202 }
1203 return new OldIndexes(indexNames, columnNames, ascDesc);
1204
1205 }
1206 catch (SQLException e)
1207 {
1208 // This should not happen. A J2EE compatiable JDBC driver is
1209 // required fully support metadata.
1210 throw new DeploymentException("Error while geting column names", e);
1211 }
1212 finally
1213 {
1214 JDBCUtil.safeClose(rs);
1215 JDBCUtil.safeClose(con);
1216 }
1217 }
1218
1219 public static String unquote(String tableName, DataSource ds)
1220 throws DeploymentException
1221 {
1222 Connection con = null;
1223 try
1224 {
1225 con = ds.getConnection();
1226 String quote = con.getMetaData().getIdentifierQuoteString();
1227 if (tableName.startsWith(quote))
1228 {
1229 if (tableName.endsWith(quote) == false)
1230 {
1231 throw new DeploymentException("Mismatched quote in table name: " + tableName);
1232 }
1233 int quoteLength = quote.length();
1234 tableName = tableName.substring(quoteLength, tableName.length() - quoteLength);
1235 }
1236 }
1237 catch(SQLException e)
1238 {
1239 throw new DeploymentException("Failed to get datasource connection");
1240 }
1241 finally
1242 {
1243 JDBCUtil.safeClose(con);
1244 }
1245 return tableName;
1246 }
1247
1248 private static JDBCType getJDBCType(JDBCFieldBridge field)
1249 {
1250 JDBCType type = field.getJDBCType();
1251 if(type != null && type.getColumnNames().length > 0)
1252 {
1253 return type;
1254 }
1255 return null;
1256 }
1257
1258 public static void dropTable(DataSource dataSource,
1259 String tableName)
1260 throws DeploymentException
1261 {
1262 Logger log = Logger.getLogger("CLEANER");
1263 String sql = "DROP TABLE " + tableName;
1264 try
1265 {
1266 Connection con = null;
1267 Statement statement = null;
1268 try
1269 {
1270 // execute sql
1271 con = dataSource.getConnection();
1272 statement = con.createStatement();
1273 statement.executeUpdate(sql);
1274 }
1275 finally
1276 {
1277 // make sure to close the connection and statement before
1278 // comitting the transaction or XA will break
1279 JDBCUtil.safeClose(statement);
1280 JDBCUtil.safeClose(con);
1281 }
1282 } catch (Exception e) {
1283 throw new DeploymentException("Error while droping table "+tableName, e);
1284 }
1285 log.info("Dropped table "+tableName+" succesfuly");
1286 }
1287
1288 /**
1289 * utility class to store the information returned by getOldColumns()
1290 */
1291 public static class OldColumns
1292 {
1293 private ArrayList columnNames;
1294 private ArrayList typeNames;
1295 private ArrayList columnSizes;
1296
1297 private OldColumns(ArrayList cn, ArrayList tn, ArrayList cs)
1298 {
1299 columnNames = cn;
1300 typeNames = tn;
1301 columnSizes = cs;
1302 }
1303
1304 public ArrayList getColumnNames()
1305 {
1306 return columnNames;
1307 }
1308
1309 public ArrayList getTypeNames()
1310 {
1311 return typeNames;
1312 }
1313
1314 public ArrayList getColumnSizes()
1315 {
1316 return columnSizes;
1317 }
1318 }
1319
1320 /**
1321 * utility class to store the information returned by getOldColumns()
1322 */
1323 public static class OldIndexes
1324 {
1325 private ArrayList indexNames;
1326 private ArrayList columnNames;
1327 private ArrayList columnAscDesc;
1328
1329 private OldIndexes(ArrayList in, ArrayList cn, ArrayList ad)
1330 {
1331 indexNames = in;
1332 columnNames = cn;
1333 columnAscDesc = ad;
1334 }
1335
1336 public ArrayList getColumnNames()
1337 {
1338 return columnNames;
1339 }
1340
1341 public ArrayList getIndexNames()
1342 {
1343 return indexNames;
1344 }
1345
1346 public ArrayList getColumnAscDesc()
1347 {
1348 return columnAscDesc;
1349 }
1350 }
1351
1352 }