0001: /*
0002: Copyright (C) 2002-2004 MySQL AB
0003:
0004: This program is free software; you can redistribute it and/or modify
0005: it under the terms of version 2 of the GNU General Public License as
0006: published by the Free Software Foundation.
0007:
0008: There are special exceptions to the terms and conditions of the GPL
0009: as it is applied to this software. View the full text of the
0010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
0011: software distribution.
0012:
0013: This program is distributed in the hope that it will be useful,
0014: but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0016: GNU General Public License for more details.
0017:
0018: You should have received a copy of the GNU General Public License
0019: along with this program; if not, write to the Free Software
0020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0021:
0022:
0023:
0024: */
0025: package com.mysql.jdbc;
0026:
0027: import java.io.UnsupportedEncodingException;
0028: import java.sql.SQLException;
0029: import java.sql.Types;
0030: import java.util.regex.PatternSyntaxException;
0031:
0032: /**
0033: * Field is a class used to describe fields in a ResultSet
0034: *
0035: * @author Mark Matthews
0036: * @version $Id: Field.java 6577 2007-09-07 16:12:04Z mmatthews $
0037: */
0038: public class Field {
0039:
0040: private static final int AUTO_INCREMENT_FLAG = 512;
0041:
0042: private static final int NO_CHARSET_INFO = -1;
0043:
0044: private byte[] buffer;
0045:
0046: private int charsetIndex = 0;
0047:
0048: private String charsetName = null;
0049:
0050: private int colDecimals;
0051:
0052: private short colFlag;
0053:
0054: private String collationName = null;
0055:
0056: private ConnectionImpl connection = null;
0057:
0058: private String databaseName = null;
0059:
0060: private int databaseNameLength = -1;
0061:
0062: // database name info
0063: private int databaseNameStart = -1;
0064:
0065: private int defaultValueLength = -1;
0066:
0067: // default value info - from COM_LIST_FIELDS execution
0068: private int defaultValueStart = -1;
0069:
0070: private String fullName = null;
0071:
0072: private String fullOriginalName = null;
0073:
0074: private boolean isImplicitTempTable = false;
0075:
0076: private long length; // Internal length of the field;
0077:
0078: private int mysqlType = -1; // the MySQL type
0079:
0080: private String name; // The Field name
0081:
0082: private int nameLength;
0083:
0084: private int nameStart;
0085:
0086: private String originalColumnName = null;
0087:
0088: private int originalColumnNameLength = -1;
0089:
0090: // column name info (before aliasing)
0091: private int originalColumnNameStart = -1;
0092:
0093: private String originalTableName = null;
0094:
0095: private int originalTableNameLength = -1;
0096:
0097: // table name info (before aliasing)
0098: private int originalTableNameStart = -1;
0099:
0100: private int precisionAdjustFactor = 0;
0101:
0102: private int sqlType = -1; // the java.sql.Type
0103:
0104: private String tableName; // The Name of the Table
0105:
0106: private int tableNameLength;
0107:
0108: private int tableNameStart;
0109:
0110: private boolean useOldNameMetadata = false;
0111:
0112: private boolean isSingleBit;
0113:
0114: private int maxBytesPerChar;
0115:
0116: /**
0117: * Constructor used when communicating with 4.1 and newer servers
0118: */
0119: Field(ConnectionImpl conn, byte[] buffer, int databaseNameStart,
0120: int databaseNameLength, int tableNameStart,
0121: int tableNameLength, int originalTableNameStart,
0122: int originalTableNameLength, int nameStart, int nameLength,
0123: int originalColumnNameStart, int originalColumnNameLength,
0124: long length, int mysqlType, short colFlag, int colDecimals,
0125: int defaultValueStart, int defaultValueLength,
0126: int charsetIndex) throws SQLException {
0127: this .connection = conn;
0128: this .buffer = buffer;
0129: this .nameStart = nameStart;
0130: this .nameLength = nameLength;
0131: this .tableNameStart = tableNameStart;
0132: this .tableNameLength = tableNameLength;
0133: this .length = length;
0134: this .colFlag = colFlag;
0135: this .colDecimals = colDecimals;
0136: this .mysqlType = mysqlType;
0137:
0138: // 4.1 field info...
0139: this .databaseNameStart = databaseNameStart;
0140: this .databaseNameLength = databaseNameLength;
0141:
0142: this .originalTableNameStart = originalTableNameStart;
0143: this .originalTableNameLength = originalTableNameLength;
0144:
0145: this .originalColumnNameStart = originalColumnNameStart;
0146: this .originalColumnNameLength = originalColumnNameLength;
0147:
0148: this .defaultValueStart = defaultValueStart;
0149: this .defaultValueLength = defaultValueLength;
0150:
0151: // If we're not running 4.1 or newer, use the connection's
0152: // charset
0153: this .charsetIndex = charsetIndex;
0154:
0155: // Map MySqlTypes to java.sql Types
0156: this .sqlType = MysqlDefs.mysqlToJavaType(this .mysqlType);
0157:
0158: checkForImplicitTemporaryTable();
0159: // Re-map to 'real' blob type, if we're a BLOB
0160:
0161: if (this .mysqlType == MysqlDefs.FIELD_TYPE_BLOB) {
0162: boolean isFromFunction = this .originalTableNameLength == 0;
0163:
0164: if (this .connection != null
0165: && this .connection.getBlobsAreStrings()
0166: || (this .connection.getFunctionsNeverReturnBlobs() && isFromFunction)) {
0167: this .sqlType = Types.VARCHAR;
0168: this .mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR;
0169: } else if (this .charsetIndex == 63
0170: || !this .connection.versionMeetsMinimum(4, 1, 0)) {
0171: if (this .connection.getUseBlobToStoreUTF8OutsideBMP()
0172: && shouldSetupForUtf8StringInBlob()) {
0173: setupForUtf8StringInBlob();
0174: } else {
0175: setBlobTypeBasedOnLength();
0176: this .sqlType = MysqlDefs
0177: .mysqlToJavaType(this .mysqlType);
0178: }
0179: } else {
0180: // *TEXT masquerading as blob
0181: this .mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING;
0182: this .sqlType = Types.LONGVARCHAR;
0183: }
0184: }
0185:
0186: if (this .sqlType == Types.TINYINT && this .length == 1
0187: && this .connection.getTinyInt1isBit()) {
0188: // Adjust for pseudo-boolean
0189: if (conn.getTinyInt1isBit()) {
0190: if (conn.getTransformedBitIsBoolean()) {
0191: this .sqlType = Types.BOOLEAN;
0192: } else {
0193: this .sqlType = Types.BIT;
0194: }
0195: }
0196:
0197: }
0198:
0199: if (!isNativeNumericType() && !isNativeDateTimeType()) {
0200: this .charsetName = this .connection
0201: .getCharsetNameForIndex(this .charsetIndex);
0202:
0203: // Handle VARBINARY/BINARY (server doesn't have a different type
0204: // for this
0205:
0206: boolean isBinary = isBinary();
0207:
0208: if (this .connection.versionMeetsMinimum(4, 1, 0)
0209: && this .mysqlType == MysqlDefs.FIELD_TYPE_VAR_STRING
0210: && isBinary && this .charsetIndex == 63) {
0211: if (this .isOpaqueBinary()) {
0212: this .sqlType = Types.VARBINARY;
0213: }
0214: }
0215:
0216: if (this .connection.versionMeetsMinimum(4, 1, 0)
0217: && this .mysqlType == MysqlDefs.FIELD_TYPE_STRING
0218: && isBinary && this .charsetIndex == 63) {
0219: //
0220: // Okay, this is a hack, but there's currently no way
0221: // to easily distinguish something like DATE_FORMAT( ..)
0222: // from the "BINARY" column type, other than looking
0223: // at the original column name.
0224: //
0225:
0226: if (isOpaqueBinary()
0227: && !this .connection.getBlobsAreStrings()) {
0228: this .sqlType = Types.BINARY;
0229: }
0230: }
0231:
0232: if (this .mysqlType == MysqlDefs.FIELD_TYPE_BIT) {
0233: this .isSingleBit = (this .length == 0);
0234:
0235: if (this .connection != null
0236: && (this .connection.versionMeetsMinimum(5, 0,
0237: 21) || this .connection
0238: .versionMeetsMinimum(5, 1, 10))
0239: && this .length == 1) {
0240: this .isSingleBit = true;
0241: }
0242:
0243: if (this .isSingleBit) {
0244: this .sqlType = Types.BIT;
0245: } else {
0246: this .sqlType = Types.VARBINARY;
0247: this .colFlag |= 128; // we need to pretend this is a full
0248: this .colFlag |= 16; // binary blob
0249: isBinary = true;
0250: }
0251: }
0252:
0253: //
0254: // Handle TEXT type (special case), Fix proposed by Peter McKeown
0255: //
0256: if ((this .sqlType == java.sql.Types.LONGVARBINARY)
0257: && !isBinary) {
0258: this .sqlType = java.sql.Types.LONGVARCHAR;
0259: } else if ((this .sqlType == java.sql.Types.VARBINARY)
0260: && !isBinary) {
0261: this .sqlType = java.sql.Types.VARCHAR;
0262: }
0263: } else {
0264: this .charsetName = "US-ASCII";
0265: }
0266:
0267: //
0268: // Handle odd values for 'M' for floating point/decimal numbers
0269: //
0270: if (!isUnsigned()) {
0271: switch (this .mysqlType) {
0272: case MysqlDefs.FIELD_TYPE_DECIMAL:
0273: case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
0274: this .precisionAdjustFactor = -1;
0275:
0276: break;
0277: case MysqlDefs.FIELD_TYPE_DOUBLE:
0278: case MysqlDefs.FIELD_TYPE_FLOAT:
0279: this .precisionAdjustFactor = 1;
0280:
0281: break;
0282: }
0283: } else {
0284: switch (this .mysqlType) {
0285: case MysqlDefs.FIELD_TYPE_DOUBLE:
0286: case MysqlDefs.FIELD_TYPE_FLOAT:
0287: this .precisionAdjustFactor = 1;
0288:
0289: break;
0290: }
0291: }
0292: }
0293:
0294: private boolean shouldSetupForUtf8StringInBlob()
0295: throws SQLException {
0296: String includePattern = this .connection
0297: .getUtf8OutsideBmpIncludedColumnNamePattern();
0298: String excludePattern = this .connection
0299: .getUtf8OutsideBmpExcludedColumnNamePattern();
0300:
0301: if (excludePattern != null
0302: && !StringUtils.isEmptyOrWhitespaceOnly(excludePattern)) {
0303: try {
0304: if (getOriginalName().matches(excludePattern)) {
0305: if (includePattern != null
0306: && !StringUtils
0307: .isEmptyOrWhitespaceOnly(includePattern)) {
0308: try {
0309: if (getOriginalName().matches(
0310: includePattern)) {
0311: return true;
0312: }
0313: } catch (PatternSyntaxException pse) {
0314: SQLException sqlEx = SQLError
0315: .createSQLException(
0316: "Illegal regex specified for \"utf8OutsideBmpIncludedColumnNamePattern\"",
0317: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
0318:
0319: if (!this .connection.getParanoid()) {
0320: sqlEx.initCause(pse);
0321: }
0322:
0323: throw sqlEx;
0324: }
0325: }
0326:
0327: return false;
0328: }
0329: } catch (PatternSyntaxException pse) {
0330: SQLException sqlEx = SQLError
0331: .createSQLException(
0332: "Illegal regex specified for \"utf8OutsideBmpExcludedColumnNamePattern\"",
0333: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
0334:
0335: if (!this .connection.getParanoid()) {
0336: sqlEx.initCause(pse);
0337: }
0338:
0339: throw sqlEx;
0340: }
0341: }
0342:
0343: return true;
0344: }
0345:
0346: private void setupForUtf8StringInBlob() {
0347: if (this .length == MysqlDefs.LENGTH_TINYBLOB
0348: || this .length == MysqlDefs.LENGTH_BLOB) {
0349: this .mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR;
0350: this .sqlType = Types.VARCHAR;
0351: } else {
0352: this .mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING;
0353: this .sqlType = Types.LONGVARCHAR;
0354: }
0355:
0356: this .charsetIndex = 33;
0357: }
0358:
0359: /**
0360: * Constructor used when communicating with pre 4.1 servers
0361: */
0362: Field(ConnectionImpl conn, byte[] buffer, int nameStart,
0363: int nameLength, int tableNameStart, int tableNameLength,
0364: int length, int mysqlType, short colFlag, int colDecimals)
0365: throws SQLException {
0366: this (conn, buffer, -1, -1, tableNameStart, tableNameLength, -1,
0367: -1, nameStart, nameLength, -1, -1, length, mysqlType,
0368: colFlag, colDecimals, -1, -1, NO_CHARSET_INFO);
0369: }
0370:
0371: /**
0372: * Constructor used by DatabaseMetaData methods.
0373: */
0374: Field(String tableName, String columnName, int jdbcType, int length) {
0375: this .tableName = tableName;
0376: this .name = columnName;
0377: this .length = length;
0378: this .sqlType = jdbcType;
0379: this .colFlag = 0;
0380: this .colDecimals = 0;
0381: }
0382:
0383: /**
0384: * Used by prepared statements to re-use result set data conversion methods
0385: * when generating bound parmeter retrieval instance for statement
0386: * interceptors.
0387: *
0388: * @param tableName
0389: * not used
0390: * @param columnName
0391: * not used
0392: * @param charsetIndex
0393: * the MySQL collation/character set index
0394: * @param jdbcType
0395: * from java.sql.Types
0396: * @param length
0397: * length in characters or bytes (for BINARY data).
0398: */
0399: Field(String tableName, String columnName, int charsetIndex,
0400: int jdbcType, int length) {
0401: this .tableName = tableName;
0402: this .name = columnName;
0403: this .length = length;
0404: this .sqlType = jdbcType;
0405: this .colFlag = 0;
0406: this .colDecimals = 0;
0407: this .charsetIndex = charsetIndex;
0408: }
0409:
0410: private void checkForImplicitTemporaryTable() {
0411: this .isImplicitTempTable = this .tableNameLength > 5
0412: && this .buffer[tableNameStart] == (byte) '#'
0413: && this .buffer[tableNameStart + 1] == (byte) 's'
0414: && this .buffer[tableNameStart + 2] == (byte) 'q'
0415: && this .buffer[tableNameStart + 3] == (byte) 'l'
0416: && this .buffer[tableNameStart + 4] == (byte) '_';
0417: }
0418:
0419: /**
0420: * Returns the character set (if known) for this field.
0421: *
0422: * @return the character set
0423: */
0424: public String getCharacterSet() throws SQLException {
0425: return this .charsetName;
0426: }
0427:
0428: public synchronized String getCollation() throws SQLException {
0429: if (this .collationName == null) {
0430: if (this .connection != null) {
0431: if (this .connection.versionMeetsMinimum(4, 1, 0)) {
0432: if (this .connection.getUseDynamicCharsetInfo()) {
0433: java.sql.DatabaseMetaData dbmd = this .connection
0434: .getMetaData();
0435:
0436: String quotedIdStr = dbmd
0437: .getIdentifierQuoteString();
0438:
0439: if (" ".equals(quotedIdStr)) { //$NON-NLS-1$
0440: quotedIdStr = ""; //$NON-NLS-1$
0441: }
0442:
0443: String csCatalogName = getDatabaseName();
0444: String csTableName = getOriginalTableName();
0445: String csColumnName = getOriginalName();
0446:
0447: if (csCatalogName != null
0448: && csCatalogName.length() != 0
0449: && csTableName != null
0450: && csTableName.length() != 0
0451: && csColumnName != null
0452: && csColumnName.length() != 0) {
0453: StringBuffer queryBuf = new StringBuffer(
0454: csCatalogName.length()
0455: + csTableName.length() + 28);
0456: queryBuf.append("SHOW FULL COLUMNS FROM "); //$NON-NLS-1$
0457: queryBuf.append(quotedIdStr);
0458: queryBuf.append(csCatalogName);
0459: queryBuf.append(quotedIdStr);
0460: queryBuf.append("."); //$NON-NLS-1$
0461: queryBuf.append(quotedIdStr);
0462: queryBuf.append(csTableName);
0463: queryBuf.append(quotedIdStr);
0464:
0465: java.sql.Statement collationStmt = null;
0466: java.sql.ResultSet collationRs = null;
0467:
0468: try {
0469: collationStmt = this .connection
0470: .createStatement();
0471:
0472: collationRs = collationStmt
0473: .executeQuery(queryBuf
0474: .toString());
0475:
0476: while (collationRs.next()) {
0477: if (csColumnName.equals(collationRs
0478: .getString("Field"))) { //$NON-NLS-1$
0479: this .collationName = collationRs
0480: .getString("Collation"); //$NON-NLS-1$
0481:
0482: break;
0483: }
0484: }
0485: } finally {
0486: if (collationRs != null) {
0487: collationRs.close();
0488: collationRs = null;
0489: }
0490:
0491: if (collationStmt != null) {
0492: collationStmt.close();
0493: collationStmt = null;
0494: }
0495: }
0496: }
0497: } else {
0498: this .collationName = CharsetMapping.INDEX_TO_COLLATION[charsetIndex];
0499: }
0500: }
0501: }
0502: }
0503:
0504: return this .collationName;
0505: }
0506:
0507: public String getColumnLabel() throws SQLException {
0508: return getName(); // column name if not aliased, alias if used
0509: }
0510:
0511: /**
0512: * DOCUMENT ME!
0513: *
0514: * @return DOCUMENT ME!
0515: */
0516: public String getDatabaseName() throws SQLException {
0517: if ((this .databaseName == null)
0518: && (this .databaseNameStart != -1)
0519: && (this .databaseNameLength != -1)) {
0520: this .databaseName = getStringFromBytes(
0521: this .databaseNameStart, this .databaseNameLength);
0522: }
0523:
0524: return this .databaseName;
0525: }
0526:
0527: int getDecimals() {
0528: return this .colDecimals;
0529: }
0530:
0531: /**
0532: * DOCUMENT ME!
0533: *
0534: * @return DOCUMENT ME!
0535: */
0536: public String getFullName() throws SQLException {
0537: if (this .fullName == null) {
0538: StringBuffer fullNameBuf = new StringBuffer(getTableName()
0539: .length()
0540: + 1 + getName().length());
0541: fullNameBuf.append(this .tableName);
0542:
0543: // much faster to append a char than a String
0544: fullNameBuf.append('.');
0545: fullNameBuf.append(this .name);
0546: this .fullName = fullNameBuf.toString();
0547: fullNameBuf = null;
0548: }
0549:
0550: return this .fullName;
0551: }
0552:
0553: /**
0554: * DOCUMENT ME!
0555: *
0556: * @return DOCUMENT ME!
0557: */
0558: public String getFullOriginalName() throws SQLException {
0559: getOriginalName();
0560:
0561: if (this .originalColumnName == null) {
0562: return null; // we don't have this information
0563: }
0564:
0565: if (this .fullName == null) {
0566: StringBuffer fullOriginalNameBuf = new StringBuffer(
0567: getOriginalTableName().length() + 1
0568: + getOriginalName().length());
0569: fullOriginalNameBuf.append(this .originalTableName);
0570:
0571: // much faster to append a char than a String
0572: fullOriginalNameBuf.append('.');
0573: fullOriginalNameBuf.append(this .originalColumnName);
0574: this .fullOriginalName = fullOriginalNameBuf.toString();
0575: fullOriginalNameBuf = null;
0576: }
0577:
0578: return this .fullOriginalName;
0579: }
0580:
0581: /**
0582: * DOCUMENT ME!
0583: *
0584: * @return DOCUMENT ME!
0585: */
0586: public long getLength() {
0587: return this .length;
0588: }
0589:
0590: public synchronized int getMaxBytesPerCharacter()
0591: throws SQLException {
0592: if (this .maxBytesPerChar == 0) {
0593: this .maxBytesPerChar = this .connection
0594: .getMaxBytesPerChar(getCharacterSet());
0595: }
0596:
0597: return this .maxBytesPerChar;
0598: }
0599:
0600: /**
0601: * DOCUMENT ME!
0602: *
0603: * @return DOCUMENT ME!
0604: */
0605: public int getMysqlType() {
0606: return this .mysqlType;
0607: }
0608:
0609: /**
0610: * DOCUMENT ME!
0611: *
0612: * @return DOCUMENT ME!
0613: */
0614: public String getName() throws SQLException {
0615: if (this .name == null) {
0616: this .name = getStringFromBytes(this .nameStart,
0617: this .nameLength);
0618: }
0619:
0620: return this .name;
0621: }
0622:
0623: public String getNameNoAliases() throws SQLException {
0624: if (this .useOldNameMetadata) {
0625: return getName();
0626: }
0627:
0628: if (this .connection != null
0629: && this .connection.versionMeetsMinimum(4, 1, 0)) {
0630: return getOriginalName();
0631: }
0632:
0633: return getName();
0634: }
0635:
0636: /**
0637: * DOCUMENT ME!
0638: *
0639: * @return DOCUMENT ME!
0640: */
0641: public String getOriginalName() throws SQLException {
0642: if ((this .originalColumnName == null)
0643: && (this .originalColumnNameStart != -1)
0644: && (this .originalColumnNameLength != -1)) {
0645: this .originalColumnName = getStringFromBytes(
0646: this .originalColumnNameStart,
0647: this .originalColumnNameLength);
0648: }
0649:
0650: return this .originalColumnName;
0651: }
0652:
0653: /**
0654: * DOCUMENT ME!
0655: *
0656: * @return DOCUMENT ME!
0657: */
0658: public String getOriginalTableName() throws SQLException {
0659: if ((this .originalTableName == null)
0660: && (this .originalTableNameStart != -1)
0661: && (this .originalTableNameLength != -1)) {
0662: this .originalTableName = getStringFromBytes(
0663: this .originalTableNameStart,
0664: this .originalTableNameLength);
0665: }
0666:
0667: return this .originalTableName;
0668: }
0669:
0670: /**
0671: * Returns amount of correction that should be applied to the precision
0672: * value.
0673: *
0674: * Different versions of MySQL report different precision values.
0675: *
0676: * @return the amount to adjust precision value by.
0677: */
0678: public int getPrecisionAdjustFactor() {
0679: return this .precisionAdjustFactor;
0680: }
0681:
0682: /**
0683: * DOCUMENT ME!
0684: *
0685: * @return DOCUMENT ME!
0686: */
0687: public int getSQLType() {
0688: return this .sqlType;
0689: }
0690:
0691: /**
0692: * Create a string with the correct charset encoding from the byte-buffer
0693: * that contains the data for this field
0694: */
0695: private String getStringFromBytes(int stringStart, int stringLength)
0696: throws SQLException {
0697: if ((stringStart == -1) || (stringLength == -1)) {
0698: return null;
0699: }
0700:
0701: String stringVal = null;
0702:
0703: if (this .connection != null) {
0704: if (this .connection.getUseUnicode()) {
0705: String encoding = this .connection
0706: .getCharacterSetMetadata();
0707:
0708: if (encoding == null) {
0709: encoding = connection.getEncoding();
0710: }
0711:
0712: if (encoding != null) {
0713: SingleByteCharsetConverter converter = null;
0714:
0715: if (this .connection != null) {
0716: converter = this .connection
0717: .getCharsetConverter(encoding);
0718: }
0719:
0720: if (converter != null) { // we have a converter
0721: stringVal = converter.toString(this .buffer,
0722: stringStart, stringLength);
0723: } else {
0724: // we have no converter, use JVM converter
0725: byte[] stringBytes = new byte[stringLength];
0726:
0727: int endIndex = stringStart + stringLength;
0728: int pos = 0;
0729:
0730: for (int i = stringStart; i < endIndex; i++) {
0731: stringBytes[pos++] = this .buffer[i];
0732: }
0733:
0734: try {
0735: stringVal = new String(stringBytes,
0736: encoding);
0737: } catch (UnsupportedEncodingException ue) {
0738: throw new RuntimeException(Messages
0739: .getString("Field.12") + encoding //$NON-NLS-1$
0740: + Messages.getString("Field.13")); //$NON-NLS-1$
0741: }
0742: }
0743: } else {
0744: // we have no encoding, use JVM standard charset
0745: stringVal = StringUtils.toAsciiString(this .buffer,
0746: stringStart, stringLength);
0747: }
0748: } else {
0749: // we are not using unicode, so use JVM standard charset
0750: stringVal = StringUtils.toAsciiString(this .buffer,
0751: stringStart, stringLength);
0752: }
0753: } else {
0754: // we don't have a connection, so punt
0755: stringVal = StringUtils.toAsciiString(this .buffer,
0756: stringStart, stringLength);
0757: }
0758:
0759: return stringVal;
0760: }
0761:
0762: /**
0763: * DOCUMENT ME!
0764: *
0765: * @return DOCUMENT ME!
0766: */
0767: public String getTable() throws SQLException {
0768: return getTableName();
0769: }
0770:
0771: /**
0772: * DOCUMENT ME!
0773: *
0774: * @return DOCUMENT ME!
0775: */
0776: public String getTableName() throws SQLException {
0777: if (this .tableName == null) {
0778: this .tableName = getStringFromBytes(this .tableNameStart,
0779: this .tableNameLength);
0780: }
0781:
0782: return this .tableName;
0783: }
0784:
0785: public String getTableNameNoAliases() throws SQLException {
0786: if (this .connection.versionMeetsMinimum(4, 1, 0)) {
0787: return getOriginalTableName();
0788: }
0789:
0790: return getTableName(); // pre-4.1, no aliases returned
0791: }
0792:
0793: /**
0794: * DOCUMENT ME!
0795: *
0796: * @return DOCUMENT ME!
0797: */
0798: public boolean isAutoIncrement() {
0799: return ((this .colFlag & AUTO_INCREMENT_FLAG) > 0);
0800: }
0801:
0802: /**
0803: * DOCUMENT ME!
0804: *
0805: * @return DOCUMENT ME!
0806: */
0807: public boolean isBinary() {
0808: return ((this .colFlag & 128) > 0);
0809: }
0810:
0811: /**
0812: * DOCUMENT ME!
0813: *
0814: * @return DOCUMENT ME!
0815: */
0816: public boolean isBlob() {
0817: return ((this .colFlag & 16) > 0);
0818: }
0819:
0820: /**
0821: * Is this field owned by a server-created temporary table?
0822: *
0823: * @return
0824: */
0825: private boolean isImplicitTemporaryTable() {
0826: return this .isImplicitTempTable;
0827: }
0828:
0829: /**
0830: * DOCUMENT ME!
0831: *
0832: * @return DOCUMENT ME!
0833: */
0834: public boolean isMultipleKey() {
0835: return ((this .colFlag & 8) > 0);
0836: }
0837:
0838: boolean isNotNull() {
0839: return ((this .colFlag & 1) > 0);
0840: }
0841:
0842: boolean isOpaqueBinary() throws SQLException {
0843:
0844: //
0845: // Detect CHAR(n) CHARACTER SET BINARY which is a synonym for
0846: // fixed-length binary types
0847: //
0848:
0849: if (this .charsetIndex == 63
0850: && isBinary()
0851: && (this .getMysqlType() == MysqlDefs.FIELD_TYPE_STRING || this
0852: .getMysqlType() == MysqlDefs.FIELD_TYPE_VAR_STRING)) {
0853:
0854: if (this .originalTableNameLength == 0
0855: && (this .connection != null && !this .connection
0856: .versionMeetsMinimum(5, 0, 25))) {
0857: return false; // Probably from function
0858: }
0859:
0860: // Okay, queries resolved by temp tables also have this 'signature',
0861: // check for that
0862:
0863: return !isImplicitTemporaryTable();
0864: }
0865:
0866: return (this .connection.versionMeetsMinimum(4, 1, 0) && "binary"
0867: .equalsIgnoreCase(getCharacterSet()));
0868:
0869: }
0870:
0871: /**
0872: * DOCUMENT ME!
0873: *
0874: * @return DOCUMENT ME!
0875: */
0876: public boolean isPrimaryKey() {
0877: return ((this .colFlag & 2) > 0);
0878: }
0879:
0880: /**
0881: * Is this field _definitely_ not writable?
0882: *
0883: * @return true if this field can not be written to in an INSERT/UPDATE
0884: * statement.
0885: */
0886: boolean isReadOnly() throws SQLException {
0887: if (this .connection.versionMeetsMinimum(4, 1, 0)) {
0888: String orgColumnName = getOriginalName();
0889: String orgTableName = getOriginalTableName();
0890:
0891: return !(orgColumnName != null
0892: && orgColumnName.length() > 0
0893: && orgTableName != null && orgTableName.length() > 0);
0894: }
0895:
0896: return false; // we can't tell definitively in this case.
0897: }
0898:
0899: /**
0900: * DOCUMENT ME!
0901: *
0902: * @return DOCUMENT ME!
0903: */
0904: public boolean isUniqueKey() {
0905: return ((this .colFlag & 4) > 0);
0906: }
0907:
0908: /**
0909: * DOCUMENT ME!
0910: *
0911: * @return DOCUMENT ME!
0912: */
0913: public boolean isUnsigned() {
0914: return ((this .colFlag & 32) > 0);
0915: }
0916:
0917: /**
0918: * DOCUMENT ME!
0919: *
0920: * @return DOCUMENT ME!
0921: */
0922: public boolean isZeroFill() {
0923: return ((this .colFlag & 64) > 0);
0924: }
0925:
0926: //
0927: // MySQL only has one protocol-level BLOB type that it exposes
0928: // which is FIELD_TYPE_BLOB, although we can divine what the
0929: // actual type is by the length reported ...
0930: //
0931: private void setBlobTypeBasedOnLength() {
0932: if (this .length == MysqlDefs.LENGTH_TINYBLOB) {
0933: this .mysqlType = MysqlDefs.FIELD_TYPE_TINY_BLOB;
0934: } else if (this .length == MysqlDefs.LENGTH_BLOB) {
0935: this .mysqlType = MysqlDefs.FIELD_TYPE_BLOB;
0936: } else if (this .length == MysqlDefs.LENGTH_MEDIUMBLOB) {
0937: this .mysqlType = MysqlDefs.FIELD_TYPE_MEDIUM_BLOB;
0938: } else if (this .length == MysqlDefs.LENGTH_LONGBLOB) {
0939: this .mysqlType = MysqlDefs.FIELD_TYPE_LONG_BLOB;
0940: }
0941: }
0942:
0943: private boolean isNativeNumericType() {
0944: return ((this .mysqlType >= MysqlDefs.FIELD_TYPE_TINY && this .mysqlType <= MysqlDefs.FIELD_TYPE_DOUBLE)
0945: || this .mysqlType == MysqlDefs.FIELD_TYPE_LONGLONG || this .mysqlType == MysqlDefs.FIELD_TYPE_YEAR);
0946: }
0947:
0948: private boolean isNativeDateTimeType() {
0949: return (this .mysqlType == MysqlDefs.FIELD_TYPE_DATE
0950: || this .mysqlType == MysqlDefs.FIELD_TYPE_NEWDATE
0951: || this .mysqlType == MysqlDefs.FIELD_TYPE_DATETIME
0952: || this .mysqlType == MysqlDefs.FIELD_TYPE_TIME || this .mysqlType == MysqlDefs.FIELD_TYPE_TIMESTAMP);
0953: }
0954:
0955: /**
0956: * DOCUMENT ME!
0957: *
0958: * @param conn
0959: * DOCUMENT ME!
0960: */
0961: public void setConnection(ConnectionImpl conn) {
0962: this .connection = conn;
0963:
0964: this .charsetName = this .connection.getEncoding();
0965: }
0966:
0967: void setMysqlType(int type) {
0968: this .mysqlType = type;
0969: this .sqlType = MysqlDefs.mysqlToJavaType(this .mysqlType);
0970: }
0971:
0972: protected void setUseOldNameMetadata(boolean useOldNameMetadata) {
0973: this .useOldNameMetadata = useOldNameMetadata;
0974: }
0975:
0976: public String toString() {
0977: try {
0978: StringBuffer asString = new StringBuffer();
0979: asString.append(super .toString());
0980: asString.append("[");
0981: asString.append("catalog=");
0982: asString.append(this .getDatabaseName());
0983: asString.append(",tableName=");
0984: asString.append(this .getTableName());
0985: asString.append(",originalTableName=");
0986: asString.append(this .getOriginalTableName());
0987: asString.append(",columnName=");
0988: asString.append(this .getName());
0989: asString.append(",originalColumnName=");
0990: asString.append(this .getOriginalName());
0991: asString.append(",mysqlType=");
0992: asString.append(getMysqlType());
0993: asString.append("(");
0994: asString.append(MysqlDefs.typeToName(getMysqlType()));
0995: asString.append(")");
0996: asString.append(",flags=");
0997:
0998: if (isAutoIncrement()) {
0999: asString.append(" AUTO_INCREMENT");
1000: }
1001:
1002: if (isPrimaryKey()) {
1003: asString.append(" PRIMARY_KEY");
1004: }
1005:
1006: if (isUniqueKey()) {
1007: asString.append(" UNIQUE_KEY");
1008: }
1009:
1010: if (isBinary()) {
1011: asString.append(" BINARY");
1012: }
1013:
1014: if (isBlob()) {
1015: asString.append(" BLOB");
1016: }
1017:
1018: if (isMultipleKey()) {
1019: asString.append(" MULTI_KEY");
1020: }
1021:
1022: if (isUnsigned()) {
1023: asString.append(" UNSIGNED");
1024: }
1025:
1026: if (isZeroFill()) {
1027: asString.append(" ZEROFILL");
1028: }
1029:
1030: asString.append(", charsetIndex=");
1031: asString.append(this .charsetIndex);
1032: asString.append(", charsetName=");
1033: asString.append(this .charsetName);
1034:
1035: //if (this.buffer != null) {
1036: // asString.append("\n\nData as received from server:\n\n");
1037: // asString.append(StringUtils.dumpAsHex(this.buffer,
1038: // this.buffer.length));
1039: //}
1040:
1041: asString.append("]");
1042:
1043: return asString.toString();
1044: } catch (Throwable t) {
1045: return super .toString();
1046: }
1047: }
1048:
1049: protected boolean isSingleBit() {
1050: return this.isSingleBit;
1051: }
1052: }
|