4

At the moment, I have a database which contains username, password, etc. I am wishing to look into the database to check if duplicates are in there.

con = lite.connect('userInfo.db')
with con:
    cur = con.cursor()
    cur.execute("SELECT * FROM Users WHERE LOWER(Username) = LOWER(?)", (newID,))
    rows = cur.fetchall()
    if len(rows)!=0:
        return "Duplicate detected"

Here is my code at the moment. newID is a new name and I wish to check if there are any existing entries in the database with the same name.

My question is - is the way I am doing it in my code a good idea? I'm mainly concerned with my approach. Should I be using something other than fetchall() ?

Thank you for your time! :) P.S. This is for a website application.

1
  • To save memory, consider replacing fetchall() with fetchone(). Commented Mar 19, 2012 at 5:18

2 Answers 2

5

Here's a way to do exactly what you asked for - find out if a given username already exists:

import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute ("""
              CREATE TABLE users (
                  uid INTEGER PRIMARY KEY AUTOINCREMENT,
                  username TEXT UNIQUE,
                  email TEXT UNIQUE );
             """)

test_users = (
    {'username':"Alice",    'email':"[email protected]"},
    {'username':"Billy",    'email':"[email protected]"},
    {'username':"Charles",  'email':"[email protected]"},
    {'username':"Dick",     'email':"[email protected]"},
    {'username':"Emily",    'email':"[email protected]"},
    {'username':"Faramir",  'email':"[email protected]"},
)

for user in test_users:
    conn.execute("INSERT INTO users (username, email) VALUES (?,?)",
                 (user['username'],user['email'])
                )

result = conn.execute("SELECT COUNT(*) FROM users WHERE username='Alice'")
number_of_Alices = result.next()[0] # number_of_Alices will be 1

Since all you want is a COUNT this is adequate.


Really, though, you shouldn't be enforcing the uniqueness of the usernames yourself. Let the database do that for you by specifying the field to be either UNIQUE or PRIMARY KEY.

If you try to insert "Alice", "[email protected]" after creating the database like above, this will get you an sqlite3.IntegrityError:

>>> conn.execute("""INSERT INTO users (username, email)
...                     VALUES ("Alice", "[email protected]");""")
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
sqlite3.IntegrityError: column username is not unique

To detect this, try to run the INSERT and detect whether it fails.

try:
    conn.execute("""INSERT INTO users (username, email)
                    VALUES ("Alice", "[email protected]");""")
except sqlite3.IntegrityError:
    print ("Username 'Alice' was already taken.")

Incidentally, be very careful with using the upper/lowercase functions. Does "Главное в новостях".lower() mean what you think it means?


Since you mention this is for a webapp, I'll just remind you to store your passwords as salted hashes of the password, using unique salts for each user (never as plain text!), and to guard against SQL injection by using the (?,?,?,?,...) placeholders for SQL queries, not the (%s,%s) % (var1, var2) string interpolation method.

To quote the sqlite3 documentation:

Usually your SQL operations will need to use values from Python variables. You shouldn’t assemble your query using Python’s string operations because doing so is insecure; it makes your program vulnerable to an SQL injection attack.

Instead, use the DB-API’s parameter substitution. Put ? as a placeholder wherever you want to use a value, and then provide a tuple of values as the second argument to the cursor’s execute() method. (Other database modules may use a different placeholder, such as %s or :1.) For example:

If you don't do this, then someone could request the username Robert Menzies; DROP TABLE users; with hilarious results.

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

3 Comments

Thank you for your fine explanation. Greatly appreciated. Just out of curiosity, what does Robert Menzies; DROP TABLE users; actually do and why? Thans!
Actually the name would need to be Robert Menzies); DROP TABLE users;. Then if your query is like "INSERT INTO users VALUES (%s)" % username, then after the variable substitution you will have "INSERT INTO users VALUES (Robert Menzies); DROP TABLE users; - which will insert Robert Menzies into the users table, then delete the entire users table.
This is called an SQL injection attack, and is possible because the ; character ends an SQL command and begins a new one. One of the things you have to do is escape ; characters in user input so this doesn't happen. Using the recommended practice - (?,?,?...) placeholders - in the sqlite3 documentation will guard against this attack and more.
0

Why not set the field in SQLite to unique which will prevent duplicate entries in the first place.

8 Comments

What happens if a duplicate entry is detected?
@Vincent if you set Username colummn to unique and a insert is made into the Users table with an already existing Username it will result in a fail. e.g. there will never be any duplications in the first place.
So would my program essentially 'crash' ? Sorry, I'm new and just starting out at coding. Your help is greatly appreciated.
You will get a SQLite3.OperationalError which says "something went wrong". If duplicate usernames could be reasonably expected you can catch the exception using a try... except block and recover. If duplicate usernames should never be attempted in the first place it's OK to crash.
And the username field should really be PRIMARY KEY. That said, using a separate user_id INTEGER PRIMARY KEY AUTOINCREMENT and username TEXT UNIQUE is probably a better way of doing things - that way strange characters in the username won't cause bugs (will your code handle the username "Главное в новостях" correctly?) and users will be free to change their usernames at will (since it's not being used as a key anywhere else.)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.