2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-10 09:16:53 +00:00

93_DbRep: contrib 8.51.2

git-svn-id: https://svn.fhem.de/fhem/trunk@27037 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2023-01-12 19:58:49 +00:00
parent 3f325a9281
commit 036c0eb56f

View File

@ -1,5 +1,5 @@
##########################################################################################################
# $Id: 93_DbRep.pm 26940 2023-01-02 20:22:51Z DS_Starter $
# $Id: 93_DbRep.pm 27031 2023-01-11 23:20:18Z DS_Starter $
##########################################################################################################
# 93_DbRep.pm
#
@ -59,6 +59,9 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch';
# Version History intern
my %DbRep_vNotesIntern = (
"8.51.2" => "12.01.2023 rewrite sub DbRep_OutputWriteToDB ",
"8.51.1" => "11.01.2023 write TYPE uppercase with writeToDB option, Commandref edited, fix add SQL Cache History ".
"set PRAGMA auto_vacuum = FULL when execute SQLite vacuum command",
"8.51.0" => "02.01.2023 online formatting of sqlCmd, sqlCmdHistory, sqlSpecial, Commandref edited, get dbValue removed ".
"sqlCmdBlocking customized like sqlCmd, bugfix avgTimeWeightMean ",
"8.50.10" => "01.01.2023 Commandref edited ",
@ -1319,6 +1322,7 @@ sub DbRep_Get {
$hash->{HELPER}{IDRETRIES} = 3; # Anzahl wie oft versucht wird initiale Daten zu holen
DbRep_delread ($hash);
DbRep_setLastCmd (@a);
ReadingsSingleUpdateValue ($hash, "state", "running", 1);
@ -3505,7 +3509,7 @@ sub DbRep_averval {
}
$dt = $tn - $to;
$sum += $val1*($dt/$tsum) if($tsum);
$sum += $val1 * ($dt/$tsum) if($tsum);
$val1 = $val;
$to = $tn;
@ -3515,25 +3519,31 @@ sub DbRep_averval {
}
my $aval = (DbRep_checktimeaggr($hash))[2];
if($aval eq "hour") {
@rsf = split(/[ :]/,$runtime_string_first);
@rsn = split(/[ :]/,$runtime_string_next);
@rsf = split /[ :]/,$runtime_string_first;
@rsn = split /[ :]/,$runtime_string_next;
$arrstr .= $runtime_string."#".$sum."#".$rsf[0]."_".$rsf[1]."|";
}
elsif ($aval eq "minute") {
@rsf = split(/[ :]/,$runtime_string_first);
@rsn = split(/[ :]/,$runtime_string_next);
@rsf = split /[ :]/,$runtime_string_first;
@rsn = split /[ :]/,$runtime_string_next;
$arrstr .= $runtime_string."#".$sum."#".$rsf[0]."_".$rsf[1]."-".$rsf[2]."|";
}
else {
@rsf = split(" ",$runtime_string_first);
@rsn = split(" ",$runtime_string_next);
@rsf = split " ",$runtime_string_first;
@rsn = split " ",$runtime_string_next;
$arrstr .= $runtime_string."#".$sum."#".$rsf[0]."|";
}
@wsf = split(" ",$runtime_string_first);
@wsn = split(" ",$runtime_string_next);
$wrstr .= $runtime_string."#".$sum."#".$wsf[0]."_".$wsf[1]."#".$wsn[0]."_".$wsn[1]."|"; # Kombi zum Rückschreiben in die DB
#@wsf = split " ",$runtime_string_first;
#@wsn = split " ",$runtime_string_next;
$runtime_string_first =~ s/\s/_/xs;
$runtime_string_next =~ s/\s/_/xs;
#$wrstr .= $runtime_string."#".$sum."#".$wsf[0]."_".$wsf[1]."#".$wsn[0]."_".$wsn[1]."|"; # Kombi zum Rückschreiben in die DB
$wrstr .= $runtime_string."#".$sum."#".$runtime_string_first."#".$runtime_string_next."|"; # Kombi zum Rückschreiben in die DB
}
}
@ -4349,7 +4359,7 @@ sub DbRep_diffval {
$runtime_string = encode_base64($runtime_string,"");
if($dbmodel eq "OLDMYSQLVER") { # Forum: https://forum.fhem.de/index.php/topic,130697.0.html
$err = DbRep_dbhDo ($name, $dbh, "set \@V:= 0, \@diff:= 0, \@diffTotal:= 0, \@RB:= 1;"); # @\RB = Resetbit wenn neues Selektionsintervall beginnt
($err, undef) = DbRep_dbhDo ($name, $dbh, "set \@V:= 0, \@diff:= 0, \@diffTotal:= 0, \@RB:= 1;"); # @\RB = Resetbit wenn neues Selektionsintervall beginnt
return "$name|$err" if ($err);
}
@ -4966,13 +4976,13 @@ sub DbRep_insert {
my ($sth,$sql,$irow);
# insert into $table mit/ohne primary key
if ($usepkh && $dbloghash->{MODEL} eq 'MYSQL') {
if ($usepkh && $dbmodel eq 'MYSQL') {
$sql = "INSERT IGNORE INTO $table (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)";
}
elsif ($usepkh && $dbloghash->{MODEL} eq 'SQLITE') {
elsif ($usepkh && $dbmodel eq 'SQLITE') {
$sql = "INSERT OR IGNORE INTO $table (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)";
}
elsif ($usepkh && $dbloghash->{MODEL} eq 'POSTGRESQL') {
elsif ($usepkh && $dbmodel eq 'POSTGRESQL') {
$sql = "INSERT INTO $table (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING";
}
else {
@ -6880,7 +6890,7 @@ sub _DbRep_setSessAttrVars {
$pm = ltrim($pm).';';
$pm =~ s/ESC_ESC_ESC/;/gx; # wiederherstellen von escapeten ";" -> umwandeln von ";;" in ";"
my $err = DbRep_dbhDo ($name, $dbh, $pm, "Set VARIABLE or PRAGMA: $pm");
(my $err, undef) = DbRep_dbhDo ($name, $dbh, $pm, "Set VARIABLE or PRAGMA: $pm");
return $err if ($err);
}
}
@ -6910,7 +6920,7 @@ sub _DbRep_setSessVars {
$pm = ltrim($pm).';';
$pm =~ s/ESC_ESC_ESC/;/gx; # wiederherstellen von escapeten ";" -> umwandeln von ";;" in ";"
my $err = DbRep_dbhDo ($name, $dbh, $pm, "Set SQL session variable: $pm");
(my $err, undef) = DbRep_dbhDo ($name, $dbh, $pm, "Set SQL session variable: $pm");
return $err if ($err);
}
}
@ -6940,7 +6950,7 @@ sub _DbRep_setSessPragma {
$pm = ltrim($pm).';';
$pm =~ s/ESC_ESC_ESC/;/gx; # wiederherstellen von escapeten ";" -> umwandeln von ";;" in ";"
my $err = DbRep_dbhDo ($name, $dbh, $pm, "Exec PRAGMA Statement: $pm");
(my $err, undef) = DbRep_dbhDo ($name, $dbh, $pm, "Exec PRAGMA Statement: $pm");
return $err if ($err);
}
}
@ -6972,7 +6982,7 @@ sub _DbRep_execSessPrepare {
$pm = ltrim($pm).';';
$pm =~ s/ESC_ESC_ESC/;/gx; # wiederherstellen von escapeten ";" -> umwandeln von ";;" in ";"
my $err = DbRep_dbhDo ($name, $dbh, $pm, "Exec PREPARE statement: $pm");
(my $err, undef) = DbRep_dbhDo ($name, $dbh, $pm, "Exec PREPARE statement: $pm");
return $err if ($err);
}
}
@ -7078,7 +7088,7 @@ sub DbRep_sqlCmdDone {
delete($hash->{HELPER}{RUNNING_PID});
my $tmpsql = delete $data{DbRep}{$name}{sqlcache}{temp}; # SQL incl. Formatierung aus Zwischenspeicher holen
my $tmpsql = $data{DbRep}{$name}{sqlcache}{temp}; # SQL incl. Formatierung aus Zwischenspeicher holen
my ($erread, $state) = DbRep_afterproc ($hash, $hash->{LASTCMD}); # Befehl nach Procedure ausführen
if ($err) {
@ -7091,6 +7101,8 @@ sub DbRep_sqlCmdDone {
return;
}
DbRep_addSQLcmdCache ($name); # Drop-Down Liste bisherige sqlCmd-Befehle füllen und in Key-File sichern
my ($rt,$brt) = split ",", $bt;
my $srf = AttrVal($name, "sqlResultFormat", "separated");
my $srs = AttrVal($name, "sqlResultFieldSep", "|");
@ -7103,8 +7115,6 @@ sub DbRep_sqlCmdDone {
ReadingsBulkUpdateValue ($hash, 'sqlCmd', $tmpsql);
ReadingsBulkUpdateValue ($hash, 'sqlResultNumRows', $nrows);
DbRep_addSQLcmdCache ($name); # Drop-Down Liste bisherige sqlCmd-Befehle füllen und in Key-File sichern
if ($srf eq "sline") {
$rowstring =~ s/§/]|[/g;
$rowstring =~ s/\|°escaped°\|/§/g;
@ -7211,7 +7221,7 @@ sub DbRep_dbmeta {
# due to incompatible changes made in MyQL 5.7.5, see http://johnemb.blogspot.de/2014/09/adding-or-removing-individual-sql-modes.html
if($dbmodel eq "MYSQL") {
$err = DbRep_dbhDo ($name, $dbh, "SET sql_mode=(SELECT REPLACE(\@\@sql_mode,'ONLY_FULL_GROUP_BY',''));");
($err, undef) = DbRep_dbhDo ($name, $dbh, "SET sql_mode=(SELECT REPLACE(\@\@sql_mode,'ONLY_FULL_GROUP_BY',''));");
return "$name|$err" if ($err);
}
@ -7760,7 +7770,12 @@ sub DbRep_optimizeTables {
Log3 ($name, 3, "DbRep $name - Size of database $dbname before optimize (MB): $db_MB_start");
$query ="VACUUM";
$query = "PRAGMA auto_vacuum = FULL;";
$err = _DbRep_setSessPragma ($name, $dbh, \$query);
return "$name|$err" if ($err);
$query = "VACUUM";
($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, $query, "VACUUM database $dbname....");
return "$name|$err" if ($err);
@ -10992,6 +11007,32 @@ sub DbRep_resolveDevspecs {
return ($devs,$devswc);
}
######################################################################################
# Erstelle Insert SQL-Schema für Tabelle mit/ohne primary key
######################################################################################
sub DbRep_createInsertSQLscheme {
my $table = shift;
my $dbmodel = shift;
my $usepkh = shift;
my $sql;
if ($usepkh && $dbmodel eq 'MYSQL') {
$sql = "INSERT IGNORE INTO $table (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)";
}
elsif ($usepkh && $dbmodel eq 'SQLITE') {
$sql = "INSERT OR IGNORE INTO $table (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)";
}
elsif ($usepkh && $dbmodel eq 'POSTGRESQL') {
$sql = "INSERT INTO $table (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING";
}
else {
$sql = "INSERT INTO $table (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)";
}
return $sql;
}
######################################################################################
# Connect zur Datenbank herstellen
#
@ -11139,7 +11180,7 @@ return ($err, $sth, $result);
}
####################################################################################################
# einfaches Sdbh->do, return ERROR-String wenn Fehler
# einfaches Sdbh->do, return ERROR-String wenn Fehler bzw. die Anzahl der betroffenen Zeilen
####################################################################################################
sub DbRep_dbhDo {
my $name = shift;
@ -11148,19 +11189,44 @@ sub DbRep_dbhDo {
my $info = shift // "simple do statement: $sql";
my $err = q{};
my $sth;
my $rv = q{};
Log3 ($name, 4, "DbRep $name - $info");
eval{ $dbh->do($sql);
eval{ $rv = $dbh->do($sql);
1;
}
or do { $err = encode_base64($@,"");
Log3 ($name, 2, "DbRep $name - ERROR - $@");
$dbh->disconnect;
};
return $err;
return ($err, $rv);
}
####################################################################################################
# führt ein sth execute aus
# return ERROR oder die Anzahl der betroffenen Zeilen
####################################################################################################
sub DbRep_sthExecOnly {
my $name = shift;
my $sth = shift;
my $arg = shift;
my $err = q{};
my $rv = q{};
my @args = split ",", $arg;
eval{ $rv = $sth->execute(@args);
}
or do { $err = encode_base64($@,"");
Log3 ($name, 2, "DbRep $name - ERROR - $@");
};
$rv = 0 if($rv eq "0E0");
return ($err, $rv);
}
####################################################################################################
@ -12687,66 +12753,71 @@ return \%ncp;
# Funktionsergebnisse in Datenbank schreiben
####################################################################################################
sub DbRep_OutputWriteToDB {
my ($name,$device,$reading,$wrstr,$optxt) = @_;
my $name = shift;
my $device = shift;
my $reading = shift;
my $wrstr = shift;
my $optxt = shift; # Operation Kürzel
my $hash = $defs{$name};
my $dbloghash = $defs{$hash->{HELPER}{DBLOGDEVICE}};
my $dbconn = $dbloghash->{dbconn};
my $dbuser = $dbloghash->{dbuser};
my $dblogname = $dbloghash->{NAME};
my $dbmodel = $dbloghash->{MODEL};
my $DbLogType = AttrVal($dblogname, "DbLogType", "History");
my $supk = AttrVal($dblogname, "noSupportPK", 0);
my $dbpassword = $attr{"sec$dblogname"}{secret};
my $utf8 = $hash->{UTF8} // 0;
my $DbLogType = AttrVal ($dblogname, 'DbLogType', 'History');
my $supk = AttrVal ($dblogname, 'noSupportPK', 0);
$device =~ s/[^A-Za-z\/\d_\.-]/\//g;
$reading =~ s/[^A-Za-z\/\d_\.-]/\//g;
my $type = "calculated";
my $event = "calculated";
my $unit = "";
my $type = 'calculated';
my $event = 'calculated';
my $unit = qq{};
my $wrt = 0;
my $irowdone = 0;
my $ndp = AttrVal($name, "numDecimalPlaces", $dbrep_defdecplaces);
my $ndp = AttrVal ($name, 'numDecimalPlaces', $dbrep_defdecplaces);
my ($dbh,$sth_ih,$sth_uh,$sth_ic,$sth_uc,$err,$value,$date,$time,$hour,$minute,$ndate,$ntime,$rsf,$rsn,@row_array);
my ($dbh,$sth_ih,$sth_ic,$err,$value,$date,$time,$hour,$minute,$ndate,$ntime,$rsf,$rsn,@wr_arr);
my ($timestamp,$year,$mon,$mday,$t1,$corr);
if(!$dbloghash->{HELPER}{COLSET}) {
$err = "No result of \"$hash->{LASTCMD}\" to database written. Cause: column width in \"$hash->{DEF}\" isn't set";
$err = qq(No result of "$hash->{LASTCMD}" to database written. Cause: column width in "$hash->{DEF}" isn't set);
Log3 ($name, 2, "DbRep $name - ERROR - $err");
$err = encode_base64($err,"");
return ($err,$wrt,$irowdone);
}
no warnings 'uninitialized';
my $aggr = (DbRep_checktimeaggr($hash))[2];
$reading = $optxt."_".$aggr."_".AttrVal($name, "readingNameMap", $reading);
my $aggr = (DbRep_checktimeaggr ($hash))[2];
$reading = $optxt."_".$aggr."_".AttrVal ($name, 'readingNameMap', $reading);
$type = $defs{$device}{TYPE} if($defs{$device}); # $type vom Device ableiten
$type = uc($defs{$device}{TYPE}) if($defs{$device}); # $type vom Device übernehmen
if($optxt =~ /avg|sum/) {
my @arr = split("\\|", $wrstr);
my @arr = split "\\|", $wrstr;
my $ele = $#arr; # Nr des letzten Elements
my $i = 0;
for my $row (@arr) {
my @a = split("#", $row);
my @a = split "#", $row;
my $runtime_string = $a[0]; # Aggregations-Alias (nicht benötigt)
$value = defined($a[1]) ? (looks_like_number($a[1]) ? sprintf("%.${ndp}f",$a[1]) : undef) : undef; # in Version 8.40.0 geändert
$rsf = $a[2]; # Runtime String first - Datum / Zeit für DB-Speicherung
($date,$time) = split("_",$rsf);
($date,$time) = split "_", $rsf;
$time =~ s/-/:/g if($time);
$rsn = $a[3]; # Runtime String next - Datum / Zeit für DB-Speicherung
($ndate,$ntime) = split("_",$rsn);
($ndate,$ntime) = split "_", $rsn;
$ntime =~ s/-/:/g if($ntime);
if($aggr =~ /no|day|week|month|year/) {
$time = "00:00:01" if($time !~ /^(\d{2}):(\d{2}):(\d{2})$/ || $hash->{LASTCMD} =~ /\bwriteToDB(Single)*?\b/); # https://forum.fhem.de/index.php/topic,105787.msg1013920.html#msg1013920
$ntime = "23:59:59" if($ntime !~ /^(\d{2}):(\d{2}):(\d{2})$/ || $hash->{LASTCMD} =~ /\bwriteToDB(Single)*?\b/);
($year,$mon,$mday) = split("-", $ndate);
$corr = ($i != $ele) ? 86400 : 0;
($year,$mon,$mday) = split "-", $ndate;
$corr = $i != $ele ? 86400 : 0;
$t1 = fhemTimeLocal(59, 59, 23, $mday, $mon-1, $year-1900)-$corr;
($ndate,undef) = split(" ",FmtDateTime($t1));
($ndate,undef) = split " ", FmtDateTime($t1);
}
elsif ($aggr =~ /minute|hour/) {
($hour,$minute) = split ":", $time;
@ -12760,36 +12831,29 @@ sub DbRep_OutputWriteToDB {
$time = "$hour:00:01" if($time !~ /^(\d{2}):(\d{2}):(\d{2})$/ || $hash->{LASTCMD} =~ /\bwriteToDB(Single)*?\b/); # https://forum.fhem.de/index.php/topic,105787.msg1013920.html#msg1013920
$ntime = "$hour:59:59" if($ntime !~ /^(\d{2}):(\d{2}):(\d{2})$/ || $hash->{LASTCMD} =~ /\bwriteToDB(Single)*?\b/);
}
if ($ntime eq "23:59:59") {
($year,$mon,$mday) = split("-", $ndate);
$t1 = fhemTimeLocal(59, 59, 23, $mday, $mon-1, $year-1900)-86400;
($ndate,undef) = split(" ",FmtDateTime($t1));
}
}
if (defined $value) {
# Daten auf maximale Länge beschneiden (DbLog-Funktion !)
($device,$type,$event,$reading,$value,$unit) = DbLog_cutCol($dbloghash,$device,$type,$event,$reading,$value,$unit);
if($i == 0) {
push(@row_array, "$date $time|$device|$type|$event|$reading|$value|$unit") if($hash->{LASTCMD} !~ /\bwriteToDBSingle\b/);
push(@row_array, "$ndate $ntime|$device|$type|$event|$reading|$value|$unit");
push @wr_arr, "$date $time|$device|$type|$event|$reading|$value|$unit" if($hash->{LASTCMD} !~ /\bwriteToDBSingle\b/);
push @wr_arr, "$ndate $ntime|$device|$type|$event|$reading|$value|$unit";
}
else {
if ($aggr =~ /no|day|week|month|year/) {
($year,$mon,$mday) = split("-", $date);
($year,$mon,$mday) = split "-", $date;
$t1 = fhemTimeLocal(01, 00, 00, $mday, $mon-1, $year-1900);
($date,$time) = split(" ",FmtDateTime($t1));
($date,$time) = split " ", FmtDateTime($t1);
}
elsif ($aggr =~ /hour/) {
($year,$mon,$mday) = split("-", $date);
($year,$mon,$mday) = split "-", $date;
$t1 = fhemTimeLocal(01, 00, $hour, $mday, $mon-1, $year-1900);
($date,$time) = split(" ",FmtDateTime($t1));
($date,$time) = split " ", FmtDateTime($t1);
}
push(@row_array, "$date $time|$device|$type|$event|$reading|$value|$unit") if($hash->{LASTCMD} !~ /\bwriteToDBSingle\b/);
push(@row_array, "$ndate $ntime|$device|$type|$event|$reading|$value|$unit");
push @wr_arr, "$date $time|$device|$type|$event|$reading|$value|$unit" if($hash->{LASTCMD} !~ /\bwriteToDBSingle\b/);
push @wr_arr, "$ndate $ntime|$device|$type|$event|$reading|$value|$unit";
}
}
$i++;
@ -12797,14 +12861,14 @@ sub DbRep_OutputWriteToDB {
}
if($optxt =~ /min|max|diff/) {
my %rh = split("§", $wrstr);
my %rh = split "§", $wrstr;
for my $key (sort(keys(%rh))) {
my @k = split("\\|",$rh{$key});
$value = defined($k[1])?sprintf("%.${ndp}f",$k[1]):undef;
my @k = split "\\|", $rh{$key};
$value = defined($k[1]) ? sprintf("%.${ndp}f",$k[1]) : undef;
$rsf = $k[2]; # Datum / Zeit für DB-Speicherung
($date,$time) = split("_",$rsf);
($date,$time) = split "_", $rsf;
$time =~ s/-/:/g if($time);
if($time !~ /^(\d{2}):(\d{2}):(\d{2})$/) {
@ -12819,21 +12883,21 @@ sub DbRep_OutputWriteToDB {
}
}
if ($value) {
# Daten auf maximale Länge beschneiden (DbLog-Funktion !)
($device,$type,$event,$reading,$value,$unit) = DbLog_cutCol($dbloghash,$device,$type,$event,$reading,$value,$unit);
push(@row_array, "$date $time|$device|$type|$event|$reading|$value|$unit");
push @wr_arr, "$date $time|$device|$type|$event|$reading|$value|$unit";
}
}
}
if (@row_array) { # Schreibzyklus aktivieren
eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, mysql_enable_utf8 => $utf8 });};
if ($@) {
$err = $@;
Log3 ($name, 2, "DbRep $name - ERROR - $@");
$err = encode_base64($err,"");
return ($err,$wrt,$irowdone);
}
return ($err,$wrt,$irowdone) if(!@wr_arr);
#Log3 ($name, 2, "DbRep $name - data for write: \n". Dumper @wr_arr);
#return;
# Schreibzyklus
##################
($err, $dbh, my $dbmodel) = DbRep_dbConnect ($name, 0);
return ($err,$wrt,$irowdone) if ($err);
# check ob PK verwendet wird, @usepkx?Anzahl der Felder im PK:0 wenn kein PK, $pkx?Namen der Felder:none wenn kein PK
my ($usepkh,$usepkc,$pkh,$pkc);
@ -12842,117 +12906,84 @@ sub DbRep_OutputWriteToDB {
($usepkh,$usepkc,$pkh,$pkc) = DbRep_checkUsePK($hash,$dbloghash,$dbh);
}
else {
Log3 ($hash->{NAME}, 5, "DbRep $name -> Primary Key usage suppressed by attribute noSupportPK in DbLog \"$dblogname\"");
Log3 ($name, 5, "DbRep $name -> Primary Key usage suppressed by attribute noSupportPK in DbLog >$dblogname<");
}
if (lc($DbLogType) =~ m(history)) { # INSERT history mit/ohne primary key
if ($usepkh && $dbloghash->{MODEL} eq 'MYSQL') {
eval { $sth_ih = $dbh->prepare_cached("INSERT IGNORE INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
}
elsif ($usepkh && $dbloghash->{MODEL} eq 'SQLITE') {
eval { $sth_ih = $dbh->prepare_cached("INSERT OR IGNORE INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
}
elsif ($usepkh && $dbloghash->{MODEL} eq 'POSTGRESQL') {
eval { $sth_ih = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); };
}
else {
eval { $sth_ih = $dbh->prepare_cached("INSERT INTO history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
}
if ($@) {
$err = $@;
Log3 ($name, 2, "DbRep $name - ERROR - $@");
$err = encode_base64($err,"");
return ($err,$wrt,$irowdone);
}
}
my $sql = DbRep_createInsertSQLscheme ('history', $dbmodel, $usepkh);
if (lc($DbLogType) =~ m(current) ) { # INSERT current mit/ohne primary key
if ($usepkc && $hash->{MODEL} eq 'MYSQL') {
eval { $sth_ic = $dbh->prepare("INSERT IGNORE INTO current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
}
elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') {
eval { $sth_ic = $dbh->prepare("INSERT OR IGNORE INTO current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
}
elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') {
eval { $sth_ic = $dbh->prepare("INSERT INTO current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); };
}
else {
# old behavior
eval { $sth_ic = $dbh->prepare("INSERT INTO current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); };
}
if ($@) {
$err = $@;
Log3 ($name, 2, "DbRep $name - ERROR - $@");
$err = encode_base64($err,"");
return ($err,$wrt,$irowdone);
}
}
($err, $sth_ih) = DbRep_prepareOnly ($name, $dbh, $sql);
return ($err,$wrt,$irowdone) if ($err);
eval { $dbh->begin_work() if($dbh->{AutoCommit}); };
if ($@) {
Log3($name, 2, "DbRep $name - ERROR - $@");
}
$sql = DbRep_createInsertSQLscheme ('current', $dbmodel, $usepkh);
# SQL-Startzeit
my $wst = [gettimeofday];
($err, $sth_ic) = DbRep_prepareOnly ($name, $dbh, $sql);
return ($err,$wrt,$irowdone) if ($err);
$err = DbRep_beginDatabaseTransaction ($name, $dbh);
return ($err,$wrt,$irowdone) if ($err);
my $wst = [gettimeofday]; # SQL-Startzeit
my $ihs = 0;
my $uhs = 0;
for my $row (@row_array) {
my @a = split("\\|",$row);
for my $row (@wr_arr) {
my @a = split "\\|", $row;
$timestamp = $a[0];
$device = $a[1];
$type = $a[2];
$event = $a[3];
$reading = $a[4];
$value = $a[5];
$unit = $a[6];
$unit = $a[6] // q{};
eval {
# update oder insert history
if (lc($DbLogType) =~ m(history) ) {
my $rv_uh = $dbh->do("UPDATE history SET TIMESTAMP=\"$timestamp\", DEVICE=\"$device\", READING=\"$reading\", TYPE=\"$type\", EVENT=\"$event\", VALUE=\"$value\", UNIT=\"$unit\" WHERE TIMESTAMP=\"$timestamp\" AND DEVICE=\"$device\" AND READING=\"$reading\"");
my ($rv_uh, $rv_ih);
($err, $rv_uh) = DbRep_dbhDo ($name, $dbh, qq(UPDATE history SET TIMESTAMP="$timestamp", DEVICE="$device", READING="$reading", TYPE="$type", EVENT="$event", VALUE="$value", UNIT="$unit" WHERE TIMESTAMP="$timestamp" AND DEVICE="$device" AND READING="$reading";));
return ($err,$wrt,$irowdone) if ($err);
$uhs += $rv_uh if($rv_uh);
Log3 ($hash->{NAME}, 4, "DbRep $name - UPDATE history: $row, RESULT: $rv_uh");
Log3 ($name, 4, "DbRep $name - UPDATE history: $row, RESULT: $rv_uh");
if ($rv_uh == 0) {
my $rv_ih = $sth_ih->execute($timestamp,$device,$type,$event,$reading,$value,$unit);
($err, $rv_ih) = DbRep_sthExecOnly ($name, $sth_ih, "$timestamp,$device,$type,$event,$reading,$value,$unit");
if ($err) {
$dbh->disconnect;
return ($err,$wrt,$irowdone);
}
$ihs += $rv_ih if($rv_ih);
Log3 $hash->{NAME}, 4, "DbRep $name - INSERT history: $row, RESULT: $rv_ih";
Log3 ($name, 4, "DbRep $name - INSERT history: $row, RESULT: $rv_ih");
}
}
# update oder insert current
if (lc($DbLogType) =~ m(current) ) {
my $rv_uc = $dbh->do("UPDATE current SET TIMESTAMP=\"$timestamp\", DEVICE=\"$device\", READING=\"$reading\", TYPE=\"$type\", EVENT=\"$event\", VALUE=\"$value\", UNIT=\"$unit\" WHERE DEVICE=\"$device\" AND READING=\"$reading\"");
my $rv_uc;
($err, $rv_uc) = DbRep_dbhDo ($name, $dbh, qq(UPDATE current SET TIMESTAMP="$timestamp", DEVICE="$device", READING="$reading", TYPE="$type", EVENT="$event", VALUE="$value", UNIT="$unit" WHERE DEVICE="$device" AND READING="$reading";));
return ($err,$wrt,$irowdone) if ($err);
if ($rv_uc == 0) {
$sth_ic->execute($timestamp,$device,$type,$event,$reading,$value,$unit);
$err = DbRep_sthExecOnly ($name, $sth_ic, "$timestamp,$device,$type,$event,$reading,$value,$unit");
}
}
};
if ($@) {
$err = $@;
Log3 ($name, 2, "DbRep $name - ERROR - $@");
$dbh->rollback;
$dbh->disconnect;
$err = encode_base64($err,"");
return ($err,$wrt,0);
}
}
eval {$dbh->commit() if(!$dbh->{AutoCommit});};
$err = DbRep_commitOnly ($name, $dbh);
return ($err,$wrt,$irowdone) if ($err);
$dbh->disconnect;
Log3 ($hash->{NAME}, 3, "DbRep $name - number of lines updated in \"$dblogname\": $uhs");
Log3 ($hash->{NAME}, 3, "DbRep $name - number of lines inserted into \"$dblogname\": $ihs");
Log3 ($name, 3, "DbRep $name - number of lines updated in >$dblogname<: $uhs");
Log3 ($name, 3, "DbRep $name - number of lines inserted into >$dblogname<: $ihs");
$irowdone = $ihs + $uhs;
# SQL-Laufzeit ermitteln
$wrt = tv_interval($wst);
}
$wrt = tv_interval($wst); # SQL-Laufzeit ermitteln
return ($err,$wrt,$irowdone);
}
@ -13410,12 +13441,12 @@ sub DbRep_setVersionInfo {
if($modules{$type}{META}{x_prereqs_src} && !$hash->{HELPER}{MODMETAABSENT}) {
# META-Daten sind vorhanden
$modules{$type}{META}{version} = "v".$v; # Version aus META.json überschreiben, Anzeige mit {Dumper $modules{SMAPortal}{META}}
if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbRep.pm 26940 2023-01-01 20:22:51Z DS_Starter $ im Kopf komplett! vorhanden )
if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbRep.pm 27031 2023-01-11 23:20:18Z DS_Starter $ im Kopf komplett! vorhanden )
$modules{$type}{META}{x_version} =~ s/1.1.1/$v/g;
} else {
$modules{$type}{META}{x_version} = $v;
}
return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbRep.pm 26940 2023-01-01 20:22:51Z DS_Starter $ im Kopf komplett! vorhanden )
return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbRep.pm 27031 2023-01-11 23:20:18Z DS_Starter $ im Kopf komplett! vorhanden )
if(__PACKAGE__ eq "FHEM::$type" || __PACKAGE__ eq $type) {
# es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen
# mit {<Modul>->VERSION()} im FHEMWEB kann Modulversion abgefragt werden
@ -14222,8 +14253,9 @@ return;
</li><br>
<li><b> dumpMySQL [clientSide | serverSide]</b>
- creates a dump of the connected MySQL database. <br>
<li><b> dumpMySQL [clientSide | serverSide]</b> <br><br>
Creates a dump of the connected MySQL database. <br>
Depending from selected option the dump will be created on Client- or on Server-Side. <br>
The variants differs each other concerning the executing system, the creating location, the usage of
attributes, the function result and the needed hardware ressources. <br>
@ -14234,7 +14266,7 @@ return;
<ul>
<b><u>Option clientSide</u></b> <br>
The dump will be created by client (FHEM-Server) and will be saved in FHEM log-directory by
The dump will be created by client (FHEM-Server) and will be saved in FHEM log-directory ((typical /opt/fhem/log/)) by
default.
The target directory can be set by attribute <a href="#DbRep-attr-dumpDirLocal">dumpDirLocal</a> and has to be
writable by the FHEM process. <br>
@ -14385,9 +14417,10 @@ return;
</ul>
<br>
<br>
</ul>
</li><br>
</ul>
</li>
<br>
<li><b> dumpSQLite </b> - creates a dump of the connected SQLite database. <br>
This function uses the SQLite Online Backup API and allow to create a consistent backup of the
@ -15249,7 +15282,7 @@ return;
<br>
<br>
The relevant attributes to control this function are: <br><br>
The following attributes are relevant for this function: <br><br>
<ul>
<table>
@ -15263,20 +15296,35 @@ return;
</li>
<li><b> vacuum </b> - optimize tables in the connected database (SQLite, PostgreSQL). <br>
Before and after an optimization it is possible to execute a FHEM command.
(please see attributes <a href="#DbRep-attr-executeBeforeProc">executeBeforeProc</a>, <a href="#DbRep-attr-executeAfterProc">executeAfterProc</a>)
<br><br>
<a id="DbRep-set-vacuum"></a>
<li><b> vacuum </b> <br><br>
Optimizes the tables in the connected database (SQLite, PostgreSQL). <br>
Especially for SQLite databases it is strongly recommended to temporarily close the connection of the relevant DbLog
device to the database (see DbLog reopen command).
<br>
<br>
The following attributes are relevant for this function: <br><br>
<ul>
<b>Note:</b> <br>
Even though the function itself is designed non-blocking, make sure the assigned DbLog-device
is operating in asynchronous mode to avoid FHEM from blocking. <br><br>
<table>
<colgroup> <col width=5%> <col width=95%> </colgroup>
<tr><td> <b>executeBeforeProc</b> </td><td>: execution of FHEM command (or Perl-routine) before operation </td></tr>
<tr><td> <b>executeAfterProc</b> </td><td>: execution of FHEM command (or Perl-routine) after operation </td></tr>
</table>
</ul>
<br>
<br>
<b>Hinweis:</b> <br>
When the vacuum command is executed, the PRAGMA <b>auto_vacuum = FULL</b> is automatically applied to SQLite databases.
</li>
</ul><br>
<br>
<br>
</ul></ul>
</ul>
</ul>
<b>For all evaluation variants (except sqlCmd,deviceRename,readingRename) applies: </b> <br>
In addition to the needed reading the device can be complemented to restrict the datasets for reporting / function.
@ -15922,7 +15970,8 @@ sub bdump {
new operation starts </li> <br>
<a id="DbRep-attr-seqDoubletsVariance"></a>
<li><b>seqDoubletsVariance &lt;positive variance [negative variance] [EDGE=negative|positive]&gt; </b> <br>
<li><b>seqDoubletsVariance &lt;positive variance [negative variance] [EDGE=negative|positive]&gt; </b> <br><br>
Accepted variance for the command "set &lt;name&gt; delSeqDoublets". <br>
The value of this attribute describes the variance up to consecutive numeric values (VALUE) of
datasets are handled as identical. If only one numeric value is declared, it is used as
@ -15943,7 +15992,8 @@ sub bdump {
<code>attr &lt;name&gt; seqDoubletsVariance 3.0 2.0 </code> <br>
<code>attr &lt;name&gt; seqDoubletsVariance 1.5 EDGE=negative </code> <br>
</ul>
<br><br>
<br>
<br>
</li>
<a id="DbRep-attr-showproctime"></a>
@ -17087,8 +17137,9 @@ return;
</li> <br>
<li><b> dumpMySQL [clientSide | serverSide]</b>
- erstellt einen Dump der angeschlossenen MySQL-Datenbank. <br>
<li><b> dumpMySQL [clientSide | serverSide]</b> <br><br>
Erstellt einen Dump der angeschlossenen MySQL-Datenbank. <br>
Abhängig von der ausgewählten Option wird der Dump auf der Client- bzw. Serverseite erstellt. <br>
Die Varianten unterscheiden sich hinsichtlich des ausführenden Systems, des Erstellungsortes, der
Attributverwendung, des erzielten Ergebnisses und der benötigten Hardwareressourcen. <br>
@ -17100,7 +17151,7 @@ return;
<ul>
<b><u>Option clientSide</u></b> <br>
Der Dump wird durch den Client (FHEM-Rechner) erstellt und per default im log-Verzeichnis des Clients
gespeichert.
(typisch /opt/fhem/log/) gespeichert.
Das Zielverzeichnis kann mit dem Attribut <a href="#DbRep-attr-dumpDirLocal">dumpDirLocal</a> verändert werden und muß auf
dem Client durch FHEM beschreibbar sein. <br>
Vor dem Dump kann eine Tabellenoptimierung (Attribut "optimizeTablesBeforeDump") oder ein FHEM-Kommando
@ -17252,7 +17303,8 @@ return;
<br>
</ul>
</li><br>
</li>
<br>
<li><b> dumpSQLite </b> - erstellt einen Dump der angeschlossenen SQLite-Datenbank. <br>
Diese Funktion nutzt die SQLite Online Backup API und ermöglicht es konsistente Backups der SQLite-DB
@ -18150,21 +18202,35 @@ return;
</li>
<a id="DbRep-set-vacuum"></a>
<li><b> vacuum </b> - optimiert die Tabellen in der angeschlossenen Datenbank (SQLite, PostgreSQL). <br>
Vor und nach der Optimierung kann ein FHEM-Kommando ausgeführt werden.
(siehe Attribute <a href="#DbRep-attr-executeBeforeProc">executeBeforeProc</a>,
<a href="#DbRep-attr-executeAfterProc">executeAfterProc</a>)
<br><br>
<li><b> vacuum </b> <br><br>
Optimiert die Tabellen in der angeschlossenen Datenbank (SQLite, PostgreSQL). <br>
Insbesondere für SQLite Datenbanken ist unbedingt empfehlenswert die Verbindung des relevanten DbLog-Devices zur
Datenbank vorübergehend zu schließen (siehe DbLog reopen Kommando).
<br>
<br>
Für diese Funktion sind folgende Attribute relevant: <br><br>
<ul>
<table>
<colgroup> <col width=5%> <col width=95%> </colgroup>
<tr><td> <b>executeBeforeProc</b> </td><td>: ausführen FHEM Kommando (oder Perl-Routine) vor Start des Befehls </td></tr>
<tr><td> <b>executeAfterProc</b> </td><td>: ausführen FHEM Kommando (oder Perl-Routine) nach Ende des Befehls </td></tr>
</table>
</ul>
<br>
<br>
<b>Hinweis:</b> <br>
Obwohl die Funktion selbst non-blocking ausgelegt ist, muß das zugeordnete DbLog-Device
im asynchronen Modus betrieben werden um ein Blockieren von FHEM zu vermeiden. <br><br>
Bei der Ausführung des vacuum Kommandos wird bei SQLite Datenbanken automatisch das PRAGMA <b>auto_vacuum = FULL</b>
angewendet.
</li>
</ul><br>
<br>
<br>
</ul></ul>
</ul>
</ul>
<b>Für alle Auswertungsvarianten (Ausnahme sqlCmd,deviceRename,readingRename) gilt: </b> <br>
Zusätzlich zu dem auszuwertenden Reading kann das Device mit angegeben werden um das Reporting nach diesen Kriterien einzuschränken.
@ -18534,6 +18600,7 @@ sub dbval {
<a id="DbRep-attr-dumpDirLocal"></a>
<li><b>dumpDirLocal </b> <br><br>
<ul>
Zielverzeichnis für die Erstellung von Dumps mit "dumpMySQL clientSide" oder "dumpSQLite". <br>
@ -18819,7 +18886,8 @@ sub bdump {
</li> <br>
<a id="DbRep-attr-seqDoubletsVariance"></a>
<li><b>seqDoubletsVariance &lt;positive Abweichung [negative Abweichung] [EDGE=negative|positive]&gt; </b> <br>
<li><b>seqDoubletsVariance &lt;positive Abweichung [negative Abweichung] [EDGE=negative|positive]&gt; </b> <br><br>
Akzeptierte Abweichung für das Kommando "set &lt;name&gt; delSeqDoublets". <br>
Der Wert des Attributs beschreibt die Abweichung bis zu der aufeinanderfolgende numerische
Werte (VALUE) von Datensätzen als gleich angesehen werden sollen.
@ -18841,7 +18909,8 @@ sub bdump {
<code>attr &lt;name&gt; seqDoubletsVariance 3.0 2.0 </code> <br>
<code>attr &lt;name&gt; seqDoubletsVariance 1.5 EDGE=negative </code> <br>
</ul>
<br><br>
<br>
<br>
</li>
<a id="DbRep-attr-showproctime"></a>