Skip to content
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

PgDatabaseMetaData.getIndexInfo breaks with certain custom operator configuration #2241

Closed
jsyrjala opened this issue Sep 7, 2021 · 2 comments · Fixed by #2242
Closed

PgDatabaseMetaData.getIndexInfo breaks with certain custom operator configuration #2241

jsyrjala opened this issue Sep 7, 2021 · 2 comments · Fixed by #2242

Comments

@jsyrjala
Copy link
Contributor

jsyrjala commented Sep 7, 2021

Describe the issue
It is possible to break method PgDatabaseMetaData.getIndexInfo() by adding certain custom operators.

This happened with a production database that was running Adobe Campaign product. That product defines a bunch of functions and operators for internal usage.

https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java#L2531
https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java#L2538

Driver Version?
Tested with 42.2.23, 42.2.12. But I expect the same issue be in wide range of versions.

Java Version?
OpenJDK 11

OS Version?

PostgreSQL Version?
AWS RDS 11.10

To Reproduce
Steps to reproduce the behaviour:

Create an & operator like this:

CREATE OR REPLACE FUNCTION f6(numeric, integer) returns integer as 'BEGIN return $1::integer & $2;END;' language plpgsql immutable;
CREATE OPERATOR & (LEFTARG = numeric, RIGHTARG = integer, PROCEDURE = f6);

call getIndexInfo()

Expected behaviour
It is expected that getIndexInfo() works and returns the data.

This is what happens:
getIndexInfo() creates this kind of SQL query and then executes it.

SELECT     tmp.TABLE_CAT,     tmp.TABLE_SCHEM,     tmp.TABLE_NAME,     tmp.NON_UNIQUE,     tmp.INDEX_QUALIFIER,     tmp.INDEX_NAME,     tmp.TYPE,     tmp.ORDINAL_POSITION,     trim(both '"' from [pg_catalog.pg](http://pg_catalog.pg)_get_indexdef([tmp.CI](http://tmp.CI)_OID, tmp.ORDINAL_POSITION, false)) AS COLUMN_NAME,   CASE [tmp.AM](http://tmp.AM)_NAME     WHEN 'btree' THEN CASE tmp.I_INDOPTION[tmp.ORDINAL_POSITION - 1] & 1       WHEN 1 THEN 'D'       ELSE 'A'     END     ELSE NULL   END AS ASC_OR_DESC,     tmp.CARDINALITY,     tmp.PAGES,     tmp.FILTER_CONDITION FROM (SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM,   ct.relname AS TABLE_NAME, NOT i.indisunique AS NON_UNIQUE,   NULL AS INDEX_QUALIFIER, ci.relname AS INDEX_NAME,   CASE i.indisclustered     WHEN true THEN 1    ELSE CASE am.amname       WHEN 'hash' THEN 2      ELSE 3    END   END AS TYPE,   (information_schema._pg_expandarray(i.indkey)).n AS ORDINAL_POSITION,   ci.reltuples AS CARDINALITY,   ci.relpages AS PAGES,   [pg_catalog.pg](http://pg_catalog.pg)_get_expr(i.indpred, i.indrelid) AS FILTER_CONDITION,   ci.oid AS CI_OID,   i.indoption AS I_INDOPTION,   am.amname AS AM_NAME FROM [pg_catalog.pg](http://pg_catalog.pg)_class ct   JOIN [pg_catalog.pg](http://pg_catalog.pg)_namespace n ON (ct.relnamespace = n.oid)   JOIN [pg_catalog.pg](http://pg_catalog.pg)_index i ON (ct.oid = i.indrelid)   JOIN [pg_catalog.pg](http://pg_catalog.pg)_class ci ON (ci.oid = i.indexrelid)   JOIN [pg_catalog.pg](http://pg_catalog.pg)_am am ON (ci.relam = am.oid) WHERE true  AND n.nspname = 'myschema' AND ct.relname = 'mytable' AND i.indisunique ) AS tmp ORDER BY NON_UNIQUE, TYPE, INDEX_NAME, ORDINAL_POSITION

Executing the query fails with this error

ERROR:  operator is not unique: smallint & integer
LINE 1: ...EN CASE tmp.I_INDOPTION[tmp.ORDINAL_POSITION - 1] & 1       ...
                                                             ^
HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.

This is the operator configuration:

select oprname, oprowner, oprcode, t1.typname as left_opr, t2.typname as right_opr, t3.typname as result from pg_operator inner join pg_type t1 on t1.oid = oprleft inner join pg_type t2 on t2.oid = oprright inner join pg_type t3 on t3.oid = oprresult where oprname = '&';
oprname,oprowner,oprcode,left_opr,right_opr,result
"&","1","int2and","int2","int2","int2"
"&","1","int4and","int4","int4","int4"
"&","1","bitand","bit","bit","bit"
"&","1","int8and","int8","int8","int8"
"&","1","varbyte_bitand","varbyte","varbyte","varbyte"

The offending places in the code are these:

https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java#L2531
https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java#L2538

This kind of code:

tmp.I_INDOPTION[tmp.ORDINAL_POSITION - 1] & 1

A fix is to change it to this:

tmp.I_INDOPTION[tmp.ORDINAL_POSITION - 1] & 1::smallint

Other alternative would be to define some additional operator configuration such that postgres can resolve which operator to use in this case.

@jsyrjala
Copy link
Contributor Author

jsyrjala commented Sep 7, 2021

I'll make soon a PR to add ::smallint casts to PgDatabaseMetaData.getIndexInfo() queries.

davecramer pushed a commit that referenced this issue Sep 13, 2021
It is possible to break method PgDatabaseMetaData.getIndexInfo() by adding certain custom operators.
This PR casts operands in a query so that PgDatabaseMetaData.getIndexInfo() works in presence
of such operator config.

Creating this operator triggers this issue:

CREATE OR REPLACE FUNCTION f6(numeric, integer) returns integer as 'BEGIN return $1::integer & $2;END;' language plpgsql immutable;
CREATE OPERATOR & (LEFTARG = numeric, RIGHTARG = integer, PROCEDURE = f6);

Fixes #2241
@jsyrjala
Copy link
Contributor Author

PR against release/42.2 is #2253

davecramer pushed a commit that referenced this issue Sep 13, 2021
…2253)

It is possible to break method PgDatabaseMetaData.getIndexInfo() by adding certain custom operators.
This PR casts operands in a query so that PgDatabaseMetaData.getIndexInfo() works in presence
of such operator config.

Creating this operator triggers this issue:

CREATE OR REPLACE FUNCTION f6(numeric, integer) returns integer as 'BEGIN return $1::integer & $2;END;' language plpgsql immutable;
CREATE OPERATOR & (LEFTARG = numeric, RIGHTARG = integer, PROCEDURE = f6);

Fixes #2241
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant