Permalink
Browse files

fix: use per-connection cache for field metadata (table name, column …

…name, etc)

Global cache might lead to wrong results and concurrency issues, thus
switched per-connection one.

closes #551
closes #552
  • Loading branch information...
Petro Semeniuk authored and vlsi committed May 14, 2016
1 parent b756a15 commit dc3bddab3f2b14fd81284f109fa5056dbe5ab40b
@@ -83,6 +83,18 @@
PREPARED_STATEMENT_CACHE_SIZE_MIB("preparedStatementCacheSizeMiB", "5",
"Specifies the maximum size (in megabytes) of a per-connection prepared statement cache. A value of {@code 0} disables the cache."),
/**
* Specifies the maximum number of fields to be cached per connection. A value of {@code 0} disables the cache.
*/
DATABASE_METADATA_CACHE_FIELDS("databaseMetadataCacheFields", "65536",
"Specifies the maximum number of fields to be cached per connection. A value of {@code 0} disables the cache."),
/**
* Specifies the maximum number of fields to be cached per connection. A value of {@code 0} disables the cache.
*/
DATABASE_METADATA_CACHE_FIELDS_MIB("databaseMetadataCacheFieldsMiB", "5",
"Specifies the maximum size (in megabytes) of fields to be cached per connection. A value of {@code 0} disables the cache."),
/**
* Default parameter for {@link java.sql.Statement#getFetchSize()}. A value of {@code 0} means
* that need fetch all rows at once
@@ -9,7 +9,9 @@
package org.postgresql.core;
import org.postgresql.PGConnection;
import org.postgresql.jdbc.FieldMetadata;
import org.postgresql.jdbc.TimestampUtils;
import org.postgresql.util.LruCache;
import java.sql.Connection;
import java.sql.ResultSet;
@@ -237,4 +239,11 @@ public ResultSet execSQLQuery(String s, int resultSetType, int resultSetConcurre
* @return true if re-write feature is enabled
*/
public boolean isReWriteBatchedInsertsEnabled();
/**
* Return metadata cache for given connection
*
* @return metadata cache
*/
LruCache<FieldMetadata.Key, FieldMetadata> getFieldMetadataCache();
}
@@ -8,7 +8,7 @@
package org.postgresql.core;
import java.sql.ResultSetMetaData;
import org.postgresql.jdbc.FieldMetadata;
/*
*/
@@ -21,7 +21,6 @@
private final int oid; // OID of the type
private final int mod; // type modifier of this field
private final String columnLabel; // Column label
private String columnName; // Column name
private int format = TEXT_FORMAT; // In the V3 protocol each field has a format
// 0 = text, 1 = binary
@@ -34,10 +33,8 @@
// Cache fields filled in by AbstractJdbc2ResultSetMetaData.fetchFieldMetaData.
// Don't use unless that has been called.
private String tableName = "";
private String schemaName = "";
private int nullable = ResultSetMetaData.columnNullableUnknown;
private boolean autoIncrement = false;
private FieldMetadata metadata;
private int sqlType;
private String pgType = NOT_YET_LOADED;
@@ -53,7 +50,7 @@
* @param mod modifier
*/
public Field(String name, int oid, int length, int mod) {
this(name, name, oid, length, mod, 0, 0);
this(name, oid, length, mod, 0, 0);
}
/**
@@ -68,25 +65,22 @@ public Field(String name, int oid) {
/**
* Construct a field based on the information fed to it.
*
* @param columnLabel the column label of the field
* @param columnName the column label the name of the field
* @param oid the OID of the field
* @param length the length of the field
* @param mod modifier
* @param tableOid the OID of the columns' table
* @param positionInTable the position of column in the table (first column is 1, second column is
* 2, etc...)
* @param positionInTable the position of column in the table (first column is 1, second column is 2, etc...)
*/
public Field(String columnLabel, String columnName, int oid, int length, int mod, int tableOid,
public Field(String columnLabel, int oid, int length, int mod, int tableOid,
int positionInTable) {
this.columnLabel = columnLabel;
this.columnName = columnName;
this.oid = oid;
this.length = length;
this.mod = mod;
this.tableOid = tableOid;
this.positionInTable = positionInTable;
this.metadata = tableOid == 0 ? new FieldMetadata(columnLabel) : null;
}
/**
@@ -142,48 +136,16 @@ public int getPositionInTable() {
return positionInTable;
}
public void setNullable(int nullable) {
this.nullable = nullable;
}
public int getNullable() {
return nullable;
}
public void setAutoIncrement(boolean autoIncrement) {
this.autoIncrement = autoIncrement;
}
public boolean getAutoIncrement() {
return autoIncrement;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getColumnName() {
return columnName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getTableName() {
return tableName;
}
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
public FieldMetadata getMetadata() {
return metadata;
}
public String getSchemaName() {
return schemaName;
public void setMetadata(FieldMetadata metadata) {
this.metadata = metadata;
}
public String toString() {
return "Field(" + (columnName != null ? columnName : "")
return "Field(" + (columnLabel != null ? columnLabel : "")
+ "," + Oid.toString(oid)
+ "," + length
+ "," + (format == TEXT_FORMAT ? 'T' : 'B')
@@ -573,7 +573,7 @@ protected void processResults(Query originalQuery, ResultHandler handler, int ma
int typeOid = pgStream.ReceiveInteger4();
int typeLength = pgStream.ReceiveInteger2();
int typeModifier = pgStream.ReceiveInteger4();
fields[i] = new Field(columnLabel, columnLabel, typeOid, typeLength, typeModifier, 0, 0);
fields[i] = new Field(columnLabel, typeOid, typeLength, typeModifier, 0, 0);
}
return fields;
@@ -2258,7 +2258,7 @@ public void handleCompletion() throws SQLException {
int typeLength = pgStream.ReceiveInteger2();
int typeModifier = pgStream.ReceiveInteger4();
int formatType = pgStream.ReceiveInteger2();
fields[i] = new Field(columnLabel, "", /* name not yet determined */
fields[i] = new Field(columnLabel,
typeOid, typeLength, typeModifier, tableOid, positionInTable);
fields[i].setFormat(formatType);
@@ -396,6 +396,38 @@ public void setPreparedStatementCacheSizeMiB(int cacheSize) {
PGProperty.PREPARED_STATEMENT_CACHE_SIZE_MIB.set(properties, cacheSize);
}
/**
* @return database metadata cache fields size (number of fields cached per connection)
* @see PGProperty#DATABASE_METADATA_CACHE_FIELDS
*/
public int getDatabaseMetadataCacheFields() {
return PGProperty.DATABASE_METADATA_CACHE_FIELDS.getIntNoCheck(properties);
}
/**
* @param cacheSize database metadata cache fields size (number of fields cached per connection)
* @see PGProperty#DATABASE_METADATA_CACHE_FIELDS
*/
public void setDatabaseMetadataCacheFields(int cacheSize) {
PGProperty.DATABASE_METADATA_CACHE_FIELDS.set(properties, cacheSize);
}
/**
* @return database metadata cache fields size (number of megabytes per connection)
* @see PGProperty#DATABASE_METADATA_CACHE_FIELDS_MIB
*/
public int getDatabaseMetadataCacheFieldsMiB() {
return PGProperty.DATABASE_METADATA_CACHE_FIELDS_MIB.getIntNoCheck(properties);
}
/**
* @param cacheSize database metadata cache fields size (number of megabytes per connection)
* @see PGProperty#DATABASE_METADATA_CACHE_FIELDS_MIB
*/
public void setDatabaseMetadataCacheFieldsMiB(int cacheSize) {
PGProperty.DATABASE_METADATA_CACHE_FIELDS_MIB.set(properties, cacheSize);
}
/**
* @param fetchSize default fetch size
* @see PGProperty#DEFAULT_ROW_FETCH_SIZE

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.
@@ -0,0 +1,89 @@
package org.postgresql.jdbc;
import org.postgresql.util.CanEstimateSize;
/**
* This is an internal class to hold field metadata info like table name, column name, etc.
* This class is not meant to be used outside of pgjdbc.
*/
public class FieldMetadata implements CanEstimateSize {
public static class Key {
final int tableOid;
final int positionInTable;
Key(int tableOid, int positionInTable) {
this.positionInTable = positionInTable;
this.tableOid = tableOid;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Key key = (Key) o;
if (tableOid != key.tableOid) {
return false;
}
return positionInTable == key.positionInTable;
}
@Override
public int hashCode() {
int result = tableOid;
result = 31 * result + positionInTable;
return result;
}
@Override
public String toString() {
return "Key{"
+ "tableOid=" + tableOid
+ ", positionInTable=" + positionInTable
+ '}';
}
}
final String columnName;
final String tableName;
final String schemaName;
final int nullable;
final boolean autoIncrement;
public FieldMetadata(String columnName) {
this(columnName, "", "", PgResultSetMetaData.columnNullableUnknown, false);
}
FieldMetadata(String columnName, String tableName, String schemaName, int nullable,
boolean autoIncrement) {
this.columnName = columnName;
this.tableName = tableName;
this.schemaName = schemaName;
this.nullable = nullable;
this.autoIncrement = autoIncrement;
}
public long getSize() {
return columnName.length() * 2
+ tableName.length() * 2
+ schemaName.length() * 2
+ 4
+ 1;
}
@Override
public String toString() {
return "FieldMetadata{"
+ "columnName='" + columnName + '\''
+ ", tableName='" + tableName + '\''
+ ", schemaName='" + schemaName + '\''
+ ", nullable=" + nullable
+ ", autoIncrement=" + autoIncrement
+ '}';
}
}
Oops, something went wrong.

0 comments on commit dc3bdda

Please sign in to comment.