Pass parameters to psql function [duplicate] - postgresql

I'm trying to use a query where the schema and table are passed as parameters into a prepared statement using pg_query_params like this:
$searchSchema = 'mySchema';
$searchTable = 'myTable';
$searchColumn = 'searchColumn';
$searchValue = 'some value';
$selQuery = "SELECT *
FROM $1.$2 --fails here
WHERE someColumn like $3;";
$rs = pg_query_params($db, $selQuery , array($searchSchema, $searchTable, $searchColumn, $searchValue));
The issue is with the schema and table which need to be set dynamically - as in the code above.

In a parameterized SQL statement (which is a prepared statement in PostgreSQL), parameters can only stand for constant values, not for table or column names.
This limitation is enforced by PostgreSQL, and there is no way around it, no matter what programming language or driver you use. This is also intentional and not a bug.
You will have to compose a string that contains the complete SQL statement with table and column names substituted and execute that. Beware of SQL injection – use functions like pg_escape_identifier to escape names.

Related

Postgresql: literal table names

I am making an application that needs to construct Postgresql queries that will execute successfully in scenarios when table names are reserved keywords etc.
In Sql Server syntax this is achieved by wrapping everything in square brackets [] i.e. SELECT * FROM [database].[schema].[table_name].
I thought the equivalent in Postgresql was the use of double quotes "" i.e. SELECT * FROM "database"."schema"."table_name".
However, when I try this in Postgresql I get the error
Relation X doesn't exist
This works:
SELECT * FROM "postgres"."schema_a".Academic_Attainment
But this doesn't:
SELECT * FROM "postgres"."schema_a"."Academic_Attainment"
Related to: Escaping keyword-like column names in Postgres
Any suggestions?
As documented in the manual unquoted identifiers are folded to lowercase.
A quoted identifier is also case sensitive, so "Foo" is a different name than "foo".
So the name Academic_Attainment is the same as academic_attainment. If you really insist on using those dreaded double quotes, then you need to use a lower case identifier:
SELECT *
FROM "schema_a"."academic_attainment"
In general it's strongly recommended to not use quoted identifiers at all. As a rule of thumb: never use double quotes and you are fine.
If you are constructing dynamic SQL, use the format() function to do that together with the %I placeholder. It will take care of quoting if necessary (and only then), e.g.
format('select * from %I.%I', 'public', 'some_table') yields select * from public.some_table but format('select * from %I.%I', 'public', 'group') yields select * from public."group"
Unrelated to your question: Postgres doesn't support cross-database queries, so you should not get into the habit including the database name into your fully qualified names. The syntax you are using only works because you are connected to the database postgres. So I would recommend to stop using the database name in any table reference.

DB2oC (DB2 on Cloud) : Facing "attempted to modify data but was not defined as MODIFIES SQL DATA" error

I have created very simple function in DB2oC as below, which has one UPDATE sql statement and one SELECT sql statement along with MODIFIES SQL DATA. But still I get the below error, though I have specified MODIFIES SQL DATA. I did GRANT ALL on that TEST table to my user id and also did GRANT EXECUTE ON FUNCTION to my user id on safe side. Can you please help to explain on what could be the issue?
I have simply invoked the function using SELECT statement like below:
SELECT TESTSCHEMA.MAIN_FUNCTION() FROM TABLE(VALUES(1));
SQL Error [38002]: User defined routine "TESTSCHEMA.MAIN_FUNCTION"
(specific name "SQL201211013006981") attempted to modify data but was
not defined as MODIFIES SQL DATA.. SQLCODE=-577, SQLSTATE=38002,
DRIVER=4.27.25
CREATE OR REPLACE FUNCTION MAIN_FUNCTION()
RETURNS VARCHAR(20)
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
DECLARE val VARCHAR(20);
UPDATE TEST t SET t.CONTENT_TEXT = 'test value' WHERE t.ID = 1;
select CONTENT_TEXT into val from TEST where ID = 1;
return val;
end;
Appreciate your help.
For the modifies SQL data clause , the usage of the function is restricted on Db2-LUW.
These restrictions do not apply for user defined functions that do not modify data.
For your specific example, that UDF will operate when used as the sole expression on the right hand side of an assignment statement in a compound-SQL compiled statemnent.
For example:
create or replace variable my_result varchar(20) default null;
begin
set my_result = main_function();
end#
Consider using stored procedures to modify table contents, instead of user defined functions.
You could avoid using a function, and just use a single "change data statement"
SELECT CONTENT_TEXT
FROM NEW TABLE(
UPDATE TEST t
SET t.CONTENT_TEXT = 'test value'
WHERE t.ID = 1
)

How to escape SQL parameter in JDBC when PreparedStatement won't work?

