I've got tables like the following (Django model definition, with a postgres database underlying it):
class Person(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=300)
class Owner(models.Model):
id = models.IntegerField()
person = models.ForeignKey(Person)
I use a Python script to set up my database from CSV files. The raw files list Owners with an integer id and an integer 'person' field, which maps to the integer in Person.id.
However, given that the 'person' column in Owner expects a Person object, how do I write a raw SQL string to insert value into Owner?
owner_id = 665
person_id = 330
sql_string = 'INSERT INTO owner (id, person) VALUES (' +
sql_string += owner_id + ', ' + ???? + ');'
You don't say why you need to do this in raw SQL. And I also don't understand why you're using structidx and person in the SQL when your PK field is called id - and the underlying column name for person is person_id. So your code should be:
sql_string = "INSERT INTO owner (`id`, `person_id`) VALUES (%s, %s)"
cursor = connection.cursor()
cursor.execute(sql_string, (665, 330))
Note that it's always good practice to use the Python db-api's quoting functionality, as I have here, to avoid SQL injection attacks.
Related
Suppose you have the following SQL Query to create a table called notes and store data in it :
CREATE TABLE notes (
id INTEGER PRIMARY KEY,
username TEXT,
token TEXT,
text TEXT
);
INSERT INTO notes (username, token, text) VALUES ('alice', 'token-a', 'Reminder: buy milk');
INSERT INTO notes (username, token, text) VALUES ('alice', 'token-a', 'I like Bob');
INSERT INTO notes (username, token, text) VALUES ('bob', 'token-b', 'TODO: write tests');
Now to attempt SQL injection to get all alice's notes without knowing her token where the query to get the data is given as :
'''SELECT text
FROM notes
WHERE token = '%s'
''' % token
What should be the text send in the variable token so as to perform SQL injection and get all alice's notes.
Try Something like this-
';SELECT text
FROM notes
WHERE username = 'alice
SQL Injection can be implemented by concatenating the SQL statement with the input parameters. For example, the following statement is vulnerable to SQL Injection:
String statement = "SELECT ID FROM USERS WHERE USERNAME = '" + inputUsername + "' AND PASSWORD = '" + hashedPassword + "'";
An attacker would enter a username like this:
' OR 1=1 Limit 1; --
Thus, the executed statement will be:
SELECT ID FROM USERS WHERE USERNAME = '' OR 1=1 Limit 1; --' AND PASSWORD = 'Blob'
Hence, the password part is commented, and the database engine would return any arbitrary result which will be acceptable by the application.
I found this nice explanation on the free preview of "Introduction to Cybersecurity for Software Developers" course.
https://www.udemy.com/course/cybersecurity-for-developers-1/
It also explains how to prevent SQL Injection.
Let's say I have an associative array (defined in another language) like so:
apply = {
'qwer': ['tju', 'snf', 'rjtj', 'sadgg']
'asdf': ['rtj', 'sfm', 'rtjt', 'adjdj']
...
'zxcv': ['qwr', 'trj', '3w4u', '3tt3']
}
And I have a table like so:
CREATE TABLE apples (
id integer,
name varchar(10),
key varchar(10),
value varchar(10)
);
I want to apply an update where if apples.value is in one of the lists of the apply variable, then set the apples.key to the key of the array. If apples.value was tju then set apples.key to qwer.
My current approach looks like this (mixing PostgreSQL with any procedural language):
for key in apply.keys:
UPDATE apples SET key=$key
FROM (SELECT unnest(array($apply[key])) AS value) AS update_table
WHERE value=update_table.value
I want to do this in a single statement.
As proof of concept for the given example, with the string formatted exactly as you display:
Demonstrating a prepared statement, like your client probably uses.
PREPARE my_update AS
UPDATE apples a
SET key = upd.key
FROM (
SELECT trim (split_part(key_val, ': ', 1), ' ''') AS key
, string_to_array(translate(split_part(key_val, ': ', 2), '[]''', ''), ', ') AS val_arr
FROM unnest(string_to_array(trim($1, E'{}\n'), E'\n')) key_val
) upd
WHERE a.value = ANY(upd.val_arr);
EXECUTE in the same session any number of times:
EXECUTE my_update($assoc_arr${
'qwer': ['tju', 'snf', 'rjtj', 'sadgg']
'asdf': ['rtj', 'sfm', 'rtjt', 'adjdj']
'zxcv': ['qwr', 'trj', '3w4u', '3tt3']
}$assoc_arr$);
SQL Fiddle.
Related:
Insert text with single quotes in PostgreSQL
Split given string and prepare case statement
But I would rather process the type in its original language and pass key and val_arr separately.
Let's say I have following SQL schema:
SQL
TABLE Person COLUMNS
(
ID AS INTEGER,
Firstname AS varchar(100),
Lastname AS varchar(100),
Fullname AS (Firstname + ' ' + Lastname)
)
GO
INSERT INTO Person (ID, Firstname, Lastname) VALUES (1, 'Simon', 'Dugré')
GO
VB retreiving class
Dim da as new SqlDataAdapter("SELECT ID, Firstname, Lastname, FullName FROM Person WHERE ID = 1", cn)
Dim ds as new DataSet()
da.Fill(ds)
da.FilleSchema(ds) ' I tried this before or after da.fill, just in case '
Dim dr as DataRow = ds.table(0).row(0)
Assert.IsTrue(dr("Firstname") = "Simon") ' Returns true '
Assert.IsTrue(dr("Lastname") = "Dugré") ' Returns true '
Assert.IsTrue(dr("Fullname") = "Simon Dugré") ' Returns true '
dr("Firstname") = "Nomis"
Assert.IsTrue(dr("Fullname") = "Nomis Dugré") ' Return False, it is still "Simon Dugré" in it. '
While debugging in the immediate window, I see the following:
?dr.table.Columns("Fullname").Computed ' It says it is set to false... Duhhh... no!'
More informations
I saw at a few places that there is a way to "hardcode" computed column formula directly from code by referring to the property instead of SQL fields... But I can't do that because to keep this question readable, I removed a lot of code but this is in fact part of a whole abstract and generic scenario.
My question is, is there in fact a way to have formula retrieved directly from SQL while calling da.Fill or da.FillSchema or something? Then, when changing Firstname or Lastname, it will automatically have an effect on the Fullname computed column?
It's not computed in the DataTable. That has no relation to how the column is defined in the database :)
There's no direct translation between SQL expressions and IL, so you'll have to type that in yourself manually, and maintain it as needed.
Of course, if you're only dealing with simple expressions, it's pretty easy to translate them automatically, you'll just have to write a parser. For stuff like this it's not even all that hard.
I have a table which has a column that represents the name of a table we'd like to create. There's a foreign key relationship to another table which has a column representing the name of the columns for the desired table (all data types assumed to be nvarchar). I'm using a stored procedure to create this table. Essentially what I'm doing is getting all of the relevant data from my tables, then building a SQL string up to generate the table, and finally using EXEC sp_executesql #CreateTableSQL.
#CreateTableSQL is generated through string concatenation like this:
SET #CreateTableSQL = 'CREATE TABLE ' + #TableName + ' (' + #ColumnString + ')';
This leaves me vulnerable to SQL injection. If someone were to use a #TableName value of:
C (t int); DROP TABLE MyTable;--
then this would drop MyTable (undesirable).
Can someone help me build this SQL and leave it invulnerable to injection? Help is greatly appreciated. Thanks!
You can make use of QUOTENAME() function which will enforce square brackets [] around the variables(Table and column names) and any value passed to these variables will only be treated as an Object name.
Something like ......
SET #CreateTableSQL = 'CREATE TABLE ' + QUOTENAME(#TableName)
+ ' (' + QUOTENAME(#ColumnString) + ')';
Now even if someone passes a value of C (t int); DROP TABLE MyTable;-- to any of these variables, the whole value C (t int); DROP TABLE MyTable;-- will still be treated as an object name.
Let's suppose we have a table defined with:
CREATE TABLE IF NOT EXISTS signals(sigid INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)
This table initially is empty.
I would like to get the sigid for a given name with a select and in case name does not exist, add it and get the new autoincremented id.
I would like to use this query in order to autogenerate ,when needed, a new id that is used as foreign key in another table. I must put attention to the performances so I cannot proceed as :
check if name is present and return id witha a SELECT
if returned id is null create a new entry with an INSERT
get the new id again with a new SELECT
Is it possible do it with a single SELECT-like query ?
Thanks!
i think with an single select no.
Let's say i want to insert id_build = 3, hashed_value = 1 into the big table 'crash'.
The code in example makes first select to check if the value was already in the table, if yes skips the insert with where .. is null then retrive the id from already saved into temporary table.
example :
create temporary table if not exists Variablez(Name TEXT primary key on conflict replace, Value TEXT); --storing vars
insert into Variablez(Name, Value) values ('tmp_id', (select id_crash from crash where hashed_value = "1" )); --put id if was existing
insert into crash(id_build, hashed_value) select 3, 1 where (select Value from Variablez where Name = 'tmp_id' ) is null; -- insert if not exists
select
case
when (select Value from Variablez where name = 'tmp_id' ) is null then
'0'
else
'1'
end
as existing,
case
when (select Value from Variablez where name = 'tmp_id' ) is null then
(select id_crash from crash where hashed_value = "1")
else
(select Value from Variablez where name = 'tmp_id')
end
as id_returned;
if the table is empty and you are the one filling it in just one shot (and you don't need to do it again later on when there is data in the table), AND you don't have too many rows, then you could just cache the names you have already inserted and look them up in memory.
It's more of a comment, I guess.
there is also this for getting the last inserted id:
SELECT last_insert_rowid();
But if the above applies, you are even faster by assigning the ids yourself, and not define it as AUTOINCREMENT. Then you don't need to get the last inserted id, you just keep a counter and all the names inserted and increment for each new name you find.
CREATE TABLE IF NOT EXISTS signals(sigid INTEGER PRIMARY KEY NOT NULL, name TEXT)
List<String> insertedNames = new List<String>();
int count = 0;
while(input.hasNext())
{
String name = input.next();
if( !insertedNames.contains(name) )
{
var sql = "insert into table (sigid,name) VALUES (" + count + ", " + name + ")";
executeSql(sql);
insertedNames.add(name);
count++;
}
}
answering your comment
public int getId( string name )
{
String sql = "select id from table where name='" + name + "'";
int theIdForTheName = executeAndGetFirstColumnAsInt(sql);
return theIdForTheName;
}
i don't know what else to tell you...