Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Mabenan

Frischling

Beiträge: 24

Wohnort: Bayern

Beruf: IT-Berater

  • Private Nachricht senden

21

13.05.2015, 08:44

Bloß das du keine Null werte übergibt sondern für die Spalte ein Default gepflegt ist.

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

22

13.05.2015, 10:50

@Mods: Eventuell sollten wir das ganze hier abspalten...

Bloß das du keine Null werte übergibt sondern für die Spalte ein Default gepflegt ist.

Gerade selbst nochmal ausprobiert (MySQL 5.6.21):

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
mysql> CREATE TABLE TEST_TB (
    ->  ID INTEGER AUTO_INCREMENT PRIMARY KEY,
    ->  BALANCE INTEGER NOT NULL,
    ->  SALARY INTEGER NOT NULL
    -> );
Query OK, 0 rows affected (0.37 sec)

mysql> DESC TEST_TB;
+---------+---------+------+-----+---------+----------------+
| Field   | Type    | Null | Key | Default | Extra          |
+---------+---------+------+-----+---------+----------------+
| ID      | int(11) | NO   | PRI | NULL    | auto_increment |
| BALANCE | int(11) | NO   |     | NULL    |                |
| SALARY  | int(11) | NO   |     | NULL    |                |
+---------+---------+------+-----+---------+----------------+
3 rows in set (0.06 sec)

mysql> INSERT INTO TEST_TB (BALANCE) VALUES (1000);
Query OK, 1 row affected, 1 warning (0.08 sec)

mysql> SHOW WARNINGS;
+---------+------+---------------------------------------------+
| Level   | Code | Message                                     |
+---------+------+---------------------------------------------+
| Warning | 1364 | Field 'SALARY' doesn't have a default value |
+---------+------+---------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT * FROM TEST_TB;
+----+---------+--------+
| ID | BALANCE | SALARY |
+----+---------+--------+
|  1 |    1000 |      0 |
+----+---------+--------+
1 row in set (0.00 sec)

Man beachte, wie als Default anscheinend NULL definiert ist, er dann beim Insert die Warnung ausgibt, es sei kein Default definiert, aber dann trotzdem einfach eine 0 einfügt.

Noch ein Beispiel: Wenn du einem User Rechte geben möchtest, den Username im GRANT-Statement aber falsch schreibst, was tut MySQL dann wohl? Einen Fehler ausgeben? Nö, einfach einen neuen User mit den Rechten anlegen. Kann man auch ausschalten, so wie alle anderen fragwürdigen Default-Einstellungen von MySQL, aber am Ende gilt immer der Leitsatz: "Das sollte standardmäßig so sein, aber man kann es immerhin so einstellen!" (Wobei sie das in einem der 5.7.x Updates glaube ich endlich gefixt haben. Aber ich bezweifle, dass die Mehrheit der MySQL DBs dieser Welt dieses Update schon eingespielt haben, XAMPP läuft noch mit 5.6.21...)

Noch einer meiner Lieblinge: CHECK constraint in MySQL is not working

Noch mehr Nonsense gefällig? Schauen wir mal, wie MySQL Boolean-Werte definiert:

Zitat

BOOL, BOOLEAN

These types are synonyms for TINYINT(1). A value of zero is considered false. Nonzero values are considered true

Dann schauen wir doch mal...

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
mysql> CREATE TABLE IF NOT EXISTS TEST_TB (
    ->  BOOLS BOOLEAN NOT NULL
    -> );
Query OK, 0 rows affected (4.02 sec)

mysql> DESC TEST_TB;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| BOOLS | tinyint(1) | NO   |     | NULL    |       |
+-------+------------+------+-----+---------+-------+
1 row in set (0.01 sec)

Sehen wir uns mal an, was man mit diesem tinyint so anstellen kann :)

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> INSERT INTO TEST_TB VALUES (TRUE), (FALSE), (1), (0), (42), (-42);
Query OK, 6 rows affected (0.09 sec)
Records: 6  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM TEST_TB;
+-------+
| BOOLS |
+-------+
|     1 |
|     0 |
|     1 |
|     0 |
|    42 |
|   -42 |
+-------+
6 rows in set (0.00 sec)

Ok, war zu erwarten. Wir erinnern uns jetzt nochmal an das Zitat von oben:

Zitat

Nonzero values are considered true

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SELECT
    ->  BOOLS,
    ->  IF(BOOLS = TRUE, "TRUE!", "FALSE!") TEST
    -> FROM TEST_TB;
