MySQL login Skript

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Hartmannsgruber
User
Beiträge: 89
Registriert: Mittwoch 15. Januar 2014, 22:30
Wohnort: Bad Kötzting
Kontaktdaten:

Servus Forum,

ich habe heute ein kleines Skript geschrieben, mit dem man sich auf einem MySQL
Server anmeldet und eine Datenbank öffnet / erstellt.
Es werden hierbei ein paar mögliche, häufige Fehler (bei weitem nicht alle) abgefangen.

Kann dieses Skript so verwendung finden, oder tauchen Gedanklichefehler, Ablauffehler, o. ä. auf?
Die Fehlercodes habe ich von der MySQL Seite entnommen.
Die try und except - Blöcke in den Funktionen (erstelle_datenbank_server_verbindung, erstelle_datenbank, erstelle_dbtabelle)
sind vorerst recht spartanisch, da ich zuerst wissen möchte ob meine vorgehensweise richtig ist.

Code: Alles auswählen

import mysql.connector
from mysql.connector import Error, errorcode       

def erstelle_datenbank_verbindung(host_name, user_name, user_passwd, db_name):
    """
    Erstellt die Verbindung zum angegeben Datenbankserver mit dem angegebenen
    Benuternamen und dem entsprechenden Passwort. 
    Es wird dabei die angegebene Datenbank geöffnet.
    """
    
    verbindung = None
    
    try:
        verbindung = mysql.connector.connect(
            host = host_name,
            user = user_name,
            passwd = user_passwd,
            database = db_name
        )
        print("MySQL Datenbank Verbindung erfolgreich")
        
    except mysql.connector.Error as err:
        if err.errno == errorcode.ER_CANT_CREATE_DB:
            # error 1006 Kann keine Datenbank erstellen
            # was soll man da dann machen?
            print(f"Error: '{err}'")
        elif err.errno == errorcode.ER_CANT_OPEN_FILE:
            # error 1016
            # Message: Can't open file: '%s' (errno: %d - %s) 
            # InnoDB reports this error when the table from the InnoDB 
            # data files cannot be found.
            print(err.errno) 
            print(f"Kann die angegebene Datenbank {db_name} nicht öffnen.") 
        elif err.errno == errorcode.ER_FILE_NOT_FOUND:
            # error 1017
            # Message: Can't find file: '%s' (errno: %d - %s) 
            print(err.errno)
            print(f"Es kann die angegebene Datenbank {db_name} nicht gefunden \
                werden.")
        elif err.errno == errorcode.ER_ERROR_ON_READ:
            # error 1024
            # Message: Error reading file '%s' (errno: %d - %s)
            print(err.errno)
            print(f"Es kann nicht von der angegebenen Datei gelesen werden.") 
        elif err.errno == errorcode.ER_DBACCESS_DENIED_ERROR:
            # error 1044            
            # Access denied for user '%s'@'%s' to database '%s'
            print(err.errno)
            print("Login Daten für die angegebene Datenbank sind falsch!")
        elif err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
            # error 1045
            # Message: Access denied for user '%s'@'%s' (using password: %s)
            print(err.errno)
            print("Benutzername oder Passwort ist falsch.")
        elif err.errno == errorcode.ER_BAD_DB_ERROR:
            # error 1049
            # Message: Unknown database '%s' 
            print(err.errno)
            print(f"Datenbank '{db_name}' existiert nicht.")
            verbindung = erstelle_datenbank_server_verbindung(host_name, \
                user_name, user_passwd)
            erstelle_datenbank(verbindung, db_name)
        else:
            print(f"Error: '{err}'")
            
    return verbindung
            
            
def erstelle_datenbank_server_verbindung(host_name, user_name, user_passwd):
    """
    Erstellt nur die Verbindung zum Datenbankserver mit dem angegebenen
    Benutzernamen und dem entsprechenden Passwort
    """
    
    verbindung = None
    
    try:
        verbindung = mysql.connector.connect(
            host = host_name,
            user = user_name,
            passwd = user_passwd
        )
    except mysql.connector.Error as err:
        if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
            print("Benutzername oder Passwort ist falsch.")
        else:
            print(f"Error: '{err}'")
        
    return verbindung


