ESP8266 sqlite3: out of memory bei INSERT INTO Befehlen

Hallo Zusammen,

ich will Daten in einer sqlite Datenbank, die sich auf einer SD-Karte die wiederum an einem ESP8266 WEMOS D1 mini Modul angeschlossen ist benutzen.
Die Datenbank (Tabellen, Indexe usw.) habe ich zuvor mit dem SQLite Studio erstellt und auch die empfohlene Blockgröße von 512 eingestellt und den vacuum befehl zum aufräumen abgesetzt.

Die Stacksize habe ich bereits in der esp8266/cont.h Datei auf den Wert von 6144 gestellt.

Soweit funktioniert der zugriff auf die Datenbank auch vom ESP8266. Ich kann die Datenbank öffnen und auch SELECT Befehle absetzen.

Wenn ich jedoch einen INSERT INTO Befehl absetzen möchte bekomme ich die Rückmeldung "Out of Memory".
Ich bin mittlerweile am Ende und frage mich ob mir hier jemand einen Tipp geben kann wo ich suchen kann.

In der folgenden Funktion soll in der Datenbank ein neues Material angelegt werden. Der obere SQL-Befehl wird problemlos ausgeführt, sobald aber der INSER Befehl kommt bekomme ich einen Fehler zurück:


int AddMaterial(sqlite3 **db, String bez, String besch, String einh, double umr){
  sqlite3_stmt *res;
  const char *tail;
  char *err_msg=0;
  String sql;
  int rc;
  long nummer;


  //Letzte Materialnummer auslesen
  sql = "SELECT MAX(nummer) FROM material\0";
  rc = sqlite3_prepare_v2(*db, sql.c_str(), -1, &res, &tail);
  if (rc != SQLITE_OK) {
      Serial.println(sql);
      String resp = "Failed to fetch data: ";
      resp += sqlite3_errmsg(*db);
      Serial.println(resp.c_str());
      sqlite3_finalize(res);
      return(1);
  }
  while (sqlite3_step(res) == SQLITE_ROW) {
     nummer = sqlite3_column_int(res, 0) + 1;
  }

  sqlite3_finalize(res);

  // Den Materialstammdatensatz hinzufügen
  sql = "INSERT INTO material(nummer,bezeichnung,beschreibung,einheit,umrechnung) VALUES(";
  sql = sql + nummer + ",'" + bez + "','" + besch + "','" + einh + "'," + umr;
  sql += ");\0";

  rc = sqlite3_prepare_v2(*db, sql.c_str(), -1, &res, &tail);
  if ((rc = sqlite3_step(res)) != SQLITE_DONE) {
     Serial.println(sql);
     String resp = "Failed to insert data: ";
     resp += sqlite3_errmsg(*db);
     resp += "\n rc=";
     resp += rc;
     Serial.println(resp.c_str());
     sqlite3_finalize(res);
     return(2);
  }

  sqlite3_finalize(res);

  return(0);
}

Die Ausgabe über die Serielle Schnittstelle sieht dann so aus:

21:36:00.907 -> INSERT INTO material(nummer,bezeichnung,beschreibung,einheit,umrechnung) VALUES(1012,'Testmat','Ein neues Material','EI',1.00);
21:36:00.907 -> Failed to insert data: out of memory
21:36:00.907 ->  rc=7

Ich bin auch der Meinung das ich noch genügend freien Speicher habe wenn ich mir das Compile-Ergebnis ansehe:

Ich habe irgendwo gelesen das so um die 20kByte Speicher frei seien sollten.

Ich habe schon diverse Dinge versucht:

  1. Das generierte INSERT SQL-Statement direkt in der Datenbank abgesetzt -> Funktioniert
  2. Die Bestimmung der Abfrage der letzten Nummer in das SQL-Statement der INSERT Anweisung integriert: Hier bekomme ich die Rückmeldung: parser stack overflow:
21:21:07.812 -> INSERT INTO material(nummer,bezeichnung,beschreibung,einheit,umrechnung) VALUES(((SELECT MAX(nummer) FROM material)+1),'Testmat','Ein neues Material','EI',1.00);
21:21:07.812 -> Failed to insert data: parser stack overflow
  1. Den INSERT Befehl mit "sqlite3_exec" abzusetzen -> hier bekomme ich auch die out of memory Meldung.

Hat vielleicht jemand SQLITE3 im Einsatz und kann mir einen Tipp geben wo ich noch schauen kann?

Vielen Dank für alle Hinweise und Tipps,

Artur

Ein Zitat aus der Doku:

The Sqlite implementation occupies considerable amount of memory and should be used with caution if amount of free RAM is less than 20k. Further, due to low RAM available on ESP8266, only Sqlite databases having page size 512 bytes can be supported. Even with such limitation millions of records and gigabyte sized databases can be accessed.

Ich gehe davon aus, dass INSERTs deutlich mehr RAM brauchen als SELECTs, hängt natürlich von der DB ab, aber das Aufdatieren von Indizes und dergleichen kann den auf einem ESP8266 verfügbaren Speicher schnell mal sprengen.

Ich halte die Nutzung von MC als Datenbankserver grundlegend für verfehlt.
Evtl. auf einem ESP32 zu Testzwecken, aber auch nicht produktiv.

Gruß Tommy

Ich befürchte auch fast das der Speicher im ESP8266 einfach nicht reicht. Das mit der page size und den 20KByte hatte ich auch schon gesehen... Alternativ würde ich dann auf den ESP32 gehen. Der hat aber so einen verdammt hohen Stromverbrauch was für ein kleines Batteriebetriebenes System nicht gerade gut ist....

Naja, trotzdem Danke für die Rückmeldung.

Wofür brauchst Du denn in Deinem kleinen System die Datenbank?
Evtl. gibt es bessere Lösungen.

Gruß Tommy

ich habe eine kleine Rezeptverwaltung gebaut und nutze den Webserver vom ESP um auf einem Handy oder Tablett die Erstellung und Abarbeitung der Rezepte durchzuführen. Am ESP hängt dann noch ein wenig "Hardware" die dann über das Rezept angesteuert wird. Ich möchte die Rezeptdaten (Stückliste, Routing, Materialfluss) einfach nicht auf irgendeinem Datenbankserver im Internet oder auf dem Handy haben. Die Daten sollen "im Gerät" bleiben. Die Weboberfläche ist schon soweit fertig und funktioniert auch super weil mehr oder weniger alles in Javascript geschrieben ist und nur die Rezeptdaten und ggf. die ansteuerung der Hardware über den ESP läuft. Bisher habe ich die Rezeptdaten in einzelne Textdateien auf der SD-Karte (jedes Rezept eine Datei) ... ist aber manchmal echt langsamt nach Dingen zu suchen. Da bietet sich dann eben eine Datenbank an.
Wie geschrieben eine Möglichkeit wäre der ESP32 (der hat ja mehr SRAM). braucht aber auch mehr Strom. Vielleicht bekommt man den Strom noch mit einem vernünftigen Powermanagement hin (Sleep-Mode,...)

Sieht so aus als wärst du nur knapp daneben...

Auch wenn ich Tommy recht gebe (ein ESP8266 als Datenbank kann nur als Spaß gemeint sein):

  • Die zu erzeugende neue Nummer separat ermitteln,
  • einen neuen Datensatz mit dieser Nummer erzeugen,
  • danach per UPDATE die anderen Daten hinzufügen,
  • auf String Objekte verzichten

wären meine verzweifelten Versuche...
Ich kenne jetzt sqlite3 nicht wirklich, gibt evtl. statt sqlite3_prepare_v2 auch andere Funktionen, um ein query auszuführen.
Ist überhaut das rc der Funktion sqlite3_prepare_v2 ok?

csv, xml, json

Ich vermute mal, dass der ESP wegen der Akutatoren sowieso stationär versorgt werden muss. Dann ist der Mehrverbrauch ja nicht ganz kritisch.

Gruß Tommy

Hi, danke für die Tipps. Ich probiere das mit dem UPDATE gleich mal aus.
Du hast Recht das mit den String Objekten ist ein echter "Speichergrauß". Ich wollte mal eben etwas schnell zusammenbauen und habe die Arduino Strings genutzt. Leider ist der Quellcode immer weiter gewachsen und auch die Strings haben sich immer mehr in meinen Quellcode verbreitet. Vielleicht sollte ich den Code nochmal überarbeiten und auf strings gehen.

Durchsuchen sich nicht wirklich sinnvoll.

Gruß Tommy

... hm, habs gerade probiert. Leider scheitert der schon bei einem sehr einfachen INSERT Befehl mit out of memory :frowning:

Ja, die returncodes der funktionen sind OK.
Alternativ gibt es noch die sqlite3_exec Funktion. Die macht aber das gleiche wie die sqlite3_prepare_Ve Funktion .. hat aber eine einfachere Syntax weil sie nur für einen SQL Befehl ausgelegt ist. Habe ich aber auch schon ausprobiert. Dort kommt auch der Speicherfehler.

Gleube ich habe einfahc zwei Probleme:
ich habe Arduino Strings verwendet -> besser C - strings nutzen oder noch besser mit pointern arbeiten.
ESP8266 hat einfach für meinen Anwendungsfall zu wenig Speicher -> ESP32 nutzen wenn man eine Minidatenbank mit SQLITE3 nutzen möchte.

Danke an alle, ich greife jetzt zum Lötkolben und baue mal einen ESP32 ein.

Viele Grüße,

Artur

Das mag vom Speicherverbrauch ähnlich sein, inhaltlich ist es das aber auf keinen Fall. Die exec verarbeitet einfach eine Zeichenkette als SQL. Wenn davon Teile aus einer Weboberfläche kommen, ist Dein Konstrukt anfällig für SQL-Injection.
Die prepare bereitet das SQL auf und übernimmt (vereinfacht gesagt) nur passende Variablen, die nicht zu einer Änderung des SQL, wie beim exec führen können.

Gruß Tommy

OK, habe ich bisher nicht gewusst. Danke für den Hinweis. Werde ich dann in meiner weiteren Entwicklung berücksichtigen. Will keinen Angriffsfläche für SQL-injection bieten.

Grüße,
Artur

Hast Du mal versucht, die DB ohne jeden Index zu machen? Geht dann der Insert?

Habs gerade mal versucht. Habe alle Indizies und auch alle Contraints aus der DB gelöscht. So das nur noch die "Nackten" Tabelle in der DB sind...
Leider hat das nicht geholfen. Habe auch gerade gedacht das es das sein könnte weil der ESP8266 nicht großartig irgendwelche Indexe oder Contraints speichern muss... Wie geschrieben hat leider keine Abhilfe gebracht.

... ich doktore jetzt weiter den Code für den ESP32 fit zu machen.

Vielen Dank für deine Rückmeldung,

Artur

Nur zum Verständnis: Das hat ein Sketch auf deinem ESP8266 hingekriegt? Dann fang doch von da aus an, das in Richtung deines "echten" Sketch aufzubohren, und sieh wo es anfängt zu hängen.

Nur zum Verständnis: Das hat ein Sketch auf deinem ESP8266 hingekriegt? Dann fang doch von da aus an, das in Richtung deines "echten" Sketch aufzubohren, und sieh wo es anfängt zu hängen

Ne, ichhabe die SD-Karte aus den ESP genommen und in meinen Rechner eingelegt und anschließend mit einem DB-Tool SQL-Statesmens auf der, auf der SD-Karte befindlichen DB, abgesetzt.

Ich habe mittlerweile das ganze auf einem ESP32 am laufen. War anscheinend echt der geringe Speicher vom ESP8266.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.