View Javadoc
1   /*
2    * Copyright 2021 TiKV Project Authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  
18  package org.tikv.common.meta;
19  
20  import static java.util.Objects.requireNonNull;
21  
22  import com.fasterxml.jackson.annotation.JsonCreator;
23  import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
24  import com.fasterxml.jackson.annotation.JsonProperty;
25  import com.google.common.annotations.VisibleForTesting;
26  import com.google.protobuf.ByteString;
27  import com.pingcap.tidb.tipb.ColumnInfo;
28  import java.io.Serializable;
29  import java.util.List;
30  import java.util.Objects;
31  import org.tikv.common.codec.CodecDataOutput;
32  import org.tikv.common.types.DataType;
33  import org.tikv.common.types.DataType.EncodeType;
34  import org.tikv.common.types.DataTypeFactory;
35  import org.tikv.common.types.IntegerType;
36  import org.tikv.common.types.MySQLType;
37  
38  @JsonIgnoreProperties(ignoreUnknown = true)
39  public class TiColumnInfo implements Serializable {
40    @VisibleForTesting private static final int PK_MASK = 0x2;
41    private final long id;
42    private final String name;
43    private final int offset;
44    private final DataType type;
45    private final org.tikv.common.meta.SchemaState schemaState;
46    private final String comment;
47    private final boolean isPrimaryKey;
48    private final String defaultValue;
49    private final String originDefaultValue;
50    private final String defaultValueBit;
51    // this version is from ColumnInfo which is used to address compatible issue.
52    // If version is 0 then timestamp's default value will be read and decoded as local timezone.
53    // if version is 1 then timestamp's default value will be read and decoded as utc.
54    private final long version;
55    private final String generatedExprString;
56  
57    @JsonCreator
58    public TiColumnInfo(
59        @JsonProperty("id") long id,
60        @JsonProperty("name") org.tikv.common.meta.CIStr name,
61        @JsonProperty("offset") int offset,
62        @JsonProperty("type") InternalTypeHolder type,
63        @JsonProperty("state") int schemaState,
64        @JsonProperty("origin_default") String originalDefaultValue,
65        @JsonProperty("default") String defaultValue,
66        @JsonProperty("default_bit") String defaultValueBit,
67        @JsonProperty("comment") String comment,
68        @JsonProperty("version") long version,
69        @JsonProperty("generated_expr_string") String generatedExprString) {
70      this.id = id;
71      this.name = requireNonNull(name, "column name is null").getL();
72      this.offset = offset;
73      this.type = DataTypeFactory.of(requireNonNull(type, "type is null"));
74      this.schemaState = org.tikv.common.meta.SchemaState.fromValue(schemaState);
75      this.comment = comment;
76      this.defaultValue = defaultValue;
77      this.originDefaultValue = originalDefaultValue;
78      this.defaultValueBit = defaultValueBit;
79      // I don't think pk flag should be set on type
80      // Refactor against original tidb code
81      this.isPrimaryKey = (type.getFlag() & PK_MASK) > 0;
82      this.version = version;
83      this.generatedExprString = generatedExprString;
84    }
85  
86    public TiColumnInfo(
87        long id,
88        String name,
89        int offset,
90        DataType type,
91        org.tikv.common.meta.SchemaState schemaState,
92        String originalDefaultValue,
93        String defaultValue,
94        String defaultValueBit,
95        String comment,
96        long version,
97        String generatedExprString) {
98      this.id = id;
99      this.name = requireNonNull(name, "column name is null").toLowerCase();
100     this.offset = offset;
101     this.type = requireNonNull(type, "data type is null");
102     this.schemaState = schemaState;
103     this.comment = comment;
104     this.defaultValue = defaultValue;
105     this.originDefaultValue = originalDefaultValue;
106     this.defaultValueBit = defaultValueBit;
107     this.isPrimaryKey = (type.getFlag() & PK_MASK) > 0;
108     this.version = version;
109     this.generatedExprString = generatedExprString;
110   }
111 
112   @VisibleForTesting
113   public TiColumnInfo(long id, String name, int offset, DataType type, boolean isPrimaryKey) {
114     this.id = id;
115     this.name = requireNonNull(name, "column name is null").toLowerCase();
116     this.offset = offset;
117     this.type = requireNonNull(type, "data type is null");
118     this.schemaState = org.tikv.common.meta.SchemaState.StatePublic;
119     this.comment = "";
120     this.isPrimaryKey = isPrimaryKey;
121     this.originDefaultValue = "1";
122     this.defaultValue = "";
123     this.defaultValueBit = null;
124     this.version = DataType.COLUMN_VERSION_FLAG;
125     this.generatedExprString = "";
126   }
127 
128   static TiColumnInfo getRowIdColumn(int offset) {
129     return new TiColumnInfo(-1, "_tidb_rowid", offset, IntegerType.ROW_ID_TYPE, true);
130   }
131 
132   TiColumnInfo copyWithoutPrimaryKey() {
133     InternalTypeHolder typeHolder = type.toTypeHolder();
134     typeHolder.setFlag(type.getFlag() & (~TiColumnInfo.PK_MASK));
135     DataType newType = DataTypeFactory.of(typeHolder);
136     return new TiColumnInfo(
137         this.id,
138         this.name,
139         this.offset,
140         newType,
141         this.schemaState,
142         this.originDefaultValue,
143         this.defaultValue,
144         this.defaultValueBit,
145         this.comment,
146         this.version,
147         this.generatedExprString);
148   }
149 
150   public long getId() {
151     return this.id;
152   }
153 
154   public String getName() {
155     return this.name;
156   }
157 
158   public boolean matchName(String name) {
159     return this.name.equalsIgnoreCase(name)
160         || String.format("`%s`", this.name).equalsIgnoreCase(name);
161   }
162 
163   public int getOffset() {
164     return this.offset;
165   }
166 
167   public DataType getType() {
168     return type;
169   }
170 
171   public long getLength() {
172     return getType().getLength();
173   }
174 
175   public long getSize() {
176     return getType().getSize();
177   }
178 
179   org.tikv.common.meta.SchemaState getSchemaState() {
180     return schemaState;
181   }
182 
183   public String getComment() {
184     return comment;
185   }
186 
187   public boolean isPrimaryKey() {
188     return isPrimaryKey;
189   }
190 
191   String getDefaultValue() {
192     return defaultValue;
193   }
194 
195   public String getDefaultValueBit() {
196     return defaultValueBit;
197   }
198 
199   // Default value use to stored in DefaultValue field, but now, bit type default value will store
200   // in DefaultValueBit for fix bit default value decode/encode bug.
201   String getOriginDefaultValue() {
202     if (this.getType().getType() == MySQLType.TypeBit
203         && originDefaultValue != null
204         && defaultValueBit != null) {
205       return defaultValueBit;
206     } else {
207       return originDefaultValue;
208     }
209   }
210 
211   private ByteString getOriginDefaultValueAsByteString() {
212     CodecDataOutput cdo = new CodecDataOutput();
213     type.encode(
214         cdo, EncodeType.VALUE, type.getOriginDefaultValue(getOriginDefaultValue(), version));
215     return cdo.toByteString();
216   }
217 
218   public long getVersion() {
219     return version;
220   }
221 
222   public boolean isAutoIncrement() {
223     return this.type.isAutoIncrement();
224   }
225 
226   TiIndexColumn toFakeIndexColumn() {
227     // we don't use original length of column since for a clustered index column
228     // it always full index instead of prefix index
229     return new TiIndexColumn(
230         org.tikv.common.meta.CIStr.newCIStr(getName()), getOffset(), DataType.UNSPECIFIED_LEN);
231   }
232 
233   TiIndexColumn toIndexColumn() {
234     return new TiIndexColumn(
235         org.tikv.common.meta.CIStr.newCIStr(getName()), getOffset(), getType().getLength());
236   }
237 
238   ColumnInfo toProto(TiTableInfo tableInfo) {
239     return ColumnInfo.newBuilder()
240         .setColumnId(id)
241         .setTp(type.getTypeCode())
242         .setCollation(type.getCollationCode())
243         .setColumnLen((int) type.getLength())
244         .setDecimal(type.getDecimal())
245         .setFlag(type.getFlag())
246         .setDefaultVal(getOriginDefaultValueAsByteString())
247         .setPkHandle(tableInfo.isPkHandle() && isPrimaryKey())
248         .addAllElems(type.getElems())
249         .build();
250   }
251 
252   @Override
253   public boolean equals(Object other) {
254     if (other == this) {
255       return true;
256     }
257 
258     if (!(other instanceof TiColumnInfo)) {
259       return false;
260     }
261 
262     TiColumnInfo col = (TiColumnInfo) other;
263     return Objects.equals(id, col.id)
264         && Objects.equals(name, col.name)
265         && Objects.equals(type, col.type)
266         && Objects.equals(schemaState, col.schemaState)
267         && isPrimaryKey == col.isPrimaryKey
268         && Objects.equals(defaultValue, col.defaultValue)
269         && Objects.equals(originDefaultValue, col.originDefaultValue);
270   }
271 
272   @Override
273   public int hashCode() {
274     return Objects.hash(
275         id, name, type, schemaState, isPrimaryKey, defaultValue, originDefaultValue);
276   }
277 
278   public String getGeneratedExprString() {
279     return generatedExprString;
280   }
281 
282   public boolean isGeneratedColumn() {
283     return generatedExprString != null && !generatedExprString.isEmpty();
284   }
285 
286   @JsonIgnoreProperties(ignoreUnknown = true)
287   public static class InternalTypeHolder {
288     private int tp;
289     private int flag;
290     private long flen;
291     private int decimal;
292     private String charset;
293     private String collate;
294     private List<String> elems;
295 
296     @JsonCreator
297     public InternalTypeHolder(
298         @JsonProperty("Tp") int tp,
299         @JsonProperty("Flag") int flag,
300         @JsonProperty("Flen") long flen,
301         @JsonProperty("Decimal") int decimal,
302         @JsonProperty("Charset") String charset,
303         @JsonProperty("Collate") String collate,
304         @JsonProperty("Elems") List<String> elems) {
305       this.tp = tp;
306       this.flag = flag;
307       this.flen = flen;
308       this.decimal = decimal;
309       this.charset = charset;
310       this.collate = collate;
311       this.elems = elems;
312     }
313 
314     public int getTp() {
315       return tp;
316     }
317 
318     public void setTp(int tp) {
319       this.tp = tp;
320     }
321 
322     public int getFlag() {
323       return flag;
324     }
325 
326     public void setFlag(int flag) {
327       this.flag = flag;
328     }
329 
330     public long getFlen() {
331       return flen;
332     }
333 
334     public void setFlen(long flen) {
335       this.flen = flen;
336     }
337 
338     public int getDecimal() {
339       return decimal;
340     }
341 
342     public void setDecimal(int decimal) {
343       this.decimal = decimal;
344     }
345 
346     public String getCharset() {
347       return charset;
348     }
349 
350     public void setCharset(String charset) {
351       this.charset = charset;
352     }
353 
354     public String getCollate() {
355       return collate;
356     }
357 
358     public void setCollate(String collate) {
359       this.collate = collate;
360     }
361 
362     public List<String> getElems() {
363       return elems;
364     }
365 
366     public void setElems(List<String> elems) {
367       this.elems = elems;
368     }
369 
370     interface Builder<E extends DataType> {
371       E build(InternalTypeHolder holder);
372     }
373   }
374 }