-
Notifications
You must be signed in to change notification settings - Fork 819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose parameter status messages (GUC_REPORT) to the user #1435
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
--- | ||
layout: default_docs | ||
title: Physical and Logical replication API | ||
header: Chapter 9. PostgreSQL™ Extensions to the JDBC API | ||
resource: media | ||
previoustitle: Parameter Status Messages | ||
previous: server-prepare.html | ||
nexttitle: Physical and Logical replication API | ||
next: replication.html | ||
--- | ||
|
||
# Parameter Status Messages | ||
|
||
PostgreSQL supports server parameters, also called server variables or, | ||
internally, Grand Unified Configuration (GUC) variables. These variables are | ||
manipulated by the `SET` command, `postgresql.conf`, `ALTER SYSTEM SET`, `ALTER | ||
USER SET`, `ALTER DATABASE SET`, the `set_config(...)` SQL-callable function, | ||
etc. See [the PostgreSQL manual](https://www.postgresql.org/docs/current/config-setting.html). | ||
|
||
For a subset of these variables the server will *automatically report changes | ||
to the value to the client driver and application*. These variables are known | ||
internally as `GUC_REPORT` variables after the name of the flag that enables | ||
the functionality. | ||
|
||
The server keeps track of all the variable scopes and reports when a variable | ||
reverts to a prior value, so the client doesn't have to guess what the current | ||
value is and whether some server-side function could've changed it. Whenever | ||
the value changes, no matter why or how it changes, the server reports the new | ||
effective value in a *Parameter Status* protocol message to the client. PgJDBC | ||
uses many of these reports internally. | ||
|
||
As of PgJDBC 42.2.6, it also exposes the parameter status information to user | ||
applications via the PGConnection extensions interface. | ||
|
||
## Methods | ||
|
||
Two methods on `org.postgresql.PGConnection` provide the client interface to | ||
reported parameters. Parameter names are case-insensitive and case-preserving. | ||
|
||
* `Map PGConnection.getParameterStatuses()` - return a map of all reported | ||
parameters and their values. | ||
|
||
* `String PGConnection.getParameterStatus()` - shorthand to retrieve one | ||
value by name, or null if no value has been reported. | ||
|
||
See the `PGConnection` JavaDoc for details. | ||
|
||
## Example | ||
|
||
If you're working directly with a `java.sql.Connection` you can | ||
|
||
import org.postgresql.PGConnection; | ||
|
||
void my_function(Connection conn) { | ||
|
||
System.out.println("My application name is " + | ||
((PGConnection)conn).getParameterStatus("application_name")); | ||
|
||
} | ||
|
||
## Other client drivers | ||
|
||
The `libpq` equivalent is the `PQparameterStatus(...)` API function. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,10 @@ | |
import java.sql.SQLException; | ||
import java.sql.SQLWarning; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
import java.util.Properties; | ||
import java.util.TreeMap; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
|
@@ -52,6 +55,10 @@ public abstract class QueryExecutorBase implements QueryExecutor { | |
private final LruCache<Object, CachedQuery> statementCache; | ||
private final CachedQueryCreateAction cachedQueryCreateAction; | ||
|
||
// For getParameterStatuses(), GUC_REPORT tracking | ||
private final TreeMap<String,String> parameterStatuses | ||
= new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pg's parameters use a mix of camel case and underscore separators. Users generally expect case-folding behaviour in postgres, too. So I'd strongly prefer to do this. |
||
|
||
protected QueryExecutorBase(PGStream pgStream, String user, | ||
String database, int cancelSignalTimeout, Properties info) throws SQLException { | ||
this.pgStream = pgStream; | ||
|
@@ -389,4 +396,42 @@ public void setFlushCacheOnDeallocate(boolean flushCacheOnDeallocate) { | |
protected boolean hasNotifications() { | ||
return notifications.size() > 0; | ||
} | ||
|
||
@Override | ||
public final Map<String,String> getParameterStatuses() { | ||
return Collections.unmodifiableMap(parameterStatuses); | ||
} | ||
|
||
@Override | ||
public final String getParameterStatus(String parameterName) { | ||
return parameterStatuses.get(parameterName); | ||
} | ||
|
||
/** | ||
* Update the parameter status map in response to a new ParameterStatus | ||
* wire protocol message. | ||
* | ||
* <p>The server sends ParameterStatus messages when GUC_REPORT settings are | ||
* initially assigned and whenever they change.</p> | ||
* | ||
* <p>A future version may invoke a client-defined listener class at this point, | ||
* so this should be the only access path.</p> | ||
* | ||
* <p>Keys are case-insensitive and case-preserving.</p> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Applications may use parameters in any combination of letter cases and expect the same result. So the PgJDBC interface should be consistent with that, otherwise users will experience unexpected results if they
Observe
As Java provides a trivial way to make map keys case-insensitive and case-preserving it makes sense to do so. |
||
* | ||
* <p>The server doesn't provide a way to report deletion of a reportable | ||
* parameter so we don't expose one here.</p> | ||
* | ||
* @param parameterName case-insensitive case-preserving name of parameter to create or update | ||
* @param parameterStatus new value of parameter | ||
* @see org.postgresql.PGConnection#getParameterStatuses | ||
* @see org.postgresql.PGConnection#getParameterStatus | ||
*/ | ||
protected void onParameterStatus(String parameterName, String parameterStatus) { | ||
if (parameterName == null || parameterName.equals("")) { | ||
throw new IllegalStateException("attempt to set GUC_REPORT parameter with null or empty-string name"); | ||
} | ||
|
||
parameterStatuses.put(parameterName, parameterStatus); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we have any potential concurrency issue here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bokken At any point where No query executor can be used for more than one connection, and no connection may run more than one statement at a time or process results from more than one statement. You could annotate it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ringerc, my concern was not necessarily concurrent threads calling onParamterStatus, but rather consistency between reading from the map on a different thread from where it was mutated. |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't
getParameterStatus(String parameterName)
just enough?Is the returned
Map
live?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The map returned is an unmodifiable wrapper.
If we don't expose the map then we should provide a
List<String>
-returning call to list known params instead. Just exposing the map is simpler.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is a bit wonky is the handling of case sensitivity.
While we can certainly provide an implementation of
String getParameterStatus(String param)
which is case insensitive toparam
, getting theMap.keySet()
off the return value here would return some specific case when iterating values.I think I would lean towards 1 method to return the known parameters and a separate method to get the value for some given parameter in a case insensitive way.
This provides the needed/desired functionality and hides the implementation detail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks sane to return "server-provided" casing, and allow for case insensitive access, doesn't it?
ResultSet.getInt(String)
allows for case-insensitive access.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vlsi, I am good with the
getParamterStatus(String)
method being case insensitive to the parameter. I am a bit conflicted on returning aMap
with all values and making any statement on it providing a case insensitive get.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bokken 👍
I'm inclining to a map with properties of:
WHYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vlsi, I think my preference would still be to return a collection of parameter names (with properties you list) to get the available names. But I am fine with the map you describe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We got rid of forbidden modifications. And I really fail to see the point of the rest, it seems excessively complex.
I take your point about
keySet()
returning the server-provided cases. But I don't really see the problem with it. The case won't tend to vary as the server doesn't change the case it uses, and the keys will get the correct parameters when looked up. The only possible issue I see issomeReturnedKey.equals("APPLICATION_NAME")
not matching ... and that's no different to how the system already behaves with a query ofpg_settings
.