package Prty::SqlPlus;
use base qw/Prty::Object/;

use strict;
use warnings;

our $VERSION = 1.111;

use Prty::Unindent;
use Prty::Option;
use Prty::Template;

# -----------------------------------------------------------------------------

=encoding utf8

=head1 NAME

Prty::SqlPlus - Erzeuge Code für SQL*Plus

=head1 BASE CLASS

L<Prty::Object>

=head1 EXAMPLE

Der Aufruf

    my $script = Prty::SqlPlus->script('test.sql',q|
            SELECT
                *
            FROM
                all_users
            ORDER BY
                username
            ;
        |,
        -before => q|
            SELECT
                SYSDATE AS t0
            FROM
                dual
            ;
        |,
        -after => q|
            SELECT
                SYSDATE AS t1
            FROM
                dual
            ;
        |,
        -author => 'Frank Seitz',
        -description => q|
            Dies ist ein Test-Skript.
        |,
    );

erzeugt

    -- NAME
    --     test.sql
    --
    -- DESCRIPTION
    --     Dies ist ein Test-Skript.
    --
    -- AUTHOR
    --     Frank Seitz
    
    COLUMN tempdatum NEW_VALUE startdatum NOPRINT
    
    SELECT
        TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS') AS tempdatum
    FROM
        dual;
    
    SPOOL test.sql.&&startdatum..log
    
    SET ECHO ON
    SET FEEDBACK ON
    SET VERIFY OFF
    SET HEADING ON
    SET TAB OFF
    SET PAGESIZE 0
    SET TRIMSPOOL ON
    SET LINESIZE 10000
    SET SERVEROUTPUT ON SIZE 10000
    SET SQLBLANKLINES ON
    SET TIMING ON
    
    WHENEVER OSERROR EXIT FAILURE ROLLBACK
    WHENEVER SQLERROR EXIT FAILURE ROLLBACK
    
    ALTER SESSION SET NLS_NUMERIC_CHARACTERS=",.";
    ALTER SESSION set NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
    
    -- ZUSTAND ZUVOR
    
    SELECT
        SYSDATE AS t0
    FROM
        dual
    ;
    
    -- OPERATION
    
    SELECT
        *
    FROM
        all_users
    ORDER BY
        username
    ;
    
    -- ZUSTAND DANACH
    
    SELECT
        SYSDATE AS t1
    FROM
        dual
    ;
    
    ROLLBACK;
    
    EXIT
    
    -- eof

=head1 METHODS

=head2 Klassenmethoden

=head3 script() - Erzeuge SQL*Plus-Skript

=head4 Synopsis

    $script = $class->script($name,$sql,@opt);

=head4 Arguments

=over 4

=item $name

Name des Skripts. Der Name wird in einen Kommentar an den Anfang
des Skripts gesetzt und für die Benennung der Logdatei genutzt

    NAME-YYYYMMDDHHMMSS.log

wobei der Zeitanteil beim Aufruf des Skripts gesetzt wird.

=item $sql

Der SQL*Plus-Code, der in den Rumpf des Skripts eingesetzt wird.

=back

=head4 Options

=over 4

=item -author => $author

Name des Skript-Autors, z.B. "Frank Seitz".

=item -description => $description

Beschreibung des Skripts. Darf mehrzeilig sein.

=item -commit => $bool (Default: 0)

Wenn diese Option gesetzt ist, wird COMMIT ans Ende des
Skripots gesetzt, sonst ROLLBACK.

=item -before => $sql

SQL*Plus-Code, der I<vor> $sql ausgeführt wird.

=item -beforeAndAfter => $sql

SQL*Plus-Code, der vor I<und> nach $sql ausgeführt wird.

=item -after => $sql

SQL*Plus-Code, der I<nach> $sql ausgeführt wird.

=back

=cut

# -----------------------------------------------------------------------------

sub script {
    my $class = shift;
    my $name = shift;
    my $sql = Prty::Unindent->trim(shift);
    # @_: @opt

    my $author = undef;
    my $description = undef;
    my $commit = 0;
    my $before = undef;
    my $beforeAndAfter = undef;
    my $after = undef;
    my $help = 0;

    Prty::Option->extract(\@_,
        -author=>\$author,
        -description=>\$description,
        -commit=>\$commit,
        -before=>\$before,
        -beforeAndAfter=>\$beforeAndAfter,
        -after=>\$after,
        -help=>\$help,
    );

    my $template = Prty::Unindent->string(q|
        __COMMENT__

        COLUMN tempdatum NEW_VALUE startdatum NOPRINT

        SELECT
            TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS') AS tempdatum
        FROM
            dual;

        SPOOL __NAME__.&&startdatum..log

        SET ECHO ON
        SET FEEDBACK ON
        SET VERIFY OFF
        SET HEADING ON
        SET TAB OFF
        SET PAGESIZE 0
        SET TRIMSPOOL ON
        SET LINESIZE 2100
        SET SQLBLANKLINES ON
        SET TIMING ON

        WHENEVER OSERROR EXIT FAILURE ROLLBACK
        WHENEVER SQLERROR EXIT FAILURE ROLLBACK

        ALTER SESSION SET NLS_NUMERIC_CHARACTERS=",.";
        ALTER SESSION set NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';

        __SQL__

        EXIT

        -- eof
    |);

    my $tpl = Prty::Template->new('text',\$template);

    my $comment = Prty::Unindent->string("
        -- NAME
        --     $name
    ");
    if ($description) {
        $description = Prty::Unindent->trim($description);
        $description =~ s/^/--     /mg;
        $comment .= "--\n-- DESCRIPTION\n$description\n";
    }
    if ($author) {
        $author = Prty::Unindent->trim($author);
        $author =~ s/^/--     /mg;
        $comment .= "--\n-- AUTHOR\n$author\n";
    }

    if ($before || $after || $beforeAndAfter) {
        $sql = "-- OPERATION\n\n$sql";
    }
    if ($beforeAndAfter) {
        $beforeAndAfter = Prty::Unindent->trim($beforeAndAfter);
        if ($before) {
            $before .= "\n";
        }
        $before .= $beforeAndAfter;
        if ($after) {
            $after .= "\n";
        }
        $after .= $beforeAndAfter;
    }
    if ($before) {
        $before = Prty::Unindent->trim($before);
        $sql =
            "-- ZUSTAND VORHER\n\n$before\n\n$sql";
    }
    if ($after) {
        $after = Prty::Unindent->trim($after);
        $sql =
            "$sql\n\n-- ZUSTAND NACHHER\n\n$after\n";
    }
    if ($commit) {
        $sql .= "\nCOMMIT;\n";
    }
    else {
        $sql .= "\nROLLBACK;\n";
    }

    $tpl->replace(
        __COMMENT__ => $comment,
        __NAME__ => $name,
        __SQL__ => $sql,
    );

    return $tpl->asStringNL;
}
    

# -----------------------------------------------------------------------------

=head1 VERSION

1.111

=head1 AUTHOR

Frank Seitz, L<http://fseitz.de/>

=head1 COPYRIGHT

Copyright (C) 2017 Frank Seitz

=head1 LICENSE

This code is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

# -----------------------------------------------------------------------------

1;

# eof