def erstelle_datenbank(verbindung, db_name):
    """
    Erstellt auf einem verbunden Datenbankserver eine neue Datenbank
    """
    cursor = verbindung.cursor()
    
    try:
        cursor.execute(f"""CREATE DATABASE {db_name}""")
        print(f"Datenbank {db_name} wurde erfolgreich erstellt")
    except mysql.connector.Error as err:
        if err.errno == errorcode. ER_DB_CREATE_EXISTS:
            # error 1007
            # Message: Can't create database '%s'; database exists
            print(f"Datenbank {db_name} existiert bereits.")
        else:
            print(f"Error: '{err}'")
        

def erstelle_dbtabelle(verbindung, tabelle):  
    
    cursor = verbindung.cursor()
     
    try:
        cursor.execute(tabelle)
        print(f"Erstelle Tabelle {tabelle}: ", end="")
    except mysql.connector.Error as err:
        if err.errno == errorcode.ER_TABLE_EXISTS_ERROR:
            print("existiert bereits.")
        else:
            print(f"Error: '{err}'")
    else:
        print("OK")
        
cursor = erstelle_datenbank_verbindung("localhost", "root", "passwort", "tabellenname")
bsp_tabelle = """CREATE TABLE pausen (
    Vorname VARCHAR(30) NOT NULL,
    Nachname VARCHAR(30) NOT NULL
    );
"""
erstelle_dbtabelle(cursor, bsp_tabelle)
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hartmannsgruber: Du nennst etwas `cursor` was eigentlich eine `verbindung` ist, was sehr verwirrend ist.

Und nein, genau das macht man *nicht*. Du wandelst hier Ausnahmen in `print()`-Ausgaben um und *einen* speziellen Rückgabewert für den Fehlerfall — `None`. Und darauf testest Du noch nicht einmal, so das unweigerlich Folgefehler mit Ausnahmen auftreten werden, an denen man dann nicht mehr erkennen kann (im Code) warum das Problem eigentlich aufgetreten ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Hartmannsgruber
User
Beiträge: 89
Registriert: Mittwoch 15. Januar 2014, 22:30
Wohnort: Bad Kötzting
Kontaktdaten:

Zum Verständnis für mich.
Dadurch, dass bei den auftretenden Fehlern kein Programmabruch, sondern nur
eine print-Anweisung ausgegeben wird treten Folgefehler auf.
Diese Folgefehler können im späteren Programmverlauf nicht mehr genau nachvollzogen werden.

Die erste Anweisung das setzen von "None", ist das falsch?
Wie sollten dann die Try except - Blöcke sinnvoller gestalltet werden, bzw.
sind diese dann eigentlich nötig?

Die Begirffe cursor und "connection" habe ich aus tutorials übernommen.
Die darin an den gleichen stellen so verwendet wurden.
Wann spricht man dann genau von einem cursor und einer verbindung.

LG
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

`erstelle_datenbank_verbindung` liefert eine Verbindung und keinen Cursor, wie der Name schon sagt. An anderer Stelle verwendest Du `cursor` korrekt. Exceptions fängt man erst dort ab, wo sie sinnvoll verarbeitet werden können.
Also erst um das Hauptprogramm, denn bei einem Fehler muss man sofort das Programm verlassen können, weil man ja ohne Verbindung nichts machen kann. Wenn du sprechende Fehlermeldungen möchtest, würde ich das in exakt eine Funktion für alle Oracle-Fehlernummern packen.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei das soweit ich das sehe auch hauptsächlich danach aussieht das hier Fehlermeldungen auf Deutsch übersetzt werden, denn die sprechenden Fehlermeldungen gehen soweit ich das sehe nicht über das hinaus was auch schon in den Originalen steht. Das ist nicht die Aufgabe eines Programms! Wenn man die MySQL-Fehlermeldungen auf Deutsch haben will, dann stellt man bei der Datenbank oder der Datenbankanbindung ein, dass MySQL die Meldungen auf deutsch liefern soll, und macht so etwas nicht selbst.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hartmannsgruber: Es wird übrigens `Error` importiert aber nicht verwendet.

Es gehen beim Übersetzen der Meldungen in den Ausnahmen sogar noch Informationen verloren, denn in den Kommentaren stehen ja auch eine Menge Platzhalter für Sachen wie Datenbanknamen, Host, Benutzer und so weiter die in der originalen Ausnahme natürlich mit den entsprechenden Werten gefüllt werden.

Ich sehe gerade das in `erstelle_datenbank_verbindung()` in den ganzen Übersetzungen tatsächlich auch eine Behandlung einer Ausnahme versteckt ist, die eine Verbindung ohne den Datenbanknamen herstellt und versucht die Datenbank anzulegen. Dieses nicht ganz so kleine Detail wird im Docstring unterschlagen! Das heisst wenn man sich beim Datenbanknamen vertippt, bekommt man keine Ausnahme, sondern es wird die Datenbank still und heimlich angelegt.

Und diese Ausnahmebehandlung ist auch das Einzige was eine eigene Funktion sinnvoll macht. Denn sonst wäre es nur ein `connect()`-Aufruf.

Die dafür aufgerufe `erstelle_datenbank_server_verbindung()` macht ausser unsinniger Ausnahmebehandlung nichts weiter als `connect()` aufzurufen, also braucht man diese Funktion nicht. Auch `erstelle_datenbank()` macht nichts sinnvolles sondern verschluckt/verschleiert nur Ausnahmen.

Die Verbindung wird zwar aufgebaut/erstellt, aber nicht wieder geschlossen.

Tabellennamen benenne ich immer in der Einzahl, denn so ein CREATE beschreibt *einen* Eintrag in der Tabelle. Das ist wie eine Klasse in Python. Das fällt besonders auf wenn man ein ORM verwendet.

Der Tabelle fehlt auch ein Primärschlüssel. Und eventuell eine UNIQUE-Einschränkung für Vor- und Nachname, falls die als Paar nur einmal vorkommen dürfen.

`erstelle_tabelle()` ist im Grunde auch keine eigene Funktion Wert.

Code: Alles auswählen

#!/usr/bin/env python3
from contextlib import closing

import mysql.connector
from mysql.connector import errorcode


def erstelle_datenbankverbindung(hostname, username, password, database):
    """
    Erstellt die Verbindung zum angegeben Datenbankserver mit dem angegebenen
    Benuternamen und dem entsprechenden Passwort.
    Es wird dabei die angegebene Datenbank geöffnet und gegebenfalls erstellt
    falls es sie noch nicht gibt.
    """
    try:
        return mysql.connector.connect(
            host=hostname,
            user=username,
            passwd=password,
            database=database,
        )
    except mysql.connector.Error as err:
        if err.errno == errorcode.ER_BAD_DB_ERROR:
            verbindung = verbindung = mysql.connector.connect(
                host=hostname, user=username, passwd=password
            )
            verbindung.cursor().execute(f"CREATE DATABASE {database}")
            return verbindung
        else:
            raise


def main():
    with closing(
        erstelle_datenbankverbindung(
            "localhost", "root", "passwort", "tabellenname"
        )
    ) as verbindung:
        #
        # TODO UNIQUE für die Kombination von Vor- und Nachname?
        #
        verbindung.cursor().execute(
            "CREATE TABLE pause ("
            "  id INTEGER AUTO_INCREMENT PRIMARY KEY"
            "  vorname VARCHAR(30) NOT NULL,"
            "  nachname VARCHAR(30) NOT NULL"
            ")"
        )


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten