configDB.pm 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171
  1. # $Id: configDB.pm 13382 2017-02-10 20:48:14Z betateilchen $
  2. =for comment
  3. ##############################################################################
  4. #
  5. # configDB.pm
  6. #
  7. # A fhem library to enable configuration from sql database
  8. # instead of plain text file, e.g. fhem.cfg
  9. #
  10. # READ COMMANDREF DOCUMENTATION FOR CORRECT USE!
  11. #
  12. # Copyright: betateilchen ®
  13. #
  14. # This file is part of fhem.
  15. #
  16. # Fhem is free software: you can redistribute it and/or modify
  17. # it under the terms of the GNU General Public License as published by
  18. # the Free Software Foundation, either version 2 of the License, or
  19. # (at your option) any later version.
  20. #
  21. # Fhem is distributed in the hope that it will be useful,
  22. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. # GNU General Public License for more details.
  25. #
  26. # You should have received a copy of the GNU General Public License
  27. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  28. #
  29. ##############################################################################
  30. #
  31. # ChangeLog
  32. #
  33. # 2014-03-01 - SVN 5080 - initial release of interface inside fhem.pl
  34. # - initial release of configDB.pm
  35. #
  36. # 2014-03-02 - added template files for sqlite in contrib/configDB
  37. # - updated commandref (EN) documentation
  38. # - added commandref (DE) documentation
  39. #
  40. # 2014-03-03 - changed performance optimized by using version uuid table
  41. # - updated commandref docu for migration
  42. # - added cfgDB_svnId for fhem.pl CommandVersion
  43. # - added cfgDB_List to show device info from database
  44. # - updated commandref docu for cfgDB_List
  45. #
  46. # 2014-03-06 - added cfgDB_Diff to compare device in two versions
  47. #
  48. # 2014-03-07 - changed optimized cfgDB_Diff
  49. # restructured libraray internally
  50. # improved source code documentation
  51. #
  52. # 2014-03-20 - added export/import
  53. # 2014-04-01 - removed export/import due to not working properly
  54. #
  55. # 2014-04-03 - fixed global attributes not read from version 0
  56. #
  57. # 2014-04-18 - added commands fileimport, fileexport
  58. # 2014-04-19 - added commands filelist, filedelete
  59. # interface cfgDB_Readfile for interaction
  60. # with other modules
  61. #
  62. # 2014-04-21 - added interface functions for FHEMWEB and fhem.pl
  63. # to show files in "Edit files" and use them
  64. # with CommandReload() mechanism
  65. #
  66. # modified _cfgDB_Info to show number of files in db
  67. #
  68. # 2014-04-23 - added command fileshow, filemove
  69. #
  70. # 2014-04-26 - added migration to generic file handling
  71. # fixed problem on migration of multiline DEFs
  72. #
  73. # 2014-04-27 - added new functions for binfile handling
  74. #
  75. # 2014-05-11 - removed command binfileimport
  76. # changed store all files as binary
  77. # added _cfgDB_Move to move all files from text
  78. # to binary filesave on first load of configDB
  79. #
  80. # 2014-05-12 - added sorted write & read for config data
  81. #
  82. # 2014-05-15 - fixed handling of multiline defs
  83. #
  84. # 2014-05-20 - removed no longer needed functions for file handling
  85. # changed code improvement; use strict; use warnings;
  86. #
  87. # 2014-08-22 - added automatic fileimport during migration
  88. #
  89. # 2014-09-30 - added support for device based userattr
  90. #
  91. # 2015-01-12 - changed use fhem function createUniqueId()
  92. # instead of database calls
  93. #
  94. # 2015-01-15 - changed remove 99_Utils.pm from filelist
  95. #
  96. # 2015-01-17 - added configdb diff all current
  97. # shows diff table between version 0
  98. # and currently running version (in memory)
  99. #
  100. # 2015-01-23 - changed attribute handling for internal configDB attrs
  101. #
  102. # 2015-01-23 - added FileRead() caching - experimental
  103. #
  104. # 2015-10-14 - changed search conditions use ESCAPE, forum #42190
  105. #
  106. # 2016-03-19 - changed use modpath, forum #51036
  107. #
  108. # 2016-03-26 - added log entry for search (verbose=5)
  109. #
  110. # 2016-05-22 - added configdb dump (for sqlite)
  111. #
  112. # 2016-05-28 - added configdb dump (for mysql)
  113. #
  114. # 2016-05-29 - changed improve support for postgresql (tnx to Matze)
  115. # added configdb dump (for postgresql)
  116. #
  117. # 2016-07-03 - added support for multiple hosts (experimental)
  118. # 2016-07-04 - fixed improve config file read
  119. # 2016-07-07 - bugfix select configuration
  120. #
  121. ##############################################################################
  122. =cut
  123. use strict;
  124. use warnings;
  125. use Text::Diff;
  126. use DBI;
  127. use Sys::Hostname;
  128. #use Data::Dumper;
  129. ##################################################
  130. # Forward declarations for functions in fhem.pl
  131. #
  132. sub AnalyzeCommandChain($$;$);
  133. sub Log($$);
  134. sub Log3($$$);
  135. sub createUniqueId();
  136. ##################################################
  137. # Forward declarations inside this library
  138. #
  139. sub cfgDB_AttrRead($);
  140. sub cfgDB_Init();
  141. sub cfgDB_FileRead($);
  142. sub cfgDB_FileUpdate($);
  143. sub cfgDB_Fileversion($$);
  144. sub cfgDB_FileWrite($@);
  145. sub cfgDB_FW_fileList($$@);
  146. sub cfgDB_Read99();
  147. sub cfgDB_ReadAll($);
  148. sub cfgDB_SaveCfg(;$);
  149. sub cfgDB_SaveState();
  150. sub cfgDB_svnId();
  151. sub _cfgDB_binFileimport($$;$);
  152. sub _cfgDB_Connect();
  153. sub _cfgDB_DeleteTemp();
  154. sub _cfgDB_Diff($$);
  155. sub __cfgDB_Diff($$$$);
  156. sub _cfgDB_InsertLine($$$$);
  157. sub _cfgDB_Execute($@);
  158. sub _cfgDB_Filedelete($);
  159. sub _cfgDB_Fileexport($;$);
  160. sub _cfgDB_Filelist(;$);
  161. sub _cfgDB_Info();
  162. sub _cfgDB_typeInfo();
  163. sub _cfgDB_Migrate();
  164. sub _cfgDB_ReadCfg(@);
  165. sub _cfgDB_ReadState(@);
  166. sub _cfgDB_Recover($);
  167. sub _cfgDB_Reorg(;$$);
  168. sub _cfgDB_Rotate($$);
  169. sub _cfgDB_Search($$;$);
  170. sub _cfgDB_Uuid();
  171. ##################################################
  172. # Read configuration file for DB connection
  173. #
  174. if(!open(CONFIG, 'configDB.conf')) {
  175. Log3('configDB', 1, 'Cannot open database configuration file configDB.conf');
  176. return 0;
  177. }
  178. #my @config=<CONFIG>;
  179. #close(CONFIG);
  180. my @config;
  181. while (<CONFIG>){
  182. my $line = $_;
  183. $line =~ s/^\s+|\s+$//g; # remove whitespaces etc.
  184. $line =~ s/;$/;;/; # duplicate ; at end-of-line
  185. push (@config,$line) if($line !~ m/^#/ && length($line) > 0);
  186. }
  187. close CONFIG;
  188. use vars qw(%configDB);
  189. my %dbconfig;
  190. my $configs = join("",@config);
  191. my @configs = split(/;;/,$configs);
  192. my $count = @configs;
  193. my $fhemhost = hostname;
  194. if ($count > 1) {
  195. foreach my $c (@configs) {
  196. next unless $c =~ m/^%dbconfig.*/;
  197. $dbconfig{fhemhost} = "";
  198. eval $c;
  199. last if ($dbconfig{fhemhost} eq $fhemhost);
  200. }
  201. eval $configs[0] if ($dbconfig{fhemhost} eq "");
  202. } else {
  203. eval $configs[0];
  204. }
  205. my $cfgDB_dbconn = $dbconfig{connection};
  206. my $cfgDB_dbuser = $dbconfig{user};
  207. my $cfgDB_dbpass = $dbconfig{password};
  208. my $cfgDB_dbtype;
  209. my $cfgDB_filename;
  210. %dbconfig = ();
  211. @config = ();
  212. $configs = undef;
  213. $count = undef;
  214. if($cfgDB_dbconn =~ m/pg:/i) {
  215. $cfgDB_dbtype ="POSTGRESQL";
  216. } elsif ($cfgDB_dbconn =~ m/mysql:/i) {
  217. $cfgDB_dbtype = "MYSQL";
  218. } elsif ($cfgDB_dbconn =~ m/sqlite:/i) {
  219. $cfgDB_dbtype = "SQLITE";
  220. (undef,$cfgDB_filename) = split(/=/,$cfgDB_dbconn);
  221. } else {
  222. $cfgDB_dbtype = "unknown";
  223. }
  224. $configDB{attr}{nostate} = 1 if($ENV{'cfgDB_nostate'});
  225. $configDB{attr}{rescue} = 1 if($ENV{'cfgDB_rescue'});
  226. ##################################################
  227. # Basic functions needed for DB configuration
  228. # directly called from fhem.pl
  229. #
  230. # initialize database, create tables if necessary
  231. sub cfgDB_Init() {
  232. ##################################################
  233. # Create non-existing database tables
  234. # Create default config entries if necessary
  235. #
  236. my $fhem_dbh = _cfgDB_Connect;
  237. # create TABLE fhemversions ifnonexistent
  238. $fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemversions(VERSION INT, VERSIONUUID CHAR(50))");
  239. # create TABLE fhemconfig if nonexistent
  240. $fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemconfig(COMMAND VARCHAR(32), DEVICE VARCHAR(64), P1 VARCHAR(50), P2 TEXT, VERSION INT, VERSIONUUID CHAR(50))");
  241. # check TABLE fhemconfig already populated
  242. my $count = $fhem_dbh->selectrow_array('SELECT count(*) FROM fhemconfig');
  243. if($count < 1) {
  244. # insert default entries to get fhem running
  245. $fhem_dbh->commit();
  246. my $uuid = _cfgDB_Uuid;
  247. $fhem_dbh->do("INSERT INTO fhemversions values (0, '$uuid')");
  248. _cfgDB_InsertLine($fhem_dbh, $uuid, '#created by cfgDB_Init',0);
  249. _cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global logdir ./log',1);
  250. _cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global logfile %L/fhem-%Y-%m-%d.log',1);
  251. _cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global modpath .',2);
  252. _cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global userattr devStateIcon devStateStyle icon sortby webCmd',3);
  253. _cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global verbose 3',4);
  254. _cfgDB_InsertLine($fhem_dbh, $uuid, 'define telnetPort telnet 7072 global',5);
  255. _cfgDB_InsertLine($fhem_dbh, $uuid, 'define web FHEMWEB 8083 global',6);
  256. _cfgDB_InsertLine($fhem_dbh, $uuid, 'define Logfile FileLog %L/fhem-%Y-%m-%d.log fakelog',7);
  257. }
  258. # create TABLE fhemstate if nonexistent
  259. $fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemstate(stateString TEXT)");
  260. # create TABLE fhembinfilesave if nonexistent
  261. if($cfgDB_dbtype eq "MYSQL") {
  262. $fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhembinfilesave(filename TEXT, content MEDIUMBLOB)");
  263. } elsif ($cfgDB_dbtype eq "POSTGRESQL") {
  264. $fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhembinfilesave(filename TEXT, content bytea)");
  265. } else {
  266. $fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhembinfilesave(filename TEXT, content BLOB)");
  267. }
  268. # close database connection
  269. $fhem_dbh->commit();
  270. $fhem_dbh->disconnect();
  271. return;
  272. }
  273. # read attributes
  274. sub cfgDB_AttrRead($) {
  275. my ($readSpec) = @_;
  276. my ($row, $sql, @line, @rets);
  277. my $fhem_dbh = _cfgDB_Connect;
  278. my $uuid = $fhem_dbh->selectrow_array('SELECT versionuuid FROM fhemversions WHERE version = 0');
  279. $sql = "SELECT * FROM fhemconfig WHERE COMMAND = 'attr' AND DEVICE = '$readSpec' AND VERSIONUUID = '$uuid'";
  280. $sql = "SELECT * FROM fhemconfig WHERE COMMAND = 'attr' AND (DEVICE = 'global' OR DEVICE = 'configdb') and VERSIONUUID = '$uuid'"
  281. if($readSpec eq 'global');
  282. my $sth = $fhem_dbh->prepare( $sql );
  283. $sth->execute();
  284. while (@line = $sth->fetchrow_array()) {
  285. if($line[1] eq 'configdb') {
  286. $configDB{attr}{$line[2]} = $line[3];
  287. # $attr{configdb}{$line[2]} = $line[3];
  288. } else {
  289. push @rets, "attr $line[1] $line[2] $line[3]";
  290. }
  291. }
  292. $fhem_dbh->disconnect();
  293. return @rets;
  294. }
  295. # generic file functions called from fhem.pl
  296. sub cfgDB_FileRead($) {
  297. my ($filename) = @_;
  298. if ($configDB{cache}{$filename} && $configDB{attr}{useCache}) {
  299. Log3(undef, 4, "configDB serving from cache: $filename");
  300. return (undef,split(/\n/,$configDB{cache}{$filename}));
  301. }
  302. Log3(undef, 4, "configDB reading file: $filename");
  303. my ($err, @ret, $counter);
  304. my $fhem_dbh = _cfgDB_Connect;
  305. my $sth = $fhem_dbh->prepare( "SELECT content FROM fhembinfilesave WHERE filename LIKE '$filename'" );
  306. $sth->execute();
  307. my $blobContent = $sth->fetchrow_array();
  308. $sth->finish();
  309. $fhem_dbh->disconnect();
  310. $counter = length($blobContent);
  311. if($counter) {
  312. if ($configDB{attr}{useCache}) {
  313. Log3(undef,4,"configDB caching: $filename");
  314. $configDB{cache}{$filename} = $blobContent;
  315. }
  316. @ret = split(/\n/,$blobContent);
  317. $err = "";
  318. } else {
  319. @ret = undef;
  320. $err = "Error on reading $filename from database!";
  321. }
  322. return ($err, @ret);
  323. }
  324. sub cfgDB_FileWrite($@) {
  325. my ($filename,@content) = @_;
  326. if ($configDB{attr}{useCache}) {
  327. Log3(undef,4,"configDB delete from cache: $filename");
  328. $configDB{cache}{$filename} = undef;
  329. }
  330. Log3(undef, 4, "configDB writing file: $filename");
  331. my $fhem_dbh = _cfgDB_Connect;
  332. $fhem_dbh->do("delete from fhembinfilesave where filename = '$filename'");
  333. my $sth = $fhem_dbh->prepare('INSERT INTO fhembinfilesave values (?, ?)');
  334. $sth->execute($filename,join("\n", @content));
  335. $sth->finish();
  336. $fhem_dbh->commit();
  337. $fhem_dbh->disconnect();
  338. return;
  339. }
  340. sub cfgDB_FileUpdate($) {
  341. my ($filename) = @_;
  342. my $fhem_dbh = _cfgDB_Connect;
  343. my $id = $fhem_dbh->selectrow_array("SELECT filename from fhembinfilesave where filename = '$filename'");
  344. $fhem_dbh->disconnect();
  345. if($id) {
  346. my $filesize = -s $filename;
  347. _cfgDB_binFileimport($filename,$filesize,1) if ($id) ;
  348. Log(5, "file $filename updated in configDB");
  349. }
  350. return;
  351. }
  352. # read and execute fhemconfig and fhemstate
  353. sub cfgDB_ReadAll($) {
  354. my ($cl) = @_;
  355. my ($ret, @dbconfig);
  356. if (defined($configDB{attr}{rescue}) && ($configDB{attr}{rescue} == 1)) {
  357. Log (0, 'configDB starting in rescue mode!');
  358. push (@dbconfig, 'attr global modpath .');
  359. push (@dbconfig, 'attr global verbose 3');
  360. push (@dbconfig, 'define telnetPort telnet 7072 global');
  361. push (@dbconfig, 'define WEB FHEMWEB 8083 global');
  362. push (@dbconfig, 'define Logfile FileLog ./log/fhem-%Y-%m-%d.log fakelog');
  363. } else {
  364. # add Config Rows to commandfile
  365. @dbconfig = _cfgDB_ReadCfg(@dbconfig);
  366. # add State Rows to commandfile
  367. @dbconfig = _cfgDB_ReadState(@dbconfig) unless $configDB{attr}{nostate};
  368. }
  369. # AnalyzeCommandChain for all entries
  370. $ret = _cfgDB_Execute($cl, @dbconfig);
  371. return $ret if($ret);
  372. return undef;
  373. }
  374. # save running configuration to version 0
  375. sub cfgDB_SaveCfg(;$) {
  376. my ($internal) = shift;
  377. $internal = defined($internal) ? $internal : 0;
  378. my (%devByNr, @rowList, %comments, $t, $out);
  379. map { $devByNr{$defs{$_}{NR}} = $_ } keys %defs;
  380. for(my $i = 0; $i < $devcount; $i++) {
  381. my ($h, $d);
  382. if($comments{$i}) {
  383. $h = $comments{$i};
  384. } else {
  385. $d = $devByNr{$i};
  386. next if(!defined($d) ||
  387. $defs{$d}{TEMPORARY} || # e.g. WEBPGM connections
  388. $defs{$d}{VOLATILE}); # e.g at, will be saved to the statefile
  389. $h = $defs{$d};
  390. }
  391. if(!defined($d)) {
  392. push @rowList, $h->{TEXT};
  393. next;
  394. }
  395. if($d ne "global") {
  396. my $def = $defs{$d}{DEF};
  397. if(defined($def)) {
  398. $def =~ s/;/;;/g;
  399. $def =~ s/\n/\\\n/g;
  400. } else {
  401. $def = "";
  402. }
  403. push @rowList, "define $d $defs{$d}{TYPE} $def";
  404. }
  405. foreach my $a (sort {
  406. return -1 if($a eq "userattr"); # userattr must be first
  407. return 1 if($b eq "userattr");
  408. return $a cmp $b;
  409. } keys %{$attr{$d}}) {
  410. next if($d eq "global" &&
  411. ($a eq "configfile" || $a eq "version"));
  412. my $val = $attr{$d}{$a};
  413. $val =~ s/;/;;/g;
  414. push @rowList, "attr $d $a $val";
  415. }
  416. }
  417. foreach my $a (sort keys %{$configDB{attr}}) {
  418. # foreach my $a (sort keys %{$attr{configdb}}) {
  419. my $val = $configDB{attr}{$a};
  420. next unless $val;
  421. $val =~ s/;/;;/g;
  422. push @rowList, "attr configdb $a $val";
  423. }
  424. # Insert @rowList into database table
  425. my $fhem_dbh = _cfgDB_Connect;
  426. my $uuid = _cfgDB_Rotate($fhem_dbh,$internal);
  427. $t = localtime;
  428. $out = "#created $t";
  429. push @rowList, $out;
  430. my $counter = 0;
  431. foreach (@rowList) {
  432. _cfgDB_InsertLine($fhem_dbh, $uuid, $_, $counter);
  433. $counter++;
  434. }
  435. $fhem_dbh->commit();
  436. $fhem_dbh->disconnect();
  437. my $maxVersions = $configDB{attr}{maxversions};
  438. $maxVersions = ($maxVersions) ? $maxVersions : 0;
  439. _cfgDB_Reorg($maxVersions,1) if($maxVersions && $internal != -1);
  440. return 'configDB saved.';
  441. }
  442. # save statefile
  443. sub cfgDB_SaveState() {
  444. my ($out,$val,$r,$rd,$t,@rowList);
  445. $t = localtime;
  446. $out = "#$t";
  447. push @rowList, $out;
  448. foreach my $d (sort keys %defs) {
  449. next if($defs{$d}{TEMPORARY});
  450. if($defs{$d}{VOLATILE}) {
  451. $out = "define $d $defs{$d}{TYPE} $defs{$d}{DEF}";
  452. push @rowList, $out;
  453. }
  454. $val = $defs{$d}{STATE};
  455. if(defined($val) &&
  456. $val ne "unknown" &&
  457. $val ne "Initialized" &&
  458. $val ne "???") {
  459. $val =~ s/;/;;/g;
  460. $val =~ s/\n/\\\n/g;
  461. $out = "setstate $d $val";
  462. push @rowList, $out;
  463. }
  464. $r = $defs{$d}{READINGS};
  465. if($r) {
  466. foreach my $c (sort keys %{$r}) {
  467. $rd = $r->{$c};
  468. if(!defined($rd->{TIME})) {
  469. Log3(undef, 4, "WriteStatefile $d $c: Missing TIME, using current time");
  470. $rd->{TIME} = TimeNow();
  471. }
  472. if(!defined($rd->{VAL})) {
  473. Log3(undef, 4, "WriteStatefile $d $c: Missing VAL, setting it to 0");
  474. $rd->{VAL} = 0;
  475. }
  476. $val = $rd->{VAL};
  477. $val =~ s/;/;;/g;
  478. $val =~ s/\n/\\\n/g;
  479. $out = "setstate $d $rd->{TIME} $c $val";
  480. push @rowList, $out;
  481. }
  482. }
  483. }
  484. my $fhem_dbh = _cfgDB_Connect;
  485. $fhem_dbh->do("DELETE FROM fhemstate");
  486. my $sth = $fhem_dbh->prepare('INSERT INTO fhemstate values ( ? )');
  487. foreach (@rowList) { $sth->execute( $_ ); }
  488. $fhem_dbh->commit();
  489. $fhem_dbh->disconnect();
  490. return;
  491. }
  492. # import existing files during migration
  493. sub cfgDB_MigrationImport() {
  494. my ($ret, $filename, @files, @def);
  495. my $modpath = AttrVal("global","modpath",".");
  496. # find eventTypes file
  497. $filename = '';
  498. @def = '';
  499. @def = _cfgDB_findDef('TYPE=eventTypes');
  500. foreach $filename (@def) {
  501. next unless $filename;
  502. push @files, $filename;
  503. }
  504. # import templateDB.gplot
  505. $filename = "$modpath/www/gplot/template.gplot";
  506. push @files, $filename;
  507. $filename = "$modpath/www/gplot/templateDB.gplot";
  508. push @files, $filename;
  509. # import template.layout
  510. $filename = "$modpath/FHEM/template.layout";
  511. push @files, $filename;
  512. # find used gplot files
  513. $filename ='';
  514. @def = '';
  515. @def = _cfgDB_findDef('TYPE=SVG','GPLOTFILE');
  516. foreach $filename (@def) {
  517. next unless $filename;
  518. push @files, "$modpath/www/gplot/".$filename.".gplot";
  519. }
  520. # find DbLog configs
  521. $filename ='';
  522. @def = '';
  523. @def = _cfgDB_findDef('TYPE=DbLog','CONFIGURATION');
  524. foreach $filename (@def) {
  525. next unless $filename;
  526. push @files, $filename;
  527. }
  528. # find RSS layouts
  529. $filename ='';
  530. @def = '';
  531. @def = _cfgDB_findDef('TYPE=RSS','LAYOUTFILE');
  532. foreach $filename (@def) {
  533. next unless $filename;
  534. push @files, $filename;
  535. }
  536. # find InfoPanel layouts
  537. $filename ='';
  538. @def = '';
  539. @def = _cfgDB_findDef('TYPE=InfoPanel','LAYOUTFILE');
  540. foreach $filename (@def) {
  541. next unless $filename;
  542. push @files, $filename;
  543. }
  544. # find holiday files
  545. $filename ='';
  546. @def = '';
  547. @def = _cfgDB_findDef('TYPE=holiday','NAME');
  548. foreach $filename (@def) {
  549. next unless $filename;
  550. push @files, "$modpath/FHEM/".$filename.".holiday";
  551. }
  552. # import uniqueID file
  553. $filename = "$modpath/FHEM/FhemUtils/uniqueID";
  554. push @files,$filename if (-e $filename);
  555. # do the import
  556. $filename = '';
  557. foreach $filename (@files) {
  558. if ( -r $filename ) {
  559. my $filesize = -s $filename;
  560. _cfgDB_binFileimport($filename,$filesize);
  561. $ret .= "importing: $filename\n";
  562. }
  563. }
  564. return $ret;
  565. }
  566. # return SVN Id, called by fhem's CommandVersion
  567. sub cfgDB_svnId() {
  568. return "# ".'$Id: configDB.pm 13382 2017-02-10 20:48:14Z betateilchen $'
  569. }
  570. # return filelist depending on directory and regexp
  571. sub cfgDB_FW_fileList($$@) {
  572. my ($dir,$re,@ret) = @_;
  573. my @files = split(/\n/, _cfgDB_Filelist('notitle'));
  574. foreach my $f (@files) {
  575. next if( $f !~ m/^$dir/ );
  576. $f =~ s,$dir\/,,;
  577. next if($f !~ m,^$re$, || $f eq '99_Utils.pm');
  578. push @ret, "$f.configDB";
  579. }
  580. return @ret;
  581. }
  582. # read filelist containing 99_ files in database
  583. sub cfgDB_Read99() {
  584. my $ret = "";
  585. my $fhem_dbh = _cfgDB_Connect;
  586. my $sth = $fhem_dbh->prepare( "SELECT filename FROM fhembinfilesave WHERE filename like '%/99_%.pm' group by filename" );
  587. $sth->execute();
  588. while (my $line = $sth->fetchrow_array()) {
  589. $line =~ m,^(.*)/([^/]*)$,; # Split into dir and file
  590. $ret .= "$2,"; #
  591. }
  592. $sth->finish();
  593. $fhem_dbh->disconnect();
  594. $ret =~ s/,$//;
  595. return $ret;
  596. }
  597. # return SVN Id from file stored in database
  598. sub cfgDB_Fileversion($$) {
  599. my ($file,$ret) = @_;
  600. $ret = "No Id found for $file";
  601. my ($err,@in) = cfgDB_FileRead($file);
  602. foreach(@in){ $ret = $_ if($_ =~ m/# \$Id:/); }
  603. return $ret;
  604. }
  605. ##################################################
  606. # Basic functions needed for DB configuration
  607. # but not called from fhem.pl directly
  608. #
  609. # connect do database
  610. sub _cfgDB_Connect() {
  611. my $fhem_dbh = DBI->connect(
  612. "dbi:$cfgDB_dbconn",
  613. $cfgDB_dbuser,
  614. $cfgDB_dbpass,
  615. { AutoCommit => 0, RaiseError => 1 },
  616. ) or die $DBI::errstr;
  617. return $fhem_dbh;
  618. }
  619. # add configuration entry into fhemconfig
  620. sub _cfgDB_InsertLine($$$$) {
  621. my ($fhem_dbh, $uuid, $line, $counter) = @_;
  622. my ($c,$d,$p1,$p2) = split(/ /, $line, 4);
  623. my $sth = $fhem_dbh->prepare('INSERT INTO fhemconfig values (?, ?, ?, ?, ?, ?)');
  624. $sth->execute($c, $d, $p1, $p2, $counter, $uuid);
  625. return;
  626. }
  627. # pass command table to AnalyzeCommandChain
  628. sub _cfgDB_Execute($@) {
  629. my ($cl, @dbconfig) = @_;
  630. my (@ret);
  631. foreach my $l (@dbconfig) {
  632. $l =~ s/[\r\n]/\n/g;
  633. $l =~ s/\\\n/\n/g;
  634. my $tret = AnalyzeCommandChain($cl, $l);
  635. push @ret, $tret if(defined($tret));
  636. }
  637. return join("\n", @ret) if(@ret);
  638. return undef;
  639. }
  640. # read all entries from fhemconfig
  641. # and add them to command table for execution
  642. sub _cfgDB_ReadCfg(@) {
  643. my (@dbconfig) = @_;
  644. my $fhem_dbh = _cfgDB_Connect;
  645. my ($sth, @line, $row);
  646. # maybe this will be done with join later
  647. my $uuid = $fhem_dbh->selectrow_array('SELECT versionuuid FROM fhemversions WHERE version = 0');
  648. $sth = $fhem_dbh->prepare( "SELECT * FROM fhemconfig WHERE versionuuid = '$uuid' and device <>'configdb' order by version" );
  649. $sth->execute();
  650. while (@line = $sth->fetchrow_array()) {
  651. $row = "$line[0] $line[1] $line[2]";
  652. $row .= " $line[3]" if defined($line[3]);
  653. push @dbconfig, $row;
  654. }
  655. $fhem_dbh->disconnect();
  656. return @dbconfig;
  657. }
  658. # read all entries from fhemstate
  659. # and add them to command table for execution
  660. sub _cfgDB_ReadState(@) {
  661. my (@dbconfig) = @_;
  662. my $fhem_dbh = _cfgDB_Connect;
  663. my ($sth, $row);
  664. $sth = $fhem_dbh->prepare( "SELECT * FROM fhemstate" );
  665. $sth->execute();
  666. while ($row = $sth->fetchrow_array()) {
  667. push @dbconfig, $row;
  668. }
  669. $fhem_dbh->disconnect();
  670. return @dbconfig;
  671. }
  672. # rotate all versions to versionnum + 1
  673. # return uuid for new version 0
  674. sub _cfgDB_Rotate($$) {
  675. my ($fhem_dbh,$newversion) = @_;
  676. my $uuid = _cfgDB_Uuid;
  677. $fhem_dbh->do("UPDATE fhemversions SET VERSION = VERSION+1 where VERSION >= 0") if $newversion == 0;
  678. $fhem_dbh->do("INSERT INTO fhemversions values ('$newversion', '$uuid')");
  679. return $uuid;
  680. }
  681. # 2015-01-12 use the fhem default function
  682. sub _cfgDB_Uuid() {
  683. return createUniqueId();
  684. }
  685. sub _cfgDB_filesize_str($) {
  686. my ($size) = @_;
  687. if ($size > 1099511627776) # TiB: 1024 GiB
  688. {
  689. return sprintf("%.2f TB", $size / 1099511627776);
  690. }
  691. elsif ($size > 1073741824) # GiB: 1024 MiB
  692. {
  693. return sprintf("%.2f GB", $size / 1073741824);
  694. }
  695. elsif ($size > 1048576) # MiB: 1024 KiB
  696. {
  697. return sprintf("%.2f MB", $size / 1048576);
  698. }
  699. elsif ($size > 1024) # KiB: 1024 B
  700. {
  701. return sprintf("%.2f KB", $size / 1024);
  702. }
  703. else # bytes
  704. {
  705. return "$size byte" . ($size == 1 ? "" : "s");
  706. }
  707. }
  708. ##################################################
  709. # Additional backend functions
  710. # not called from fhem.pl directly
  711. #
  712. # migrate existing fhem config into database
  713. sub _cfgDB_Migrate() {
  714. my $ret;
  715. $ret = "Starting migration...\n";
  716. Log3('configDB',4,'Starting migration');
  717. $ret .= "Processing: database initialization\n";
  718. Log3('configDB',4,'Processing: cfgDB_Init');
  719. cfgDB_Init;
  720. $ret .= "Processing: save config\n";
  721. Log3('configDB',4,'Processing: cfgDB_SaveCfg');
  722. cfgDB_SaveCfg;
  723. $ret .= "Processing: save state\n";
  724. Log3('configDB',4,'Processing: cfgDB_SaveState');
  725. cfgDB_SaveState;
  726. $ret .= "Processing: fileimport\n";
  727. Log3('configDB',4,'Processing: cfgDB_MigrationImport');
  728. $ret .= cfgDB_MigrationImport;
  729. $ret .= "Migration completed\n\n";
  730. Log3('configDB',4,'Migration completed.');
  731. $ret .= _cfgDB_Info;
  732. return $ret;
  733. }
  734. # show database statistics
  735. sub _cfgDB_Info() {
  736. my ($l, @r, $f);
  737. for my $i (1..65){ $l .= '-';}
  738. push @r, $l;
  739. push @r, " configDB Database Information";
  740. push @r, $l;
  741. push @r, " ".cfgDB_svnId;
  742. push @r, $l;
  743. push @r, " dbconn: $cfgDB_dbconn";
  744. push @r, " dbuser: $cfgDB_dbuser" if !$configDB{attr}{private};
  745. push @r, " dbpass: $cfgDB_dbpass" if !$configDB{attr}{private};
  746. push @r, " dbtype: $cfgDB_dbtype";
  747. push @r, " Unknown dbmodel type in configuration file." if $cfgDB_dbtype eq 'unknown';
  748. push @r, " Only Mysql, Postgresql, SQLite are fully supported." if $cfgDB_dbtype eq 'unknown';
  749. if ($cfgDB_dbtype eq "SQLITE") {
  750. my $size = -s $cfgDB_filename;
  751. $size = _cfgDB_filesize_str($size);
  752. push @r, " dbsize: $size";
  753. }
  754. push @r, $l;
  755. my $fhem_dbh = _cfgDB_Connect;
  756. my ($sql, $sth, @line, $row);
  757. # read versions table statistics
  758. my $maxVersions = $configDB{attr}{maxversions};
  759. $maxVersions = ($maxVersions) ? $maxVersions : 0;
  760. push @r, " max Versions: $maxVersions" if($maxVersions);
  761. my $count;
  762. $count = $fhem_dbh->selectrow_array('SELECT count(*) FROM fhemconfig');
  763. push @r, " config: $count entries";
  764. push @r, "";
  765. # read versions creation time
  766. $sql = "SELECT * FROM fhemconfig as c join fhemversions as v on v.versionuuid=c.versionuuid ".
  767. "WHERE COMMAND like '#created%' ORDER by v.VERSION";
  768. $sth = $fhem_dbh->prepare( $sql );
  769. $sth->execute();
  770. while (@line = $sth->fetchrow_array()) {
  771. $line[3] = "" unless defined $line[3];
  772. $row = " Ver $line[6] saved: $line[1] $line[2] $line[3] def: ".
  773. $fhem_dbh->selectrow_array("SELECT COUNT(*) from fhemconfig where COMMAND = 'define' and VERSIONUUID = '$line[5]'");
  774. $row .= " attr: ".
  775. $fhem_dbh->selectrow_array("SELECT COUNT(*) from fhemconfig where COMMAND = 'attr' and VERSIONUUID = '$line[5]'");
  776. push @r, $row;
  777. }
  778. push @r, $l;
  779. # read state table statistics
  780. $count = $fhem_dbh->selectrow_array('SELECT count(*) FROM fhemstate');
  781. $f = ($count>1) ? "s" : "";
  782. # read state table creation time
  783. $sth = $fhem_dbh->prepare( "SELECT * FROM fhemstate WHERE STATESTRING like '#%'" );
  784. $sth->execute();
  785. while ($row = $sth->fetchrow_array()) {
  786. (undef,$row) = split(/#/,$row);
  787. $row = " state: $count entrie$f saved: $row";
  788. push @r, $row;
  789. }
  790. push @r, $l;
  791. $row = $fhem_dbh->selectall_arrayref("SELECT filename from fhembinfilesave group by filename");
  792. $count = @$row;
  793. $count = ($count)?$count:'No';
  794. $f = ("$count" ne '1') ? "s" : "";
  795. $row = " filesave: $count file$f stored in database";
  796. push @r, $row;
  797. push @r, $l;
  798. $fhem_dbh->disconnect();
  799. return join("\n", @r);
  800. }
  801. # recover former config from database archive
  802. sub _cfgDB_Recover($) {
  803. my ($version) = @_;
  804. my ($cmd, $count, $ret);
  805. if($version > 0) {
  806. my $fhem_dbh = _cfgDB_Connect;
  807. $cmd = "SELECT count(*) FROM fhemconfig WHERE VERSIONUUID in (select versionuuid from fhemversions where version = $version)";
  808. $count = $fhem_dbh->selectrow_array($cmd);
  809. if($count > 0) {
  810. my $fromuuid = $fhem_dbh->selectrow_array("select versionuuid from fhemversions where version = $version");
  811. my $touuid = _cfgDB_Uuid;
  812. # Delete current version 0
  813. $fhem_dbh->do("DELETE FROM fhemconfig WHERE VERSIONUUID in (select versionuuid from fhemversions where version = 0)");
  814. $fhem_dbh->do("update fhemversions set versionuuid = '$touuid' where version = 0");
  815. # Copy selected version to version 0
  816. my ($sth, $sth2, @line);
  817. $cmd = "SELECT * FROM fhemconfig WHERE VERSIONUUID = '$fromuuid'";
  818. $sth = $fhem_dbh->prepare($cmd);
  819. $sth->execute();
  820. $sth2 = $fhem_dbh->prepare('INSERT INTO fhemconfig values (?, ?, ?, ?, ?, ?)');
  821. while (@line = $sth->fetchrow_array()) {
  822. $sth2->execute($line[0], $line[1], $line[2], $line[3], $line[4], $touuid);
  823. }
  824. $fhem_dbh->commit();
  825. $fhem_dbh->disconnect();
  826. # Inform user about restart or rereadcfg needed
  827. $ret = "Version 0 deleted.\n";
  828. $ret .= "Version $version copied to version 0\n\n";
  829. $ret .= "Please use rereadcfg or restart to activate configuration.";
  830. } else {
  831. $fhem_dbh->disconnect();
  832. $ret = "No entries found in version $version.\nNo changes committed to database.";
  833. }
  834. } else {
  835. $ret = 'Please select version 1..n for recovery.';
  836. }
  837. return $ret;
  838. }
  839. # delete old configurations
  840. sub _cfgDB_Reorg(;$$) {
  841. my ($lastversion,$quiet) = @_;
  842. $lastversion = (defined($lastversion)) ? $lastversion : 3;
  843. Log3('configDB', 4, "DB Reorg started, keeping last $lastversion versions.");
  844. my $fhem_dbh = _cfgDB_Connect;
  845. $fhem_dbh->do("delete FROM fhemconfig where versionuuid in (select versionuuid from fhemversions where version > $lastversion)");
  846. $fhem_dbh->do("delete from fhemversions where version > $lastversion");
  847. $fhem_dbh->do("delete FROM fhemconfig where versionuuid in (select versionuuid from fhemversions where version = -1)");
  848. $fhem_dbh->do("delete from fhemversions where version = -1");
  849. $fhem_dbh->commit();
  850. $fhem_dbh->disconnect();
  851. eval qx(sqlite3 $cfgDB_filename vacuum) if($cfgDB_dbtype eq "SQLITE");
  852. return if(defined($quiet));
  853. return " Result after database reorg:\n"._cfgDB_Info;
  854. }
  855. # delete temporary version
  856. sub _cfgDB_DeleteTemp() {
  857. Log3('configDB', 4, "configDB: delete temporary Version -1");
  858. my $fhem_dbh = _cfgDB_Connect;
  859. $fhem_dbh->do("delete FROM fhemconfig where versionuuid in (select versionuuid from fhemversions where version = -1)");
  860. $fhem_dbh->do("delete from fhemversions where version = -1");
  861. $fhem_dbh->commit();
  862. $fhem_dbh->disconnect();
  863. return;
  864. }
  865. # search for device or fulltext in db
  866. sub _cfgDB_Search($$;$) {
  867. my ($search,$searchversion,$dsearch) = @_;
  868. return 'Syntax error.' if(!(defined($search)));
  869. my $fhem_dbh = _cfgDB_Connect;
  870. my ($sql, $sth, @line, $row, @result, $ret, $text);
  871. $sql = "SELECT command, device, p1, p2 FROM fhemconfig as c join fhemversions as v ON v.versionuuid=c.versionuuid ";
  872. $sql .= "WHERE v.version = '$searchversion' AND command not like '#create%' ";
  873. # 2015-10-24 - changed, forum #42190
  874. if($cfgDB_dbtype eq 'SQLITE') {;
  875. $sql .= "AND device like '$search%' ESCAPE '\\' " if($dsearch);
  876. $sql .= "AND (device like '$search%' ESCAPE '\\' OR P1 like '$search%' ESCAPE '\\' OR P2 like '$search%' ESCAPE '\\') " if(!$dsearch);
  877. } else {
  878. $sql .= "AND device like '$search%' " if($dsearch);
  879. $sql .= "AND (device like '$search%' OR P1 like '$search%' OR P2 like '$search%') " if(!$dsearch);
  880. }
  881. $sql .= "ORDER BY lower(device),command DESC";
  882. $sth = $fhem_dbh->prepare( $sql);
  883. Log 5,"configDB: $sql";
  884. $sth->execute();
  885. $text = " device" if($dsearch);
  886. push @result, "search result for$text: $search in version: $searchversion";
  887. push @result, "--------------------------------------------------------------------------------";
  888. while (@line = $sth->fetchrow_array()) {
  889. $row = "$line[0] $line[1] $line[2] $line[3]";
  890. push @result, "$row";
  891. }
  892. $fhem_dbh->disconnect();
  893. $ret = join("\n", @result);
  894. return $ret;
  895. }
  896. # called from cfgDB_Diff
  897. sub __cfgDB_Diff($$$$) {
  898. my ($fhem_dbh,$search,$searchversion,$svinternal) = @_;
  899. my ($sql, $sth, @line, $ret);
  900. if($svinternal != -1) {
  901. $sql = "SELECT command, device, p1, p2 FROM fhemconfig as c join fhemversions as v ON v.versionuuid=c.versionuuid ".
  902. "WHERE v.version = '$searchversion' AND device = '$search' ORDER BY command DESC";
  903. } else {
  904. $sql = "SELECT command, device, p1, p2 FROM fhemconfig as c join fhemversions as v ON v.versionuuid=c.versionuuid ".
  905. "WHERE v.version = '$searchversion' ORDER BY command DESC";
  906. }
  907. $sth = $fhem_dbh->prepare( $sql);
  908. $sth->execute();
  909. while (@line = $sth->fetchrow_array()) {
  910. $ret .= "$line[0] $line[1] $line[2] $line[3]\n";
  911. }
  912. return $ret;
  913. }
  914. # compare device configurations from 2 versions
  915. sub _cfgDB_Diff($$) {
  916. my ($search,$searchversion) = @_;
  917. my ($ret, $v0, $v1);
  918. if ($search eq 'all' && $searchversion eq 'current') {
  919. _cfgDB_DeleteTemp();
  920. cfgDB_SaveCfg(-1);
  921. $searchversion = -1;
  922. }
  923. my $fhem_dbh = _cfgDB_Connect;
  924. $v0 = __cfgDB_Diff($fhem_dbh,$search,0,$searchversion);
  925. $v1 = __cfgDB_Diff($fhem_dbh,$search,$searchversion,$searchversion);
  926. $fhem_dbh->disconnect();
  927. $ret = diff \$v0, \$v1, { STYLE => "Table" };
  928. if($searchversion == -1) {
  929. _cfgDB_DeleteTemp();
  930. $searchversion = "UNSAVED";
  931. }
  932. $ret = "\nNo differences found!" if !$ret;
  933. $ret = "compare device: $search in current version 0 (left) to version: $searchversion (right)\n$ret\n";
  934. return $ret;
  935. }
  936. # find DEF, input supports devspec definitions
  937. sub _cfgDB_findDef($;$) {
  938. my ($search,$internal) = @_;
  939. $internal = 'DEF' unless defined($internal);
  940. my @ret;
  941. my @etDev = devspec2array($search);
  942. foreach my $d (@etDev) {
  943. next unless $d;
  944. push @ret, $defs{$d}{$internal};
  945. }
  946. return @ret;
  947. }
  948. ##################################################
  949. # functions used for file handling
  950. # called by 98_configdb.pm
  951. #
  952. # delete file from database
  953. sub _cfgDB_Filedelete($) {
  954. my ($filename) = @_;
  955. my $fhem_dbh = _cfgDB_Connect;
  956. my $ret = $fhem_dbh->do("delete from fhembinfilesave where filename = '$filename'");
  957. $fhem_dbh->commit();
  958. $fhem_dbh->disconnect();
  959. if($ret > 0) {
  960. $ret = "File $filename deleted from database.";
  961. } else {
  962. $ret = "File $filename not found in database.";
  963. }
  964. return $ret;
  965. }
  966. # export file from database to filesystem
  967. sub _cfgDB_Fileexport($;$) {
  968. my ($filename,$raw) = @_;
  969. my $fhem_dbh = _cfgDB_Connect;
  970. my $sth = $fhem_dbh->prepare( "SELECT content FROM fhembinfilesave WHERE filename = '$filename'" );
  971. $sth->execute();
  972. my $blobContent = $sth->fetchrow_array();
  973. my $counter = length($blobContent);
  974. $sth->finish();
  975. $fhem_dbh->disconnect();
  976. return "No data found for file $filename" unless $counter;
  977. return ($blobContent,$counter) if $raw;
  978. open( FILE,">$filename" );
  979. binmode(FILE);
  980. print FILE $blobContent;
  981. close( FILE );
  982. return "$counter bytes written from database into file $filename";
  983. }
  984. # import file into database
  985. sub _cfgDB_binFileimport($$;$) {
  986. my ($filename,$filesize,$doDelete) = @_;
  987. $doDelete = (defined($doDelete)) ? 1 : 0;
  988. open (inFile,"<$filename") || die $!;
  989. my $blobContent;
  990. binmode(inFile);
  991. my $readBytes = read(inFile, $blobContent, $filesize);
  992. close(inFile);
  993. my $fhem_dbh = _cfgDB_Connect;
  994. $fhem_dbh->do("delete from fhembinfilesave where filename = '$filename'");
  995. my $sth = $fhem_dbh->prepare('INSERT INTO fhembinfilesave values (?, ?)');
  996. # add support for postgresql by Matze
  997. $sth->bind_param( 1, $filename );
  998. if ($cfgDB_dbtype eq "POSTGRESQL") {
  999. $sth->bind_param( 2, $blobContent, { pg_type => DBD::Pg::PG_BYTEA() } );
  1000. } else {
  1001. $sth->bind_param( 2, $blobContent );
  1002. }
  1003. $sth->execute($filename, $blobContent);
  1004. $sth->finish();
  1005. $fhem_dbh->commit();
  1006. $fhem_dbh->disconnect();
  1007. unlink($filename) if(($configDB{attr}{deleteimported} || $doDelete) && $readBytes);
  1008. return "$readBytes bytes written from file $filename to database";
  1009. }
  1010. # list all files stored in database
  1011. sub _cfgDB_Filelist(;$) {
  1012. my ($notitle) = @_;
  1013. my $ret = "Files found in database:\n".
  1014. "------------------------------------------------------------\n";
  1015. $ret = "" if $notitle;
  1016. my $fhem_dbh = _cfgDB_Connect;
  1017. my $sql = "SELECT filename FROM fhembinfilesave group by filename order by filename";
  1018. my $content = $fhem_dbh->selectall_arrayref($sql);
  1019. foreach my $row (@$content) {
  1020. $ret .= "@$row[0]\n" if(defined(@$row[0]));
  1021. }
  1022. $fhem_dbh->disconnect();
  1023. return $ret;
  1024. }
  1025. 1;
  1026. =pod
  1027. =item helper
  1028. =item summary configDB backend
  1029. =item summary_DE configDB backend
  1030. =begin html
  1031. <a name="configDB"></a>
  1032. <h3>configDB</h3>
  1033. <ul>
  1034. <a href="https://forum.fhem.de/index.php?board=46.0">Link to FHEM forum</a><br/><br/>
  1035. This is the core backend library for configuration from SQL database.<br/>
  1036. See <a href="#configdb">configdb command documentation</a> for detailed info.<br/>
  1037. </ul>
  1038. =end html
  1039. =begin html_DE
  1040. <a name="configDB"></a>
  1041. <h3>configDB</h3>
  1042. <ul>
  1043. configDB ist die Funktionsbibliothek f&uuml;r die Konfiguration aus einer SQL Datenbank.<br/>
  1044. Die ausf&uuml;hrliche Dokumentation findet sich in der <a href="#configdb">configdb Befehlsbeschreibung</a>.
  1045. </ul>
  1046. =end html_DE
  1047. =cut