+-------+--------+
| BOOLS | TEST   |
+-------+--------+
|     1 | TRUE!  |
|     0 | FALSE! |
|     1 | TRUE!  |
|     0 | FALSE! |
|    42 | FALSE! |
|   -42 | FALSE! |
+-------+--------+
6 rows in set (0.00 sec)

wat

Ich gebe zu, dass man ein ordentliches Ergebnis erhält, wenn man IF(BOOLS, "TRUE!", "FALSE!") verwendet, aber wir haben hier wieder einen Fall, bei dem das Ergebnis von meinen Erwartungen und Erfahrungen mit anderen DBMS abweicht. Die Frage, wieso MySQL überhaupt Booleans nicht einfach auf feste "true" und "false" Werte festlegt, steht aber immer noch im Raum.

Aber ich hab grad soviel Spaß mit MySQL, da kann ich noch einen ziehen!

Quellcode

1
2
3
4
5
6
7
8
9
mysql> CREATE TABLE TEST_TB (
    ->  ID INTEGER AUTO_INCREMENT PRIMARY KEY,
    ->  VALUE INTEGER NOT NULL
    -> );
Query OK, 0 rows affected (0.30 sec)

mysql> INSERT INTO TEST_TB (VALUE) VALUES (0), (1), (42);
Query OK, 3 rows affected (0.08 sec)
Records: 3  Duplicates: 0  Warnings: 0

Hat jemand Hunger?

Quellcode

1
2
3
4
5
6
7
mysql> SELECT * FROM TEST_TB WHERE VALUE = "APFELKUCHEN";
+----+-------+
| ID | VALUE |
+----+-------+
|  1 |     0 |
+----+-------+
1 row in set, 1 warning (0.03 sec)


Ähm. Was? Ah, wir haben zumindest eine Warnung, schauen wir mal...

Quellcode

1
2
3
4
5
6
7
mysql> SHOW WARNINGS;
+---------+------+-------------------------------------------------+
| Level   | Code | Message                                         |
+---------+------+-------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'APFELKUCHEN' |
+---------+------+-------------------------------------------------+
1 row in set (0.00 sec)

Heute habe ich gelernt, dass ein Apfelkuchen eine Gleitkommazahl ist. Noch dazu habe ich gelernt, dass MySQL (out of the box) keine Fehlermeldung oder Warnung ausgibt, wenn ich eine Gleitkommazahl (Oder einen Apfelkuchen) in ein Integer-Feld stecken will.
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

23

13.05.2015, 11:36

Das Problem mit den Booleans ist leicht erklärt: in SQL gibt es keine Booleans und jedes DBMS bietet da leicht andere Möglichkeiten an.
Der Vergleich, den du anstellst (BOOLS = TRUE) resultiert also in einem Vergleich von Zahlen (BOOLS = 1). Das Ergebnis ist also richtig.

Aber ansonsten stimme ich soweit zu, bei Fehlern durch eine falsche Verwendung sollten standardmäßig auch Fehlermeldungen ausgegeben werden. Allerdings merkt man an der Formulierung schon, dass diese Fehler eben nur durch eine falsche Verwendung auftreten. In seiner Anwendung weiß man, wie die Struktur der Datenbank aussieht und weiß somit auch, welche Werte gültig und ungültig sind. Ein String ist einfach keine Zahl und somit liegt beim Apfelkuchen bereits eine falsche Verwendung vor.

Das einzige aufgezeigte Problem, was bei einem normalen Verwendung auftreten kann, ist das implizite Einfügen von 0 an Stelle von null und einem Fehlschlag. Aber genauso wie bei MySQL dieser falsche Wert eingefügt wird, kann man in Oracle Datenbanken keine Leerstrings speichern, da diese immer in null umgesetzt werden.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

24

13.05.2015, 11:47

Aber ansonsten stimme ich soweit zu, bei Fehlern durch eine falsche Verwendung sollten standardmäßig auch Fehlermeldungen ausgegeben werden. Allerdings merkt man an der Formulierung schon, dass diese Fehler eben nur durch eine falsche Verwendung auftreten. In seiner Anwendung weiß man, wie die Struktur der Datenbank aussieht und weiß somit auch, welche Werte gültig und ungültig sind. Ein String ist einfach keine Zahl und somit liegt beim Apfelkuchen bereits eine falsche Verwendung vor.

Was ist bei Nutzereingaben, z.B. über eine Website? Rein theoretisch wäre es hier am saubersten, das Statement an die DB zu schicken, und eine eventuelle Fehlermeldung seitens der DB abzufangen und dem Nutzer zurückzugeben. Wieso muss ich die Aufgabe übernehmen, sämtliche Randfälle in meiner Anwendung abzudecken, wenn ich mit anderen DBMS die Fehlerfälle von diesen klären lassen kann und sie nur zurückgeben, ob die Query i.O. war?
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

25

13.05.2015, 12:09

Im eigenen Code wird man mit den eingegebenen Werten aber auch etwas machen wollen, deshalb _muss_ man dafür sorgen, dass man bei sich im Code aus dem vom Benutzer erhaltenen Text eine Zahl macht. Wenn das nicht geht, muss man die Datenbank nicht mit ungültigen SQL-Statements belästigen und wenn es funktioniert kann man eine gültige Abfrage aufbauen.

Mal ganz abgesehen davon, dass man mit einer solchen Vorgehensweise sich Sicherheitslücken reinholen kann. Nehme man einmal an, dass die Query über sowas wie "INSERT INTO mytable(mynumber) VALUES(" + userinput + ");" aufgebaut wird, dann würde man im Normalfall eine gültige Eingabe bekommen (bei "400" als Eingabe wäre es "INSERT INTO mytable(mynumber) VALUES(400);") und bei den meisten ungültigen Eingaben (bei "Apfelkuchen" wäre es "INSERT INTO mytable(mynumber) VALUES(Apfelkuchen);"). Bei bestimmten Eingaben würde man keinen Fehler erhalten, aber dafür würden noch weitere, unerwünschte Dinge passieren: bei "0); DROP TABLE mytable; commit; --" hätte man die Abfrage(n) "INSERT INTO mytable(mynumber) VALUES(0); DROP TABLE mytable; commit; --);" erhalten. Der Schaden sollte ersichtlich sein. ;)

Außerdem sollten viele Prüfungen bereits an der Oberfläche durchgeführt werden, damit der Benutzer nicht erst ein Formular an einen Server schicken muss, um die Meldung zu bekommen, dass bestimmte Eingaben fehlerhaft waren.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

26

13.05.2015, 12:20

Mal ganz abgesehen davon, dass man mit einer solchen Vorgehensweise sich Sicherheitslücken reinholen kann.

Wird von Prepared Statements abgedeckt.

Quellcode

1
2
3
4
5
6
7
8
9
public function doesUserExist($username) {
    $stmt = $this->pdo->prepare('SELECT COUNT(*) FROM USERS_TB WHERE NAME = ?');
    if($stmt->execute(array($username))) {
        return $stmt->fetchColumn() > 0;
    }
    else {
        // Handle Errors...
    }
}
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

Mabenan

Frischling

Beiträge: 24

Wohnort: Bayern

Beruf: IT-Berater

  • Private Nachricht senden

27

13.05.2015, 12:27

Rein theoretisch wäre es hier am saubersten, das Statement an die DB zu schicken, und eine eventuelle Fehlermeldung seitens der DB abzufangen und dem Nutzer zurückzugeben.


Das soll Ironisch sein oder? Wo ist es bitte denn am saubersten einfach ein Statement an die DB zu schicken?

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

28

13.05.2015, 13:36

Mal ganz abgesehen davon, dass man mit einer solchen Vorgehensweise sich Sicherheitslücken reinholen kann.

Wird von Prepared Statements abgedeckt.

Quellcode

1
2
3
4
5
6
7
8
9
public function doesUserExist($username) {
    $stmt = $this->pdo->prepare('SELECT COUNT(*) FROM USERS_TB WHERE NAME = ?');
    if($stmt->execute(array($username))) {
        return $stmt->fetchColumn() > 0;
    }
    else {
        // Handle Errors...
    }
}

Wozu man aber den richtigen Datentypen braucht, da sonst die Zahl als String zur Datenbank geschickt wird, oder aus einem String, der zufällig nur eine Zahl beinhaltet, eine Zahl gemacht wird.
Nicht falsch verstehen: prepared Statements oder ähnliche Konstrukte zum Aufbau von Anfragen sollte man schon verwenden, aber mit den richtigen Typen.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

29

13.05.2015, 13:43

Rein theoretisch wäre es hier am saubersten, das Statement an die DB zu schicken, und eine eventuelle Fehlermeldung seitens der DB abzufangen und dem Nutzer zurückzugeben.


Das soll Ironisch sein oder? Wo ist es bitte denn am saubersten einfach ein Statement an die DB zu schicken?

Wie schon gesagt, Prepared Statement.
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

xardias

Community-Fossil

Beiträge: 2 731

Wohnort: Santa Clara, CA

Beruf: Software Engineer

  • Private Nachricht senden

30

13.05.2015, 17:57

Nimelrian: Wo kommt der ganze Hass denn her? Wurdest du mal gezwungen MySQL zu verwenden? ;)

Werbeanzeige