Skip to content

Commit

Permalink
Item15083: initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelDaum committed Apr 27, 2022
0 parents commit 941110d
Show file tree
Hide file tree
Showing 22 changed files with 1,386 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
@@ -0,0 +1,9 @@
*.swp
*,v
/DBIPlugin.md5
/DBIPlugin.sha1
/DBIPlugin.tgz
/DBIPlugin.txt
/DBIPlugin.zip
/DBIPlugin_installer
/DBIPlugin_installer.pl
79 changes: 79 additions & 0 deletions data/System/DBIPlugin.txt
@@ -0,0 +1,79 @@
%META:TOPICINFO{author="micha" date="1648549480" format="1.1" version="2"}%
---+!! %TOPIC%
%FORMFIELD{"Description"}%

%TOC%

---++ Description

This plugin offers a middle layer for Foswiki extensions to ease connecting to
SQL databases and manage schemes of table and index definitions. The idea is to
keep things as lean as possible without imposing any additional structure. <nop>DBIPlugin
will maintain a plugin's database scheme to make sure it is created and updated as required.

A plugin sub-classes =Foswiki::DBI::Schema= which is then loaded before
connecting to a database. While connecting to the database the schema then is added to the database.

<nop>DBIPlugin supports any database for which a DBD perl driver is available. A plugin may support
multiple databases where the schemes differ in parts. For example the schema for SQLite differs
from the one for <nop>MariaDB, <nop>MySQL or <nop>PostgreSQL. The plugin will then implement:

* =Foswiki::DBISchema::SQLite=
* =Foswiki::DBISchema::MariaDB=
* =Foswiki::DBISchema::MySQL=
* =Foswiki::DBISchema::PostgreSQL=

The syntax of each of them are custom tailored towards the respective database vendor. Note
however that from there on it is the plugin's responsibility to cope with further differences
among databases beyond just schema definitions.

Have a look at [[DBIPluginPerlAPI]] for further information.

---++ Creating a Foswiki database

The base assumption of <nop>DBIPLugin is that all plugins share a single database that all
tables and indexes are created within. This means that plugins must pay attention to naming them.
Best practices is to prefix any table or index with the name of the plugin. For example <nop>LikePlugin
stores its data in a table called =LikePlugin_likes=.

Before configuring <nop>DBIPlugin you need to create a database and a user/password for this plugin
(except for SQLite). For instance a shell script like this will do:

<verbatim class="bash">
#!/bin/sh

database_server=$1
database_name=$2
database_user=$3
database_password=$4

echo "creating database $database_name on $database_server"
cat <<HERE | mysql -h $database_server
CREATE USER IF NOT EXISTS $database_user@$database_server IDENTIFIED BY '$database_password';
CREATE DATABASE IF NOT EXISTS $database_name;
GRANT ALL ON $database_name.* TO $database_user@$database_server;
FLUSH PRIVILEGES;
HERE
</verbatim>

---++ Installation Instructions
%$INSTALL_INSTRUCTIONS%

---++ Dependencies
%$DEPENDENCIES%

---++ Change History

%TABLE{columnwidths="7em" tablewidth="100%"}%
| 29 Mar 2022 | initial release |

%META:FORM{name="PackageForm"}%
%META:FIELD{name="Author" title="Author" value="Michael Daum"}%
%META:FIELD{name="Version" title="Version" value="%25$VERSION%25"}%
%META:FIELD{name="Release" title="Release" value="%25$RELEASE%25"}%
%META:FIELD{name="Description" title="Description" value="%25$SHORTDESCRIPTION%25"}%
%META:FIELD{name="Repository" title="Repository" value="https://github.com/foswiki/%25$ROOTMODULE%25"}%
%META:FIELD{name="Copyright" title="Copyright" value="2021-2022, Michael Daum, All Rights Reserved"}%
%META:FIELD{name="License" title="License" value="GPL ([[https://www.gnu.org/copyleft/gpl.html][GNU General Public License]])"}%
%META:FIELD{name="Home" title="Home" value="https://foswiki.org/Extensions/%25$ROOTMODULE%25"}%
%META:FIELD{name="Support" title="Support" value="https://foswiki.org/Support/%25$ROOTMODULE%25"}%
13 changes: 13 additions & 0 deletions data/System/DBIPluginPerlAPI.txt
@@ -0,0 +1,13 @@
%META:TOPICINFO{author="micha" comment="reprev" date="1648549528" format="1.1" reprev="2" version="2"}%
%META:TOPICPARENT{name="DBIPlugin"}%
---+ DBIPlugin Perl API
<!--
* Set EDITCHAPTERPLUGIN_ENABLED = off
-->

%TOC%

%INCLUDE{"doc:Foswiki::DBI" level="2"}%
%INCLUDE{"doc:Foswiki::DBI::Database" level="2"}%
%INCLUDE{"doc:Foswiki::DBI::Schema" level="2"}%
%INCLUDE{"doc:Foswiki::Iterator::DBIterator" level="2"}%
118 changes: 118 additions & 0 deletions lib/Foswiki/DBI.pm
@@ -0,0 +1,118 @@
# Plugin for Foswiki - The Free and Open Source Wiki, https://foswiki.org/
#
# DBIPlugin is Copyright (C) 2021-2022 Michael Daum http://michaeldaumconsulting.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details, published at
# http://www.gnu.org/copyleft/gpl.html

package Foswiki::DBI;

=begin TML
---+ package Foswiki::DBI
Interface for Foswiki DBI developers
=cut

use strict;
use warnings;
use constant MEMORYCACHE => 0; # experimental
our $DB;

=begin TML
---++ StaticMethod getDB() -> $database
Creates a database connection to the configured implementation,
connects to it and loads the base schema.
=cut

sub getDB {

unless ($DB) {
my $impl = $Foswiki::cfg{DBI}{Implementation};
eval "require $impl";
die($@) if $@;

$DB = $impl->new();

loadSchema("Foswiki::DBI::Schema");
}

return $DB;
}

=begin TML
---++ StaticMethod loadSchema($schemaBase) -> $database
Loads a database schema and returns the db. The =$schemaBase= is the
base perl class of the caller. The real database schema being loaded
resides in a sub class of the =$schemaBase= according to the database
implementation of the system.
For example a =MyPlugin= must provide the following classes to support
SQLite and <nop>MariaDB:
<verbatim>
Foswiki::Plugins::MyPlugin::Schema
Foswiki::Plugins::MyPlugin::Schema::MariaDB
Foswiki::Plugins::MyPlugin::Schema::SQLite
</verbatim>
The schema is then called using:
<verbatim class="perl">
my $db = Foswiki::DBI::loadSchema("Foswiki::Plugins::MyPlugin::Schema");
</verbatim>
Given =MariaDB= is the current database implementation, it actually loads the
schema =Foswiki::Plugins::MyPlugin::Schema::MariaDB= and returns a singleton
database object of type =Foswiki::DBI::Database::MariaDB=.
This singleton object is shared among all subsystems connecting to the
database.
=cut

sub loadSchema {
my $schemaBase = shift;

my $db = getDB();
my $package = $schemaBase."::".$db->getClassName();

eval "require $package";
die("ERROR: $@") if $@;

my $schema = $package->new();
$db->applySchema($schema);

return $db;
}

=begin TML
---++ StaticMethod finish()
Close any database connection being made during the session
=cut

sub finish {
unless (MEMORYCACHE) {
$DB->finish if defined $DB;
undef $DB;
}
}


1;

0 comments on commit 941110d

Please sign in to comment.