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<>();
070
071    /** Additional WHERE clauses parameters. */
072    public final List<Serializable> whereParams = new LinkedList<>();
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 sb = new StringBuilder();
180        sb.append("<");
181        sb.append(k);
182        sb.append(" JOIN ");
183        sb.append(table);
184        if (tableAlias != null) {
185            sb.append(" ");
186            sb.append(tableAlias);
187        }
188        sb.append(" ON ");
189        sb.append(getClause(null));
190        if (!whereClauses.isEmpty()) {
191            sb.append(" WHERE ");
192            sb.append(whereClauses);
193            sb.append(" % ");
194            sb.append(whereParams);
195        }
196        sb.append(">");
197        return sb.toString();
198    }
199
200}