1

I implemented a Postgres function that takes the table name, column name, and column id as parameters.

It is dynamic because the result varies according to the table name.

This works very well when tested in sql.

postgresql function

create or replace function test(layer_name anyelement, field_name text, object_id text)
   returns setof anyelement
   language plpgsql
as $function$
   begin 
       return query execute format('
           select 
            *
           from
            %s
           where
            %s = cast($1 as int4)'
           , pg_typeof(layer_name), field_name)
           using object_id;
   end;
$function$
;

call

select * from test(null::table_name,'field_name','2');

But I call the function using mybatis, and it fails without receiving results.

<select id="test" parameterType="map" resultType="map">
    select * from test(null::#{layer_name}, #{field_name}, #{object_id})
</select>

or

<select id="test" parameterType="map" resultType="map">
    select * from test(cast(null as #{layer_name}), #{field_name}, #{object_id})
</select>

What am I missing and what do I need to try to make it work?

Error Code 1

### The error occurred while setting parameters
### SQL: select * from test(null::?, ?, ?)
### Cause: org.postgresql.util.PSQLException: ERROR: syntax error at or near "$1"

Error Code 2

### The error occurred while setting parameters
### SQL: select * from test(cast(null as ?), ?, ?)
### Cause: org.postgresql.util.PSQLException: ERROR: syntax error at or near "$1"

1 Answer 1

1

You need to use text substitution ${} instead of placeholder #{} for a table name.
Please see this FAQ entry for the details.

<select id="test" resultType="map">
  select * from test(null::${layer_name}, #{field_name}, #{object_id})
</select>

Here is an executable demo.

Sign up to request clarification or add additional context in comments.

3 Comments

Consider that using ${layer_name} may make your code vulnerable to SQL Injection. Make sure you fully control the value of the property layer_name at all times.
thank you. Is there a way to make it work while preventing sql injection?
@Hans You should be able to validate the layer_name value before executing the statement e.g. tableNames.contains(layerName). In case the target tables are dynamically created, you may have to get the list of tables first. See this answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.