001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.storage.sql.jdbc.db; 020 021import java.io.Serializable; 022import java.util.LinkedList; 023import java.util.List; 024 025import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect; 026 027/** 028 * A SQL JOIN. 029 */ 030public class Join implements Serializable, Comparable<Join> { 031 032 private static final long serialVersionUID = 1L; 033 034 public static final int INNER = 1; 035 036 public static final int LEFT = 2; 037 038 public static final int RIGHT = 3; 039 040 public static final int IMPLICIT = 4; 041 042 /** INNER / LEFT / RIGHT / IMPLICIT */ 043 public final int kind; 044 045 /** Table name. */ 046 public final String table; 047 048 /** Table alias, or {@code null}. */ 049 public final String tableAlias; 050 051 /** 052 * Parameter if table name is an expression that contains a "?", or {@code null}. 053 */ 054 public final String tableParam; 055 056 /** Left part of equijoin. */ 057 public Column column1; 058 059 /** Right part of equijoin. */ 060 public Column column2; 061 062 /** Left part of equijoin. */ 063 public String on1; 064 065 /** Right part of equijoin. */ 066 public String on2; 067 068 /** Additional WHERE clauses. */ 069 public final List<String> whereClauses = new LinkedList<String>(); 070 071 /** Additional WHERE clauses parameters. */ 072 public final List<Serializable> whereParams = new LinkedList<Serializable>(); 073 074 private Join(int kind, String table, String tableAlias, String tableParam) { 075 this.kind = kind; 076 this.table = table; 077 this.tableAlias = tableAlias; 078 this.tableParam = tableParam; 079 } 080 081 public Join(int kind, String table, String tableAlias, String tableParam, Column column1, Column column2) { 082 this(kind, table, tableAlias, tableParam); 083 this.column1 = column1; 084 this.column2 = column2; 085 } 086 087 public Join(int kind, String table, String tableAlias, String tableParam, String on1, String on2) { 088 this(kind, table, tableAlias, tableParam); 089 this.on1 = on1; 090 this.on2 = on2; 091 } 092 093 public void addWhereClause(String whereClause, Serializable whereParam) { 094 whereClauses.add(whereClause); 095 whereParams.add(whereParam); 096 } 097 098 // make sure IMPLICIT joins are last 099 @Override 100 public int compareTo(Join other) { 101 if (kind == IMPLICIT && other.kind == IMPLICIT) { 102 return 0; 103 } 104 if (kind == IMPLICIT) { 105 return 1; 106 } 107 if (other.kind == IMPLICIT) { 108 return -1; 109 } 110 return 0; 111 } 112 113 public String getTable(Dialect dialect) { 114 if (tableAlias == null) { 115 return table; 116 } else { 117 return table + " " + dialect.openQuote() + tableAlias + dialect.closeQuote(); 118 } 119 } 120 121 public String getClause(Dialect dialect) { 122 if (on1 == null && on2 == null) { 123 on1 = column1.getFullQuotedName(); 124 on2 = column2.getFullQuotedName(); 125 boolean isid1 = column1.getType().isId(); 126 boolean isid2 = column2.getType().isId(); 127 if (dialect != null && isid1 != isid2) { 128 // temporary fix cast uuid to varchar because relation table 129 // has varchar source and target field 130 if (isid1) { 131 on1 = dialect.castIdToVarchar(on1); 132 } else { 133 on2 = dialect.castIdToVarchar(on2); 134 } 135 } 136 } 137 return on1 + " = " + on2; 138 } 139 140 /** 141 * Does not return the WHERE clause. 142 * <p> 143 * {@inheritDoc} 144 */ 145 public String toSql(Dialect dialect) { 146 switch (kind) { 147 case INNER: 148 return String.format(" JOIN %s ON %s", getTable(dialect), getClause(dialect)); 149 case LEFT: 150 return String.format(" LEFT JOIN %s ON %s", getTable(dialect), getClause(dialect)); 151 case RIGHT: 152 return String.format(" RIGHT JOIN %s ON %s", getTable(dialect), getClause(dialect)); 153 case IMPLICIT: 154 return String.format(", %s", getTable(dialect)); 155 default: 156 throw new AssertionError(); 157 } 158 } 159 160 @Override 161 public String toString() { 162 String k; 163 switch (kind) { 164 case INNER: 165 k = "INNER"; 166 break; 167 case LEFT: 168 k = "LEFT"; 169 break; 170 case RIGHT: 171 k = "RIGHT"; 172 break; 173 case IMPLICIT: 174 k = "IMPLICIT"; 175 break; 176 default: 177 throw new AssertionError(); 178 } 179 StringBuilder buf = new StringBuilder(); 180 buf.append("<"); 181 buf.append(k); 182 buf.append(" JOIN "); 183 buf.append(table); 184 if (tableAlias != null) { 185 buf.append(" "); 186 buf.append(tableAlias); 187 } 188 buf.append(" ON "); 189 buf.append(getClause(null)); 190 if (!whereClauses.isEmpty()) { 191 buf.append(" WHERE "); 192 buf.append(whereClauses); 193 buf.append(" % "); 194 buf.append(whereParams); 195 } 196 buf.append(">"); 197 return buf.toString(); 198 } 199 200}