2
2
Copyright (C) 2002-2004 MySQL AB
4
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of version 2 of the GNU General Public License as
5
it under the terms of version 2 of the GNU General Public License as
6
6
published by the Free Software Foundation.
8
There are special exceptions to the terms and conditions of the GPL
9
as it is applied to this software. View the full text of the
10
exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
8
There are special exceptions to the terms and conditions of the GPL
9
as it is applied to this software. View the full text of the
10
exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
11
11
software distribution.
13
13
This program is distributed in the hope that it will be useful,
27
27
import java.io.UnsupportedEncodingException;
28
28
import java.sql.SQLException;
29
29
import java.sql.Types;
30
import java.util.regex.PatternSyntaxException;
32
33
* Field is a class used to describe fields in a ResultSet
34
35
* @author Mark Matthews
35
* @version $Id: Field.java 5639 2006-08-16 14:18:23Z mmatthews $
36
* @version $Id: Field.java 6577 2007-09-07 16:12:04Z mmatthews $
37
38
public class Field {
38
// ~ Static fields/initializers
39
// ---------------------------------------------
41
40
private static final int AUTO_INCREMENT_FLAG = 512;
43
42
private static final int NO_CHARSET_INFO = -1;
46
// --------------------------------------------------------
48
44
private byte[] buffer;
50
46
private int charsetIndex = 0;
110
106
private int tableNameLength;
112
108
private int tableNameStart;
114
110
private boolean useOldNameMetadata = false;
116
112
private boolean isSingleBit;
119
// -----------------------------------------------------------
114
private int maxBytesPerChar;
122
117
* Constructor used when communicating with 4.1 and newer servers
124
Field(Connection conn, byte[] buffer, int databaseNameStart,
119
Field(ConnectionImpl conn, byte[] buffer, int databaseNameStart,
125
120
int databaseNameLength, int tableNameStart, int tableNameLength,
126
121
int originalTableNameStart, int originalTableNameLength,
127
122
int nameStart, int nameLength, int originalColumnNameStart,
157
152
this.charsetIndex = charsetIndex;
160
155
// Map MySqlTypes to java.sql Types
161
156
this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
158
checkForImplicitTemporaryTable();
163
159
// Re-map to 'real' blob type, if we're a BLOB
165
161
if (this.mysqlType == MysqlDefs.FIELD_TYPE_BLOB) {
166
if (this.charsetIndex == 63 ||
162
boolean isFromFunction = this.originalTableNameLength == 0;
164
if (this.connection != null && this.connection.getBlobsAreStrings() ||
165
(this.connection.getFunctionsNeverReturnBlobs() && isFromFunction)) {
166
this.sqlType = Types.VARCHAR;
167
this.mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR;
168
} else if (this.charsetIndex == 63 ||
167
169
!this.connection.versionMeetsMinimum(4, 1, 0)) {
168
setBlobTypeBasedOnLength();
169
this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
170
if (this.connection.getUseBlobToStoreUTF8OutsideBMP()
171
&& shouldSetupForUtf8StringInBlob()) {
172
setupForUtf8StringInBlob();
174
setBlobTypeBasedOnLength();
175
this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
171
178
// *TEXT masquerading as blob
172
179
this.mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING;
190
197
if (!isNativeNumericType() && !isNativeDateTimeType()) {
191
198
this.charsetName = this.connection
192
199
.getCharsetNameForIndex(this.charsetIndex);
195
202
// Handle VARBINARY/BINARY (server doesn't have a different type
198
205
boolean isBinary = isBinary();
200
207
if (this.connection.versionMeetsMinimum(4, 1, 0) &&
201
this.mysqlType == MysqlDefs.FIELD_TYPE_VAR_STRING &&
208
this.mysqlType == MysqlDefs.FIELD_TYPE_VAR_STRING &&
203
210
this.charsetIndex == 63) {
204
211
if (this.isOpaqueBinary()) {
205
212
this.sqlType = Types.VARBINARY;
209
216
if (this.connection.versionMeetsMinimum(4, 1, 0) &&
210
this.mysqlType == MysqlDefs.FIELD_TYPE_STRING &&
217
this.mysqlType == MysqlDefs.FIELD_TYPE_STRING &&
211
218
isBinary && this.charsetIndex == 63) {
213
220
// Okay, this is a hack, but there's currently no way
215
222
// from the "BINARY" column type, other than looking
216
223
// at the original column name.
219
if (isOpaqueBinary()) {
226
if (isOpaqueBinary() && !this.connection.getBlobsAreStrings()) {
220
227
this.sqlType = Types.BINARY;
226
233
if (this.mysqlType == MysqlDefs.FIELD_TYPE_BIT) {
227
234
this.isSingleBit = (this.length == 0);
229
236
if (this.connection != null && (this.connection.versionMeetsMinimum(5, 0, 21) ||
230
237
this.connection.versionMeetsMinimum(5, 1, 10)) && this.length == 1) {
231
238
this.isSingleBit = true;
234
241
if (this.isSingleBit) {
235
242
this.sqlType = Types.BIT;
237
244
this.sqlType = Types.VARBINARY;
238
245
this.colFlag |= 128; // we need to pretend this is a full
239
246
this.colFlag |= 16; // binary blob
244
252
// Handle TEXT type (special case), Fix proposed by Peter McKeown
290
private boolean shouldSetupForUtf8StringInBlob() throws SQLException {
291
String includePattern = this.connection
292
.getUtf8OutsideBmpIncludedColumnNamePattern();
293
String excludePattern = this.connection
294
.getUtf8OutsideBmpExcludedColumnNamePattern();
296
if (excludePattern != null
297
&& !StringUtils.isEmptyOrWhitespaceOnly(excludePattern)) {
299
if (getOriginalName().matches(excludePattern)) {
300
if (includePattern != null
301
&& !StringUtils.isEmptyOrWhitespaceOnly(includePattern)) {
303
if (getOriginalName().matches(includePattern)) {
306
} catch (PatternSyntaxException pse) {
307
SQLException sqlEx = SQLError
309
"Illegal regex specified for \"utf8OutsideBmpIncludedColumnNamePattern\"",
310
SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
312
if (!this.connection.getParanoid()) {
313
sqlEx.initCause(pse);
322
} catch (PatternSyntaxException pse) {
323
SQLException sqlEx = SQLError
325
"Illegal regex specified for \"utf8OutsideBmpExcludedColumnNamePattern\"",
326
SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
328
if (!this.connection.getParanoid()) {
329
sqlEx.initCause(pse);
339
private void setupForUtf8StringInBlob() {
340
if (this.length == MysqlDefs.LENGTH_TINYBLOB || this.length == MysqlDefs.LENGTH_BLOB) {
341
this.mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR;
342
this.sqlType = Types.VARCHAR;
344
this.mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING;
345
this.sqlType = Types.LONGVARCHAR;
348
this.charsetIndex = 33;
285
352
* Constructor used when communicating with pre 4.1 servers
287
Field(Connection conn, byte[] buffer, int nameStart, int nameLength,
354
Field(ConnectionImpl conn, byte[] buffer, int nameStart, int nameLength,
288
355
int tableNameStart, int tableNameLength, int length, int mysqlType,
289
356
short colFlag, int colDecimals) throws SQLException {
290
357
this(conn, buffer, -1, -1, tableNameStart, tableNameLength, -1, -1,
303
370
this.colFlag = 0;
304
371
this.colDecimals = 0;
308
// ----------------------------------------------------------------
375
* Used by prepared statements to re-use result set data conversion methods
376
* when generating bound parmeter retrieval instance for statement
383
* @param charsetIndex
384
* the MySQL collation/character set index
386
* from java.sql.Types
388
* length in characters or bytes (for BINARY data).
390
Field(String tableName, String columnName, int charsetIndex, int jdbcType,
392
this.tableName = tableName;
393
this.name = columnName;
394
this.length = length;
395
this.sqlType = jdbcType;
397
this.colDecimals = 0;
398
this.charsetIndex = charsetIndex;
310
401
private void checkForImplicitTemporaryTable() {
311
402
this.isImplicitTempTable = this.tableNameLength > 5
312
403
&& this.buffer[tableNameStart] == (byte) '#'
329
420
if (this.collationName == null) {
330
421
if (this.connection != null) {
331
422
if (this.connection.versionMeetsMinimum(4, 1, 0)) {
332
java.sql.DatabaseMetaData dbmd = this.connection
335
String quotedIdStr = dbmd.getIdentifierQuoteString();
337
if (" ".equals(quotedIdStr)) { //$NON-NLS-1$
338
quotedIdStr = ""; //$NON-NLS-1$
341
String csCatalogName = getDatabaseName();
342
String csTableName = getOriginalTableName();
343
String csColumnName = getOriginalName();
345
if (csCatalogName != null && csCatalogName.length() != 0
346
&& csTableName != null && csTableName.length() != 0
347
&& csColumnName != null
348
&& csColumnName.length() != 0) {
349
StringBuffer queryBuf = new StringBuffer(csCatalogName
351
+ csTableName.length() + 28);
352
queryBuf.append("SHOW FULL COLUMNS FROM "); //$NON-NLS-1$
353
queryBuf.append(quotedIdStr);
354
queryBuf.append(csCatalogName);
355
queryBuf.append(quotedIdStr);
356
queryBuf.append("."); //$NON-NLS-1$
357
queryBuf.append(quotedIdStr);
358
queryBuf.append(csTableName);
359
queryBuf.append(quotedIdStr);
361
java.sql.Statement collationStmt = null;
362
java.sql.ResultSet collationRs = null;
365
collationStmt = this.connection.createStatement();
367
collationRs = collationStmt.executeQuery(queryBuf
370
while (collationRs.next()) {
371
if (csColumnName.equals(collationRs
372
.getString("Field"))) { //$NON-NLS-1$
373
this.collationName = collationRs
374
.getString("Collation"); //$NON-NLS-1$
380
if (collationRs != null) {
385
if (collationStmt != null) {
386
collationStmt.close();
387
collationStmt = null;
423
if (this.connection.getUseDynamicCharsetInfo()) {
424
java.sql.DatabaseMetaData dbmd = this.connection
427
String quotedIdStr = dbmd.getIdentifierQuoteString();
429
if (" ".equals(quotedIdStr)) { //$NON-NLS-1$
430
quotedIdStr = ""; //$NON-NLS-1$
433
String csCatalogName = getDatabaseName();
434
String csTableName = getOriginalTableName();
435
String csColumnName = getOriginalName();
437
if (csCatalogName != null && csCatalogName.length() != 0
438
&& csTableName != null && csTableName.length() != 0
439
&& csColumnName != null
440
&& csColumnName.length() != 0) {
441
StringBuffer queryBuf = new StringBuffer(csCatalogName
443
+ csTableName.length() + 28);
444
queryBuf.append("SHOW FULL COLUMNS FROM "); //$NON-NLS-1$
445
queryBuf.append(quotedIdStr);
446
queryBuf.append(csCatalogName);
447
queryBuf.append(quotedIdStr);
448
queryBuf.append("."); //$NON-NLS-1$
449
queryBuf.append(quotedIdStr);
450
queryBuf.append(csTableName);
451
queryBuf.append(quotedIdStr);
453
java.sql.Statement collationStmt = null;
454
java.sql.ResultSet collationRs = null;
457
collationStmt = this.connection.createStatement();
459
collationRs = collationStmt.executeQuery(queryBuf
462
while (collationRs.next()) {
463
if (csColumnName.equals(collationRs
464
.getString("Field"))) { //$NON-NLS-1$
465
this.collationName = collationRs
466
.getString("Collation"); //$NON-NLS-1$
472
if (collationRs != null) {
477
if (collationStmt != null) {
478
collationStmt.close();
479
collationStmt = null;
484
this.collationName = CharsetMapping.INDEX_TO_COLLATION[charsetIndex];
398
490
return this.collationName;
401
493
public String getColumnLabel() throws SQLException {
402
494
return getName(); // column name if not aliased, alias if used
408
500
* @return DOCUMENT ME!
410
502
public String getDatabaseName() throws SQLException {
858
955
StringBuffer asString = new StringBuffer();
859
956
asString.append(super.toString());
861
asString.append("\n catalog: ");
957
asString.append("[");
958
asString.append("catalog=");
862
959
asString.append(this.getDatabaseName());
863
asString.append("\n table name: ");
960
asString.append(",tableName=");
864
961
asString.append(this.getTableName());
865
asString.append("\n original table name: ");
962
asString.append(",originalTableName=");
866
963
asString.append(this.getOriginalTableName());
867
asString.append("\n column name: ");
964
asString.append(",columnName=");
868
965
asString.append(this.getName());
869
asString.append("\n original column name: ");
966
asString.append(",originalColumnName=");
870
967
asString.append(this.getOriginalName());
871
asString.append("\n MySQL data type: ");
968
asString.append(",mysqlType=");
872
969
asString.append(getMysqlType());
874
if (this.buffer != null) {
875
asString.append("\n\nData as received from server:\n\n");
876
asString.append(StringUtils.dumpAsHex(this.buffer,
877
this.buffer.length));
970
asString.append("(");
971
asString.append(MysqlDefs.typeToName(getMysqlType()));
972
asString.append(")");
973
asString.append(",flags=");
975
if (isAutoIncrement()) {
976
asString.append(" AUTO_INCREMENT");
979
if (isPrimaryKey()) {
980
asString.append(" PRIMARY_KEY");
984
asString.append(" UNIQUE_KEY");
988
asString.append(" BINARY");
992
asString.append(" BLOB");
995
if (isMultipleKey()) {
996
asString.append(" MULTI_KEY");
1000
asString.append(" UNSIGNED");
1004
asString.append(" ZEROFILL");
1007
asString.append(", charsetIndex=");
1008
asString.append(this.charsetIndex);
1009
asString.append(", charsetName=");
1010
asString.append(this.charsetName);
1013
//if (this.buffer != null) {
1014
// asString.append("\n\nData as received from server:\n\n");
1015
// asString.append(StringUtils.dumpAsHex(this.buffer,
1016
// this.buffer.length));
1019
asString.append("]");
880
1021
return asString.toString();
881
1022
} catch (Throwable t) {
882
1023
return super.toString();