|
10 | 10 | package net.sf.jsqlparser.statement.insert; |
11 | 11 |
|
12 | 12 | import net.sf.jsqlparser.expression.Expression; |
| 13 | +import net.sf.jsqlparser.expression.ExpressionVisitor; |
| 14 | +import net.sf.jsqlparser.statement.create.table.Index; |
| 15 | +import java.util.function.Consumer; |
13 | 16 |
|
14 | 17 | import java.io.Serializable; |
15 | 18 | import java.util.*; |
|
23 | 26 | * ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ] |
24 | 27 | * ON CONSTRAINT constraint_name |
25 | 28 | * </pre> |
26 | | - * <p> |
27 | | - * Currently, COLLATE is not supported yet. |
28 | 29 | */ |
29 | 30 | public class InsertConflictTarget implements Serializable { |
| 31 | + private final List<Index.ColumnParams> indexElements = new ArrayList<>(); |
| 32 | + private Expression whereExpression; |
| 33 | + private String constraintName; |
30 | 34 |
|
31 | | - ArrayList<String> indexColumnNames = new ArrayList<>(); |
32 | | - Expression indexExpression; |
33 | | - Expression whereExpression; |
34 | | - String constraintName; |
| 35 | + public InsertConflictTarget() {} |
35 | 36 |
|
36 | 37 | public InsertConflictTarget(String indexColumnName, Expression indexExpression, |
37 | 38 | Expression whereExpression, String constraintName) { |
38 | | - this.indexColumnNames.add(indexColumnName); |
39 | | - this.indexExpression = indexExpression; |
| 39 | + this(indexColumnName == null ? Collections.emptyList() |
| 40 | + : Collections.singletonList(indexColumnName), |
| 41 | + indexExpression, whereExpression, constraintName); |
| 42 | + } |
40 | 43 |
|
| 44 | + public InsertConflictTarget(Collection<String> indexColumnNames, Expression indexExpression, |
| 45 | + Expression whereExpression, String constraintName) { |
| 46 | + if (indexColumnNames != null && !indexColumnNames.isEmpty()) { |
| 47 | + addAllIndexColumnNames(indexColumnNames); |
| 48 | + } else if (indexExpression != null) { |
| 49 | + setIndexExpression(indexExpression); |
| 50 | + } |
41 | 51 | this.whereExpression = whereExpression; |
42 | 52 | this.constraintName = constraintName; |
43 | 53 | } |
44 | 54 |
|
45 | | - public InsertConflictTarget(Collection<String> indexColumnName, Expression indexExpression, |
46 | | - Expression whereExpression, String constraintName) { |
47 | | - this.indexColumnNames.addAll(indexColumnName); |
48 | | - this.indexExpression = indexExpression; |
| 55 | + /** Ordered column and expression keys, including their collation and operator class. */ |
| 56 | + public List<Index.ColumnParams> getIndexElements() { |
| 57 | + return indexElements; |
| 58 | + } |
49 | 59 |
|
50 | | - this.whereExpression = whereExpression; |
51 | | - this.constraintName = constraintName; |
| 60 | + public void setIndexElements(List<Index.ColumnParams> elements) { |
| 61 | + List<Index.ColumnParams> copy = new ArrayList<>(elements); |
| 62 | + indexElements.clear(); |
| 63 | + indexElements.addAll(copy); |
52 | 64 | } |
53 | 65 |
|
| 66 | + public InsertConflictTarget withIndexElements(List<Index.ColumnParams> elements) { |
| 67 | + setIndexElements(elements); |
| 68 | + return this; |
| 69 | + } |
| 70 | + |
| 71 | + /** A mutable view of the column keys; expression keys are available via getIndexElements(). */ |
54 | 72 | public List<String> getIndexColumnNames() { |
55 | | - return indexColumnNames; |
| 73 | + return new AbstractList<String>() { |
| 74 | + private int elementIndex(int index) { |
| 75 | + int columnIndex = 0; |
| 76 | + for (int i = 0; i < indexElements.size(); i++) { |
| 77 | + if (!indexElements.get(i).isExpression() && columnIndex++ == index) { |
| 78 | + return i; |
| 79 | + } |
| 80 | + } |
| 81 | + throw new IndexOutOfBoundsException("Column index: " + index); |
| 82 | + } |
| 83 | + |
| 84 | + @Override |
| 85 | + public String get(int index) { |
| 86 | + return indexElements.get(elementIndex(index)).getColumnName(); |
| 87 | + } |
| 88 | + |
| 89 | + @Override |
| 90 | + public int size() { |
| 91 | + return (int) indexElements.stream().filter(key -> !key.isExpression()).count(); |
| 92 | + } |
| 93 | + |
| 94 | + @Override |
| 95 | + public String set(int index, String name) { |
| 96 | + return indexElements.set(elementIndex(index), new Index.ColumnParams(name)) |
| 97 | + .getColumnName(); |
| 98 | + } |
| 99 | + |
| 100 | + @Override |
| 101 | + public void add(int index, String name) { |
| 102 | + indexElements.add(index == size() ? indexElements.size() : elementIndex(index), |
| 103 | + new Index.ColumnParams(name)); |
| 104 | + } |
| 105 | + |
| 106 | + @Override |
| 107 | + public String remove(int index) { |
| 108 | + return indexElements.remove(elementIndex(index)).getColumnName(); |
| 109 | + } |
| 110 | + }; |
56 | 111 | } |
57 | 112 |
|
58 | 113 | @Deprecated |
59 | 114 | public String getIndexColumnName() { |
60 | | - return indexColumnNames.isEmpty() ? null : indexColumnNames.get(0); |
| 115 | + return getIndexColumnName(0); |
61 | 116 | } |
62 | 117 |
|
63 | 118 | public String getIndexColumnName(int index) { |
64 | | - return indexColumnNames.size() > index ? indexColumnNames.get(index) : null; |
| 119 | + List<String> names = getIndexColumnNames(); |
| 120 | + return names.size() > index ? names.get(index) : null; |
65 | 121 | } |
66 | 122 |
|
67 | | - public boolean addIndexColumnName(String indexColumnName) { |
68 | | - this.indexExpression = null; |
69 | | - return this.indexColumnNames.add(indexColumnName); |
| 123 | + public boolean addIndexColumnName(String name) { |
| 124 | + indexElements.removeIf(Index.ColumnParams::isExpression); |
| 125 | + return indexElements.add(new Index.ColumnParams(name)); |
70 | 126 | } |
71 | 127 |
|
72 | | - public InsertConflictTarget withIndexColumnName(String indexColumnName) { |
73 | | - this.indexExpression = null; |
74 | | - this.indexColumnNames.add(indexColumnName); |
| 128 | + public InsertConflictTarget withIndexColumnName(String name) { |
| 129 | + addIndexColumnName(name); |
75 | 130 | return this; |
76 | 131 | } |
77 | 132 |
|
78 | | - public boolean addAllIndexColumnNames(Collection<String> indexColumnName) { |
79 | | - this.indexExpression = null; |
80 | | - return this.indexColumnNames.addAll(indexColumnName); |
| 133 | + public boolean addAllIndexColumnNames(Collection<String> names) { |
| 134 | + indexElements.removeIf(Index.ColumnParams::isExpression); |
| 135 | + return getIndexColumnNames().addAll(names); |
81 | 136 | } |
82 | 137 |
|
83 | | - |
| 138 | + /** Returns the first expression key, or null for a column-only target. */ |
84 | 139 | public Expression getIndexExpression() { |
85 | | - return indexExpression; |
| 140 | + return indexElements.stream().filter(Index.ColumnParams::isExpression) |
| 141 | + .map(Index.ColumnParams::getExpression).findFirst().orElse(null); |
86 | 142 | } |
87 | 143 |
|
88 | | - public void setIndexExpression(Expression indexExpression) { |
89 | | - this.indexExpression = indexExpression; |
90 | | - this.indexColumnNames.clear(); |
| 144 | + public void setIndexExpression(Expression expression) { |
| 145 | + indexElements.clear(); |
| 146 | + if (expression != null) { |
| 147 | + indexElements.add(new Index.ColumnParams(expression)); |
| 148 | + } |
91 | 149 | } |
92 | 150 |
|
93 | | - public InsertConflictTarget withIndexExpression(Expression indexExpression) { |
94 | | - setIndexExpression(indexExpression); |
| 151 | + public InsertConflictTarget withIndexExpression(Expression expression) { |
| 152 | + setIndexExpression(expression); |
95 | 153 | return this; |
96 | 154 | } |
97 | 155 |
|
@@ -121,35 +179,42 @@ public InsertConflictTarget withConstraintName(String constraintName) { |
121 | 179 | return this; |
122 | 180 | } |
123 | 181 |
|
124 | | - public StringBuilder appendTo(StringBuilder builder) { |
125 | | - if (constraintName == null) { |
126 | | - builder.append(" ( "); |
127 | | - |
128 | | - // @todo: Index Expression is not supported yet |
129 | | - if (!indexColumnNames.isEmpty()) { |
130 | | - boolean insertComma = false; |
131 | | - for (String s : indexColumnNames) { |
132 | | - builder.append(insertComma ? ", " : " ").append(s); |
133 | | - insertComma |= true; |
134 | | - } |
135 | | - } else { |
136 | | - builder.append(" ( ").append(indexExpression).append(" )"); |
| 182 | + /** Visits expression keys and the optional index predicate. */ |
| 183 | + public <S> void accept(ExpressionVisitor<?> visitor, S context) { |
| 184 | + for (Index.ColumnParams element : indexElements) { |
| 185 | + if (element.getExpression() != null) { |
| 186 | + element.getExpression().accept(visitor, context); |
137 | 187 | } |
138 | | - builder.append(" "); |
139 | | - |
140 | | - // @todo: Collate is not supported yet |
| 188 | + } |
| 189 | + if (whereExpression != null) { |
| 190 | + whereExpression.accept(visitor, context); |
| 191 | + } |
| 192 | + } |
141 | 193 |
|
142 | | - builder.append(") "); |
| 194 | + public StringBuilder appendTo(StringBuilder builder) { |
| 195 | + return appendTo(builder, expression -> builder.append(expression)); |
| 196 | + } |
143 | 197 |
|
144 | | - if (whereExpression != null) { |
145 | | - builder.append(" WHERE ").append(whereExpression); |
| 198 | + public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) { |
| 199 | + if (constraintName != null) { |
| 200 | + return builder.append(" ON CONSTRAINT ").append(constraintName); |
| 201 | + } |
| 202 | + builder.append(" ("); |
| 203 | + for (int i = 0; i < indexElements.size(); i++) { |
| 204 | + if (i > 0) { |
| 205 | + builder.append(", "); |
146 | 206 | } |
147 | | - } else { |
148 | | - builder.append(" ON CONSTRAINT ").append(constraintName); |
| 207 | + indexElements.get(i).appendTo(builder, expressionPrinter); |
| 208 | + } |
| 209 | + builder.append(")"); |
| 210 | + if (whereExpression != null) { |
| 211 | + builder.append(" WHERE "); |
| 212 | + expressionPrinter.accept(whereExpression); |
149 | 213 | } |
150 | 214 | return builder; |
151 | 215 | } |
152 | 216 |
|
| 217 | + @Override |
153 | 218 | public String toString() { |
154 | 219 | return appendTo(new StringBuilder()).toString(); |
155 | 220 | } |
|
0 commit comments