Skip to content

Commit

Permalink
Add support for running in a non-standard schema
Browse files Browse the repository at this point in the history
To prove it works, run the tests in the non-standard schema named 'xyz';
the standard schema being "public".

Closes #5990.
  • Loading branch information
ehuelsmann committed Nov 8, 2021
1 parent 67ac869 commit 0f82ccd
Show file tree
Hide file tree
Showing 26 changed files with 184 additions and 63 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Expand Up @@ -128,6 +128,7 @@ commands:
-e "s/# \(smtphost = \).*\$/\1mailhog:1025/g" \
-e "s/# \(backup_email_from = \).*\$/\1lsmb-backups@example.com/g" \
-e "s/#dojo_built = 1/dojo_built = 0/" \
-e "s/db_namespace = public/db_namespace = xyz/" \
ledgersmb.conf
# Freshen up CPAN
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Expand Up @@ -164,6 +164,7 @@ jobs:
run: |
mkdir screens logs
cp doc/conf/ledgersmb.conf.default ledgersmb.conf
sed -i -e 's/db_namespace = public/db_namespace = xyz/' ledgersmb.conf
- name: Setup Perl environment
uses: shogo82148/actions-setup-perl@v1
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Expand Up @@ -134,9 +134,9 @@ ifneq ($(origin DOCKER_CMD),undefined)
else
# the 'dropdb' command may fail, hence the prefix minus-sign
-dropdb lsmb_test
perl -Ilib bin/ledgersmb-admin create \
$${PGUSER:-postgres}@$${PGHOST:-localhost}/$${PGDATABASE:-lsmb_test}
yath test --no-color --retry=2 \
perl -Ilib bin/ledgersmb-admin --debug create \
$${PGUSER:-postgres}@$${PGHOST:-localhost}/$${PGDATABASE:-lsmb_test}#xyz
PGOPTIONS="-c search_path=xyz" yath test --no-color --retry=2 \
--pgtap-dbname=lsmb_test --pgtap-username=postgres \
--pgtap-psql=.circleci/psql-wrap \
--Feature-tags=~@wip \
Expand Down
11 changes: 8 additions & 3 deletions bin/migrate-company
Expand Up @@ -155,13 +155,15 @@ my $loglevel = 'error';
my $logconf = './migrate-company.log.conf';

my $cfg;
my $schema = 'public';

GetOptions(
'help' => \&help,
'host|h=s' => \$host,
'user|U=s' => \$user,
'dbname|d=s' => \$dbname,
'data=s' => \$data,
'schema=s' => \$schema,
'log=s' => \$loglevel,
'logconf'=> \$logconf,
)
Expand Down Expand Up @@ -387,9 +389,12 @@ sub migrate {
return 2;
}
my $db = LedgerSMB::Database->new(
username => $user,
password => $ENV{PGPASSWORD},
dbname => $dbname
connect_data => {
user => $user,
password => $ENV{PGPASSWORD},
dbname => $dbname,
},
schema => $schema,
);
my $dbinfo = $db->get_info;
if (not $dbinfo->{status}
Expand Down
9 changes: 6 additions & 3 deletions bin/patch-release-upgrade
Expand Up @@ -9,6 +9,7 @@ use LedgerSMB::Database;
use LedgerSMB::Database::SchemaChecks::JSON qw( json_formatter_context );

my $user;
my $schema = 'public';
my $script_name = `basename $0`; chomp $script_name;
my $data_dir = '.';
my $usage = qq|
Expand All @@ -35,11 +36,12 @@ This is to prevent passwords being saved into the history file.
sub rebuild_modules {

my $database = LedgerSMB::Database->new(
{
username => $ENV{PGUSER},
connect_data => {
user => $ENV{PGUSER},
dbname => $ENV{PGDATABASE},
password => $ENV{PGPASSWORD},
})
},
schema => $schema)
or die "No database connection.";

my $out = json_formatter_context {
Expand All @@ -62,6 +64,7 @@ sub usage { print $usage; exit; }
GetOptions(
'help' => \&usage,
'user=s' => \$user,
'schema=s' => \$schema,
'data-dir=s'=> \$data_dir,
);

Expand Down
7 changes: 5 additions & 2 deletions bin/prepare-company-database
Expand Up @@ -65,6 +65,7 @@ my $coa="$srcdir/sql/coa/us/chart/General.sql";
my $gifi=undef;
my $progress=0;
my $help=0;
my $schema='public';

my $script_name = 'tools/prepare-company-database.pl';

Expand Down Expand Up @@ -121,6 +122,7 @@ GetOptions(
'cc=s' => \$cc,
'coa=s' => \$coa,
'gifi=s' => \$gifi,
'schema=s' => \$schema,
'help' => \$help
);

Expand All @@ -143,7 +145,8 @@ my $database = LedgerSMB::Database->new(
chart_name => $coa,
company_name => $company,
username => $owner,
password => $pass}
password => $pass},
schema => $schema,
);

