content: /*
   * Copyright (c) 2002, 2021, Oracle and/or its affiliates.
   *
   * This program is free software; you can redistribute it and/or modify it under
   * the terms of the GNU General Public License, version 2.0, as published by the
   * Free Software Foundation.
   *
   * This program is also distributed with certain software (including but not
   * limited to OpenSSL) that is licensed under separate terms, as designated in a
   * particular file or component or in included license documentation. The
   * authors of MySQL hereby grant you an additional permission to link the
   * program and your derivative works with the separately licensed software that
   * they have included with MySQL.
   *
   * Without limiting anything contained in the foregoing, this file, which is
   * part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
   * version 1.0, a copy of which can be found at
   * http://oss.oracle.com/licenses/universal-foss-exception.
   *
   * This program is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
   * for more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
   */
  
  package com.mysql.cj.result;
  
  import com.mysql.cj.MysqlType;
  import com.mysql.cj.protocol.ProtocolEntity;
  import com.mysql.cj.util.LazyString;
  
  /**
   * Field is a class used to describe fields in a ResultSet
   */
  public class Field implements ProtocolEntity {
  
      private int collationIndex = 0;
  
      private String encoding = "US-ASCII";
  
      private int colDecimals;
  
      private short colFlag;
  
      private LazyString databaseName = null;
      private LazyString tableName = null;
      private LazyString originalTableName = null;
      private LazyString columnName = null;
      private LazyString originalColumnName = null;
  
      private String fullName = null;
  
      private long length; // Internal length of the field;
  
      private int mysqlTypeId = -1; // the MySQL type ID in legacy protocol
  
      private MysqlType mysqlType = MysqlType.UNKNOWN;
  
      public Field(LazyString databaseName, LazyString tableName, LazyString originalTableName, LazyString columnName, LazyString originalColumnName, long length,
              int mysqlTypeId, short colFlag, int colDecimals, int collationIndex, String encoding, MysqlType mysqlType) {
          this.databaseName = databaseName;
          this.tableName = tableName;
          this.originalTableName = originalTableName;
          this.columnName = columnName;
          this.originalColumnName = originalColumnName;
          this.length = length;
          this.colFlag = colFlag;
          this.colDecimals = colDecimals;
          this.mysqlTypeId = mysqlTypeId;
          this.collationIndex = collationIndex;
  
          // ucs2, utf16, and utf32 cannot be used as a client character set, but if it was received from server under some circumstances we can parse them as utf16
          this.encoding = "UnicodeBig".equals(encoding) ? "UTF-16" : encoding;
  
          // MySQL encodes JSON data with utf8mb4.
          if (mysqlType == MysqlType.JSON) {
              this.encoding = "UTF-8";
          }
  
          this.mysqlType = mysqlType;
  
          adjustFlagsByMysqlType();
      }
  
      private void adjustFlagsByMysqlType() {
  
          switch (this.mysqlType) {
              case BIT:
                  if (this.length > 1) {
                      this.colFlag |= MysqlType.FIELD_FLAG_BINARY;
                      this.colFlag |= MysqlType.FIELD_FLAG_BLOB;
                  }
                  break;
  
              case BINARY:
              case VARBINARY:
                  this.colFlag |= MysqlType.FIELD_FLAG_BINARY;
                  this.colFlag |= MysqlType.FIELD_FLAG_BLOB;
                  break;
  
              case DECIMAL_UNSIGNED:
              case TINYINT_UNSIGNED:
              case SMALLINT_UNSIGNED:
              case INT_UNSIGNED:
              case FLOAT_UNSIGNED:
              case DOUBLE_UNSIGNED:
              case BIGINT_UNSIGNED:
              case MEDIUMINT_UNSIGNED:
                  this.colFlag |= MysqlType.FIELD_FLAG_UNSIGNED;
                  break;
  
              default:
                  break;
          }
  
      }
  
      /**
       * Used by prepared statements to re-use result set data conversion methods
       * when generating bound parameter retrieval instance for statement interceptors.
       *
       * @param tableName
       *            not used
       * @param columnName
       *            not used
       * @param collationIndex
       *            the MySQL collation/character set index
       * @param encoding
       *            encoding of data in this field
       * @param mysqlType
       *            {@link MysqlType}
       * @param length
       *            length in characters or bytes (for BINARY data).
       */
      public Field(String tableName, String columnName, int collationIndex, String encoding, MysqlType mysqlType, int length) {
  
          this.databaseName = new LazyString(null);
          this.tableName = new LazyString(tableName);
          this.originalTableName = new LazyString(null);
          this.columnName = new LazyString(columnName);
          this.originalColumnName = new LazyString(null);
          this.length = length;
          this.mysqlType = mysqlType;
          this.colFlag = 0;
          this.colDecimals = 0;
  
          adjustFlagsByMysqlType();
  
          switch (mysqlType) {
              case CHAR:
              case VARCHAR:
              case TINYTEXT:
              case TEXT:
              case MEDIUMTEXT:
              case LONGTEXT:
              case JSON:
                  // TODO: this becomes moot when DBMD results aren't built from ByteArrayRow
                  // it possibly overrides correct encoding already existing in the Field instance
                  this.collationIndex = collationIndex;
  
                  // ucs2, utf16, and utf32 cannot be used as a client character set, but if it was received from server under some circumstances we can parse them as utf16
                  this.encoding = "UnicodeBig".equals(encoding) ? "UTF-16" : encoding;
  
                  break;
              default:
                  // ignoring charsets for non-string types
          }
      }
  
      /**
       * Returns the Java encoding for this field.
       *
       * @return the Java encoding
       */
      public String getEncoding() {
          return this.encoding;
      }
  
      public String getColumnLabel() {
          return getName();
      }
  
      public String getDatabaseName() {
          return this.databaseName.toString();
      }
  
      public int getDecimals() {
          return this.colDecimals;
      }
  
      public String getFullName() {
          if (this.fullName == null) {
              StringBuilder fullNameBuf = new StringBuilder(this.tableName.length() + 1 + this.columnName.length());
              fullNameBuf.append(this.tableName.toString());
              fullNameBuf.append('.');
              fullNameBuf.append(this.columnName.toString());
              this.fullName = fullNameBuf.toString();
          }
  
          return this.fullName;
      }
  
      public long getLength() {
          return this.length;
      }
  
      public int getMysqlTypeId() {
          return this.mysqlTypeId;
      }
  
      public void setMysqlTypeId(int id) {
          this.mysqlTypeId = id;
      }
  
      public String getName() {
          return this.columnName.toString();
      }
  
      public String getOriginalName() {
          return this.originalColumnName.toString();
      }
  
      public String getOriginalTableName() {
          return this.originalTableName.toString();
      }
  
      public int getJavaType() {
          return this.mysqlType.getJdbcType();
      }
  
      public String getTableName() {
          return this.tableName.toString();
      }
  
      public boolean isAutoIncrement() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_AUTO_INCREMENT) > 0);
      }
  
      public boolean isBinary() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_BINARY) > 0);
      }
  
      public void setBinary() {
          this.colFlag |= MysqlType.FIELD_FLAG_BINARY;
      }
  
      public boolean isBlob() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_BLOB) > 0);
      }
  
      public void setBlob() {
          this.colFlag |= MysqlType.FIELD_FLAG_BLOB;
      }
  
      public boolean isMultipleKey() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_MULTIPLE_KEY) > 0);
      }
  
      public boolean isNotNull() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_NOT_NULL) > 0);
      }
  
      public boolean isPrimaryKey() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_PRIMARY_KEY) > 0);
      }
  
      public boolean isFromFunction() {
          return this.originalTableName.length() == 0;
      }
  
      /**
       * Is this field _definitely_ not writable?
       *
       * @return true if this field can not be written to in an INSERT/UPDATE
       *         statement.
       */
      public boolean isReadOnly() {
          return this.originalColumnName.length() == 0 && this.originalTableName.length() == 0;
      }
  
      public boolean isUniqueKey() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_UNIQUE_KEY) > 0);
      }
  
      public boolean isUnsigned() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_UNSIGNED) > 0);
      }
  
      public boolean isZeroFill() {
          return ((this.colFlag & MysqlType.FIELD_FLAG_ZEROFILL) > 0);
      }
  
      @Override
      public String toString() {
          try {
              StringBuilder asString = new StringBuilder();
              asString.append(super.toString());
              asString.append("[");
              asString.append("dbName=");
              asString.append(this.getDatabaseName());
              asString.append(",tableName=");
              asString.append(this.getTableName());
              asString.append(",originalTableName=");
              asString.append(this.getOriginalTableName());
              asString.append(",columnName=");
              asString.append(this.getName());
              asString.append(",originalColumnName=");
              asString.append(this.getOriginalName());
              asString.append(",mysqlType=");
              asString.append(getMysqlTypeId());
              asString.append("(");
              MysqlType ft = getMysqlType();
              if (ft.equals(MysqlType.UNKNOWN)) {
                  asString.append(" Unknown MySQL Type # ");
                  asString.append(getMysqlTypeId());
              } else {
                  asString.append("FIELD_TYPE_");
                  asString.append(ft.getName());
              }
              asString.append(")");
              asString.append(",sqlType=");
              asString.append(ft.getJdbcType());
              asString.append(",flags=");
  
              if (isAutoIncrement()) {
                  asString.append(" AUTO_INCREMENT");
              }
  
              if (isPrimaryKey()) {
                  asString.append(" PRIMARY_KEY");
              }
  
              if (isUniqueKey()) {
                  asString.append(" UNIQUE_KEY");
              }
  
              if (isBinary()) {
                  asString.append(" BINARY");
              }
  
              if (isBlob()) {
                  asString.append(" BLOB");
              }
  
              if (isMultipleKey()) {
                  asString.append(" MULTI_KEY");
              }
  
              if (isUnsigned()) {
                  asString.append(" UNSIGNED");
              }
  
              if (isZeroFill()) {
                  asString.append(" ZEROFILL");
              }
  
              asString.append(", charsetIndex=");
              asString.append(this.collationIndex);
              asString.append(", charsetName=");
              asString.append(this.encoding);
  
              asString.append("]");
  
              return asString.toString();
          } catch (Throwable t) {
              return super.toString() + "[<unable to generate contents>]";
          }
      }
  
      public boolean isSingleBit() {
          return (this.length <= 1);
      }
  
      public boolean getValueNeedsQuoting() {
          switch (this.mysqlType) {
              case BIGINT:
              case BIGINT_UNSIGNED:
              case BIT:
              case DECIMAL:
              case DECIMAL_UNSIGNED:
              case DOUBLE:
              case DOUBLE_UNSIGNED:
              case INT:
              case INT_UNSIGNED:
              case MEDIUMINT:
              case MEDIUMINT_UNSIGNED:
              case FLOAT:
              case FLOAT_UNSIGNED:
              case SMALLINT:
              case SMALLINT_UNSIGNED:
              case TINYINT:
              case TINYINT_UNSIGNED:
                  return false;
              default:
                  return true;
          }
      }
  
      public int getCollationIndex() {
          return this.collationIndex;
      }
  
      public MysqlType getMysqlType() {
          return this.mysqlType;
      }
  
      public void setMysqlType(MysqlType mysqlType) {
          this.mysqlType = mysqlType;
      }
  
      public short getFlags() {
          return this.colFlag;
      }
  
      public void setFlags(short colFlag) {
          this.colFlag = colFlag;
      }
  }
  