Proper insertion of table name - javascript

How does one correctly provide the table name if the name can be dynamically determined and still prevent SQL injection attacks? I am using node-postgres.
For example:
The following works but I believe is insecure:
dbclient.query("INSERT INTO " + table_name + " VALUES ($1, $2, $3)", [value_a, value_b, value_c])`
What I would like equivalently (but does not work) is:
dbclient.query("INSERT INTO $1 VALUES ($2, $3, $4)", [table_name, value_a, value_b, value_c])`

Any good library should provide proper escaping for SQL names, which include:
schema name
table name
column name
For example, within pg-promise you would use it like this:
db.query("INSERT INTO $1~ VALUES ($2, $3, $4)", [table_name, value_a, value_b, value_c])
i.e. you get your table name properly escaped by appending the variable with ~, which in turn makes it safe from SQL injection.
From here, a simple escaping for table names executed by the library:
return '"' + name.replace(/"/g, '""') + '"';
See also: SQL Names

How about having a hash let tables = {tableName1: 'table_name1', tableName2: 'table_name2'...} and then
//assuming you receive t as table name input
if(tables[t])
//build SQL query with tables[t] as the table name
else
//throw error about non-existing table
This way, you control the actual table names in the DB.
Also, do not forget to clean all input - the values may contain injections.

You might manually check the validity if table name with a regex some other validation logic. I would probably use a dictionary containing permissible table names.
var tables = {users:'users', boats:'boats'};
table_name = tables[table_name];
if (! table_name) throw new Error();
dbclient.query("INSERT INTO " + table_name + " VALUES ($1, $2, $3)", [value_a, value_b, value_c])
If you plan on generating a lot of dynamic sql, use a query builder like http://knexjs.org/

You can use escape function from pg-escape npm module to quote identifiers such as table names:
Only using escape function:
escape("INSERT INTO %I VALUES (%L, %L, %L)", table_name, value_a, value_b, value_c);
Combine it with node postgres:
dbclient.query(escape("INSERT INTO %I VALUES ($2, $3, $4)", table_name), [value_a, value_b, value_c]);

Related

Error with passing in the same variable into multiple parts of SQL statement

So I have a Node.js query that should pass in these three values and update a specific column by a specific amount for a user with a specific id:
await client.query(sqlStatement, [columnName, changeBy, id])
The 'sqlStatement' looks like:
'UPDATE tableName SET ($1) = ($1) + ($2) WHERE id = ($3)'
As you can see, it should pass in the values such that the SQL statement looks like:
'UPDATE tableName SET columnName = columnName + changeBy WHERE id = id'
I've also tried to write sqlStatement as the below (and just pass in columnName twice into the query but this also results in an error:
'UPDATE tableName SET ($1) = ($2) + ($3) WHERE id = ($4)'
The error returned is error: syntax error at or near "$1".
Not sure how to fix this--any help is much appreciated.
The reason this is happening is that node-postgres doesn't support query parameters for identifiers and your column name after SET is an identifier. Also, even if this worked, unless node-postgres somehow substituted the entire ($1) (with parentheses) for your value, you'd get
ERROR: source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression.
If you wish to keep node-postgres, its documentation recommends to use pg-format in such cases, to build a dynamic SQL statement. If you consider alternatives, you can look into Knex.js that will build the queries for you, like so:
knex('tableName')
.update({
columnName: knex.raw('?? + ?',['columnName',changeBy])
})
.where('id', id)
And in the meanwhile, as a workaround, you should be able to set your sqlStatement to a dynamic SQL query on your own:
do language plpgsql $$ begin execute format('UPDATE test SET %I = %I + %s WHERE id = %s','$1','$1',$2,$3); end$$;
and try with that. Note that I removed the parentheses from around the update column to avoid the multiple-column UPDATE error. What should happen is that node-postgres evaluates the above to
do language plpgsql $$ begin execute format('UPDATE test SET %I = %I + %s WHERE id = %s','col','col',51,72); end$$;
and pass it to PostgreSQL, which should execute it as
UPDATE test SET "col" = "col" + 51 WHERE id = 72;
From the doc:
%I is for Identifiers, that PostgreSQL will automatically quote if needed.
%L is for string Literals
%s is for simple string substitution, in the sense that it will not be quoted, so it can be used for numeric literals

javascript - parameter in a postgres query (e.g. ORDER BY $1) not working properly

postgresql / node-postgres / javascript
I am trying to use a parameter $1 in a postrgesql query so I can sort a table based on a string variable (eventually the string variable will come from req.query, but for now it is just a variable called sort that I am creating and defining)
In my code I have:
let sort = 'user_name';
const allScores = await pool.query(
"SELECT * FROM typeTable ORDER BY $1 desc;", [sort]);
console.log(allScores);
but allScores does not sort the table by user_name (which is a valid column name)
The code below does sort the table by user_name. The only difference is that I am manually writing user_name instead of using a parameter.
const allScores = await pool.query(
"SELECT * FROM typeTable ORDER BY user_name desc;");
console.log(allScores);
In my head, I would have thought these two blocks of code would basically be equivalent but they are not. I am not sure what my mistake is. Any help to get the parameter working would be appreciated. Thanks!

How can I INSERT new values and values from an already existing table?

I'm currently learning MySQL and have learned the INSERTO INTO statement and the INSERT INTO SELECT statement.
The INSERT INTO statement works like this:
INSERT INTO table_name (column1, column2, column3, ...) VALUES (value1, value2, value3, ...);
While the INSERT INTO SELECT works like this:
INSERT INTO table2 (column1, column2, column3, ...)
SELECT column1, column2, column3, ...
FROM table1
WHERE condition;
What I'm trying to do is add new values while also adding values that I already have stored in another table.
con.query("INSERT INTO vehicles (vehicleType, vehicleModel, vehicleOwner, vehicleSpawnX, vehicleSpawnY, vehicleSpawnZ)")
From the query above, I already have the vehicleOwner value stored in another table, while the other ones I've just gotten.
How can I do this? Add a VALUES statement before SELECT?
I'm using SQL Workbench 8.0 and JavaScript. Also, all the values are NOT NULL, so I can't make two different queries, unless on the first one I add temporary values that I'll update on the second one.
What I want to replace:
vehicleType -> "players"
vehicleModel -> vehicleCreate.model
vehicleOwner -> playerID FROM players table
vehicleSpawnX -> pos.x
vehicleSpawnY -> pos.y
vehicleSpawnZ -> pos.z
Thanks!
It's not possible... But you can select data and store that on variables, then you will store it in another table
You would construct a query. Your data model is a bit hard to follow since you give no examples of the data or of what you really want to do.
So let me give a simpler example. Say, you have a table with two columns for two players and you want to put the ids into a table -- but using their names. The query would look like:
insert into pairs (playerId1, playerId2)
select p1.playerId, p2.playerId
from players p1 join
players p2
on p1.name = ? and p2.name = ?;
The ? are for parameter placeholders. I assume you know to aways use parameters when passing values into a query.

Return inserted values from 'insert' query

I have simple table and I need to insert in to it values from array, but only values which are not exists in table and return from query inserted values, how I can do this?
I have next query, but it just inserts values:
INSERT INTO my_table(id, card_id, size)
VALUES ${myArray.map(item => `($${addDbValue(item.id, dbValues)},
$${dbValues.push(item.card_id)}::int, '24')`)}
`, dbValues)
unique is card_id
You seem to want on conflict and returning:
insert into my_table(id, card_id, size)
values (?, ?, ?)
on conflict (card_id) do nothing
returning *
The query inserts the new row, and returns the entire row (including columns that were not initially given for insert). If a row already exists with the same card_id, the insert is not performed (for this to work, you need a unique index or constraint on card_id).
Note that you should be using query parameters (as shown above) rather than concatenating variables in the query string. Have a look at the parameterized query feature of your client.

How to get column_name and table_name from DML statement?

I am building an algorithm to mock DB operations.
I have one problem, how i can get Table name and column names from a DML statement?
i.e:
string = "SELECT id,name FROM USER_TBL"
string TABLE_NAME = getTableName(string); //this will return "USER_TBL"
array COLUMN_NAME = getColumnNames(string); //this will return ["id","name"]
If i consider these as string manipulation, how i can design algorithm to get table name & column names?
Currently i use following logic,
function getTableName(iString){
//find string between "FROM " to next " "(space) and return it
}
function getColumnNames(string){
//get string between "SELECT " and " FROM" and split string based on "," (comma) and return it
}
I wanted to know is there any algorithm already available for this(for reference)? What and all the cases i need to handle other than these?
I don't know an algorithm, but a few things that come to my mind:
Table name
- Tablenames can have aliases, but with your logic that should be OK
- Can be prefixed with the database name (dbname.tblname) -> so you might need to look for dots before returning the string
- Tablename can be a derived table --> select statement in brackets. In that case there is not really a simple to tablename derive
Column names
- Column names can have aliases, not sure how you want to handle that.
- What about calculations, conditions (Case when...) --> that is not just a column name but a combined or calculated value. In that case no real column name to retrieve
- You can have also select statements for single columns in the resultset
So overall, your approach works if there is no complexity in the sql queries, if yes, your logic might not return what you want to.
What about other statements, like UPDATE and INSERT? Is that also something that you want to parse?

Categories