$database->create_and_load();
Expand All @@ -156,7 +159,7 @@ $database->create_and_load();
# returned by LedgerSMB::Database, but that is not done yet.

my $lsmb = LedgerSMB->new() || die 'could not create new LedgerSMB object';
$lsmb->{dbh} = DBI->connect("dbi:Pg:dbname=$ENV{PGDATABASE}",
$lsmb->{dbh} = DBI->connect("dbi:Pg:dbname=$ENV{PGDATABASE};options='-c ''search_path=$schema'''",
undef, undef, { AutoCommit => 0 });

# We also have to retrieve the country ID which requires a database query
Expand Down
4 changes: 4 additions & 0 deletions lib/LedgerSMB/Admin/Command.pm
Expand Up @@ -68,10 +68,14 @@ sub connect_data_from_arg {
(?<host>\[[:0-9a-zA-Z]+\]|[\w.]+)
(:(?<port>\d+))?/)?
((?<dbname>[a-z0-9A-Z_% -]+)
(\#(?<schema>[a-z0-9A-Z_% -]+))?
(\?(?<queryparameters>.+))?
)
$!x or die "'$arg' doesn't parse as a connection URI";
my %r = %+;
my $schema = delete $r{schema} // 'public';

$self->config->config->{schema} = $schema;
my $rv = {
map { $_ => _decode($r{$_}) }
grep { $_ ne 'queryparameters' }
Expand Down
1 change: 1 addition & 0 deletions lib/LedgerSMB/Admin/Command/create.pm
Expand Up @@ -30,6 +30,7 @@ sub run {
$self->db(LedgerSMB::Database->new(
connect_data => $connect_data,
source_dir => $self->config->sql_directory,
schema => $self->config->get('schema'),
));
###TODO shouldn't we want to generate the logging output as part of
## the the regular logging output ? Meaning that STDERR gets logged
Expand Down
39 changes: 34 additions & 5 deletions lib/LedgerSMB/Database.pm
Expand Up @@ -39,7 +39,7 @@ use DBD::Pg;
use DBI;
use File::Spec;
use File::Temp;
use PGObject::Util::DBAdmin 'v1.4.0';
use PGObject::Util::DBAdmin 'v1.6.1';

use Moose;
use namespace::autoclean;
Expand All @@ -51,6 +51,24 @@ use LedgerSMB::Database::Loadorder;
our $VERSION = '1.2';



around BUILDARGS => sub {
my $orig = shift;
my $class = shift;
my %args = @_;

if ($args{schema} && $args{connect_data}) {
$args{connect_data}->{options} //= '';

my $enc_schema = ($args{schema} =~ s/['\\]/$1$1/gr);
$args{connect_data}->{options} .= qq{-c search_path=$enc_schema};
}
elsif ($args{schema}) {
die q{Missing 'connect_data' arg; expected to set default schema};
}
return $class->$orig( %args );
};

=head1 PROPERTIES
=head2 source_dir
Expand Down Expand Up @@ -469,19 +487,27 @@ tag in the LOADORDER file will be applied (the main use-case being migrations).
sub load_base_schema {
my ($self, %args) = @_;

my $dbh = $self->connect({ AutoCommit => 1 });
$dbh->do(
q|CREATE SCHEMA IF NOT EXISTS | . $dbh->quote_identifier($self->schema)
)
or die $dbh->errstr;
$self->logger->infof('Created schema "%s"', $self->schema);
$self->run_file(
file => "$self->{source_dir}/Pg-database.sql",
vars => {
lsmb_schema => $self->schema,
},
);
my $dbh = $self->connect({ AutoCommit => 1 });
my $sth = $dbh->prepare(
q|select true
from pg_class cls
join pg_namespace nsp
on nsp.oid = cls.relnamespace
where cls.relname = 'defaults'
and nsp.nspname = 'public'
and nsp.nspname = ?
|);
$sth->execute();
$sth->execute($self->schema);
my ($success) = $sth->fetchrow_array();
$sth->finish();

Expand Down Expand Up @@ -522,6 +548,9 @@ sub _load_module {

my ($success, $stdout, $stderr) = $self->run_file_with_logs(
file => "$self->{source_dir}/modules/$module",
vars => {
lsmb_schema => $self->schema,
},
);
$success or die $stderr;

Expand Down Expand Up @@ -587,7 +616,7 @@ sub create_and_load {
my ($self, $args) = @_;
$self->logger->info('Creating database');
$self->create;
$self->logger->info('Loading schema');
$self->logger->info('Loading schema into ' . $self->schema);
$self->load_base_schema(
log_stdout => $args->{log},
errlog => $args->{errlog},
Expand Down
10 changes: 7 additions & 3 deletions lib/LedgerSMB/Middleware/Authenticate/Company.pm
Expand Up @@ -84,19 +84,23 @@ sub _connect {

my $session = $env->{'lsmb.session'};
my %creds;
@creds{qw/dbname username password/} = (! $login)
@creds{qw/dbname user password/} = (! $login)
? (@{$session}{qw/company login password/})
: ($company, $login, $password);

unless ($creds{username} && $creds{password}) {
unless ($creds{user} && $creds{password}) {
if (! wantarray) {
die q{Expected username and password};
}
return (undef, LedgerSMB::PSGI::Util::unauthorized());
}

my $schema = $self->schema =~ s/'/''/gr;
my $dbh = $env->{'lsmb.db'} =
LedgerSMB::Database->new(schema => $self->schema, %creds)->connect;
LedgerSMB::Database->new(
schema => $schema,
connect_data => \%creds
)->connect;

if (! defined $dbh) {
$env->{'psgix.logger'}->(
Expand Down
17 changes: 16 additions & 1 deletion lib/LedgerSMB/Middleware/MainAppConnect.pm
Expand Up @@ -90,6 +90,14 @@ sub _connect {
$dbh->{pg_user}, $dbh->{pg_db} );
die 'Unable to switch to authenticated user: ' . $dbh->errstr;
};
$dbh->do(q{SET SEARCH_PATH TO }
. $dbh->{private_LedgerSMB}->{schema})
or do {
$log->fatalf( 'Unable to set schema resolution: %s',
$dbh->errstr );
die $dbh->errstr;
};
# $log->infof( 'Schema resolution set to %s', $dbh->quote
}
else {
$log->fatal(
Expand Down Expand Up @@ -218,8 +226,15 @@ sub _verify_session {
sub _create_session {
my ($dbh, $company, $session) = @_;

$log->info('Session database schema: ' . $dbh->{private_LedgerSMB}->{schema});
$log->info('Session database pg_options: ' . $dbh->{pg_options});
my ($current_schemas) = $dbh->selectall_array(
q{SHOW search_path}, { },
) or die $dbh->errstr;
$log->info("Current schema settings: $current_schemas->[0]");

my ($created_session) = $dbh->selectall_array(
q{SELECT * FROM session_create();}, { Slice => {} },
q{SELECT * FROM session_create()}, { Slice => {} },
) or die $dbh->errstr;
$dbh->commit if $created_session->{session_id};

Expand Down
9 changes: 6 additions & 3 deletions lib/LedgerSMB/Scripts/setup.pm
Expand Up @@ -57,6 +57,7 @@ use LedgerSMB::Template::DB;
use LedgerSMB::Database::Upgrade;



my $logger = Log::Any->get_logger(category => 'LedgerSMB::Scripts::setup');
my $CURRENT_MINOR_VERSION;
if ( $LedgerSMB::VERSION =~ /(\d+\.\d+)./ ) {
Expand Down Expand Up @@ -114,9 +115,11 @@ sub _get_database {

return (undef,
LedgerSMB::Database->new(
username => $creds->{login},
password => $creds->{password},
dbname => $request->{database},
connect_data => {
user => $creds->{login},
password => $creds->{password},
dbname => $request->{database},
},
schema => LedgerSMB::Sysconfig::db_namespace(),
));
}
Expand Down
2 changes: 2 additions & 0 deletions sql/modules/Roles.sql
Expand Up @@ -196,6 +196,8 @@ GRANT SELECT ON location_class_to_entity_class TO PUBLIC;

\echo BASE ROLES
SELECT lsmb__create_role('base_user');
SELECT lsmb__grant_schema('base_user', :'lsmb_schema');


\echo BUDGETS
SELECT lsmb__create_role('budget_enter');
Expand Down
18 changes: 12 additions & 6 deletions xt/40-database.t
Expand Up @@ -42,7 +42,8 @@ $db = LedgerSMB::Database->new(
user => $ENV{PGUSER},
password => $ENV{PGPASSWORD},
},
source_dir => './xt/data'
source_dir => './xt/data',
schema => 'xyz',
);
like( dies { $db->create_and_load; },
qr/(APPLICATION ERROR|Specified file does not exist)/,
Expand All @@ -58,7 +59,8 @@ $db = LedgerSMB::Database->new(
user => $ENV{PGUSER},
password => $ENV{PGPASSWORD},
},
source_dir => './xt/data/missing-directory'
source_dir => './xt/data/missing-directory',
schema => 'xyz',
);
like( dies { $db->create_and_load; },
qr/(APPLICATION ERROR|Specified file does not exist)/,
Expand All @@ -81,7 +83,8 @@ $db = LedgerSMB::Database->new(
user => $ENV{PGUSER},
password => $ENV{PGPASSWORD},
},
source_dir => './xt/data/40-database/no-defaults-table'
source_dir => './xt/data/40-database/no-defaults-table',
schema => 'xyz',
);
like( dies { $db->create_and_load; }, qr/Base schema failed to load/,
'Database creation fails on missing defaults table');
Expand All @@ -101,7 +104,8 @@ $db = LedgerSMB::Database->new(
user => $ENV{PGUSER},
password => $ENV{PGPASSWORD},
},
source_dir => './xt/data/40-database/schema-failure'
source_dir => './xt/data/40-database/schema-failure',
schema => 'xyz',
);
like( dies { $db->create_and_load; },
qr/(ERROR:\s*relation "defal" does not exist|error running (command|file))/,
Expand All @@ -123,7 +127,8 @@ $db = LedgerSMB::Database->new(
user => $ENV{PGUSER},
password => $ENV{PGPASSWORD},
},
source_dir => './xt/data/40-database/module-failure-1'
source_dir => './xt/data/40-database/module-failure-1',
schema => 'xyz',
);
like( dies { $db->create_and_load; }, qr/Module FaultyModule.sql failed to load/,
'Database creation fails when a module fails to load (empty module)');
Expand All @@ -137,7 +142,8 @@ $db = LedgerSMB::Database->new(
user => $ENV{PGUSER},
password => $ENV{PGPASSWORD},
},
source_dir => './xt/data/40-database/module-failure-2'
source_dir => './xt/data/40-database/module-failure-2',
schema => 'xyz',
);
like( dies { $db->create_and_load; },
qr/(ERROR:\s*function "fail_me" already exists|error running command)/,
Expand Down

0 comments on commit 0f82ccd

Please sign in to comment.