I have a string that I want to pass to SQL. To prevent SQL injection and other quoting and escaping problems, the best practice is to use a PreparedStatement with ?. For example:
val ps = conn.prepareStatement("select * from foo where name = ?")
ps.setString(1, name)
But for some SQL code, this won't work. For example, here is PostgreSQL, trying to create a view.
val ps = conn.prepareStatement("create temp view v1 as select * from foo where name = ?")
ps.setString(1, name)
val rs = ps.execute()
This throws an error:
org.postgresql.util.PSQLException: ERROR: there is no parameter $1
It apparently doesn't allow parameters to create view. How do you get around this and safely escape the string?
Prepared statements are used to plan a complex statement once and then execute it multiple (= very many) times with different parameter values. Simple statements have no noticeable benefit from using a prepared statement because planning them is trivial. DDL statements are not supported at all, so that is most likely the cause of the error, although the error message is confusing.
From the documentation:
PREPARE name [ ( data_type [, ...] ) ] AS statement
statement
Any SELECT, INSERT, UPDATE, DELETE, or VALUES statement.
The PreparedStatement class does document that you can use DDL in the executeUpdate() method, but from a logical standpoint that is just nonsense, at least in PostgreSQL.
Instead, you should use a Statement and then call execute() or executeUpdate() (a bit odd that the latter method would support DDL statements because there is no update being performed).
Preventing SQL-injection
In order to prevent SQL-injection you can use a few PostgreSQL functions:
quote_literal() - As can be expected, this will quote a literal parameter value to be safe in the query. Not only does this prevent you from Bobby Tables, but also from the likes of Mr. O'Brien.
quote_nullable() - For literals like above, but will generate proper code when the parameter IS NULL.
quote_identifier() - Will double quote any table or column name that might cause problems for the planner, like table name from with columns int, type and from: SELECT int, type, from FROM from WHERE int = type becomes SELECT "int", "type", "from" FROM "from" WHERE "int" = "type".
You can use these functions directly in your SQL statements and then let PostgreSQL deal with nasty input.

What command should I use to make my void function display a paticular table using a variable from the function in the where clause?

I basically have a void function that creates a tuple on a existing table. Now at the end of the function I want display the table with the updated tuple. I am running it problems when trying to do this.
This is the statement I am using:
EXECUTE 'SELECT * FROM table WHERE IDNo = idnumber';
-- (idnumber is a variable that is assigned a value in the function)
I get the following error:
ERROR: column "idnumber" does not exist.
Can someone please help me find a solution.
For the actual query, you would want to do something like this:
execute 'select * from table where IDNo = $1' using idnumber;
With the key being the $1 and the USING clause to interpolate the variable.
That should resolve the column error regarding idnumber.
However, I'm not quite sure what you mean by:
display the table with the updated tuple
Do you mean you want to return all the rows in the table including the newly added row? Or just the newly added row? or something else?
Edit in response to comment from OP:
The substitution variables, e.g. $1, $2, $3... are scoped (i.e. unique) to each separate execute statement. So if you had two statements, the first with 3 variables, the second with three, you could use $1, $2, $3 in each and they would refer to the variables mentioned in the USING clause for that individual statement.
See the Postgres Basic Statements doc, specifically the section entitled 40.5.4. Executing Dynamic Commands, for more detail.
Second edit in response to display comments from OP:
When executeing statements, they won't output the way, say, a select statement would if you were doing it within psql or pgadmin. Rather, you have a couple different options, depending on what you ultimately want to do.
First, you could use an INTO clause to put the result into a record (although how you do this depends on whether it's just one row or many rows).
You would need to declare it in that case in the declaration section something like this: foo RECORD;
And then add INTO foo before the USING clause. If it complains about more than one record, you could add LIMIT 1 clause at the end of the query.
You could then do whatever else you wanted to with that record, including RAISE NOTICE with interpolating the record's columns, which would print it to the console.
If you want the entire table, and you want it to "display" more like psql or similar would (that is, return the rows obtained), you would want to have the function return a setof a specific type.
So it may then look something like this:
create function get_table() returns setof table as $$
execute 'select * from table where IDNo = $1' using idnumber;
$$ language 'plpgsql';
Where table is the name of the table you want. If you just want to return the existing rows of the table, this sort of query should work. It would then "display" in a client (e.g. psql, etc.) as the result set.
If you want to modify that (say, by dynamically adding some columns), then you would need to define that new type specifically, and then use that as the type being returned.
See the Postgres Wiki for more details. The wiki content is pretty old (Postgres 7.x vintage), but it generally still applies.

Parameters in the FormsOf function and SQL injection

Is the following SQL susceptible to SQL injection via the #SearchWord parameter?
I want to use parameters with the FormsOf function, but the only guide to doing so I've found is in this Stack Overflow question: How to pass parameter to FormsOf function in sql server
However the solution seems to be to use a bit of dynamic SQL, and I was wondering if that would be susceptible to SQL injection. What would happen in the following example if #searchWord contained a SQL injection type string? Is it not a problem because it's still within a parameter, passed as an argument to FREETEXTTABLE?
The solution given is:
DECLARE #SearchWord nvarchar(max)
SET #SearchWord = 'tax'
DECLARE #SearchString nvarchar(max)
SET #SearchString = 'FormsOf(INFLECTIONAL, "' + #SearchWord + '")'
SELECT listing_id, RANK, name, address, city, zip, heading, phone
FROM listings a,
FREETEXTTABLE(listings, *, #SearchString)
WHERE [KEY] = a.listing_id
ORDER BY RANK DESC, name
No, it's not susceptible. There's no dynamic SQL here (that would require either using EXEC or sp_executesql), so there's no vector for SQL injection.
In order for a SQL injection vulnerability to exist, the user-supplied string (in this case #SearchWord) must actually be inserted directly into the text of the SQL statement. Here, it's only being used to construct another string variable, which is subsequently used as a parameter to another SQL statement.
This statement can, however, fail if the user inputs an "invalid" search word, i.e. one containing single quotes, so you should probably still escape whatever value is passed to #SearchWord. But it cannot be used to execute arbitrary SQL.
I haven't tested this, but I don't think the interpreter is simply pasting the value of #SearchString into the statement. It should parse #SearchString using the rules that FREETEXTTABLE expects--that's the way other parameters work.