package PROP::SQL;

use Hash::Util qw/lock_keys/;
use PROP::Constants;

sub new {
    my ($invocant) = @_;
    my $class = ref($invocant) || $invocant;
    my $self = {};
    bless($self, $class);

    $self->{-fields}     = [];
    $self->{-tables}     = [];
    $self->{-conditions} = [];
    $self->{-orderings}  = [];
    $self->{-limit}      = undef;
    $self->{-distinct}   = 0;

    lock_keys(%$self) if DEBUG;

    return $self;
}

sub add_table {
    my ($self, $table) = @_;
    push(@{$self->{-tables}}, $table);
}

sub push_field {
    my ($self, $field) = @_;
    push(@{$self->{-fields}}, $field);
}

sub unshift_field {
    my ($self, $field) = @_;
    unshift(@{$self->{-fields}}, $field);
}

sub push_conditional_expression {
    my ($self, $condition) = @_;
    push(@{$self->{-conditions}}, $condition);
}

sub push_ordering {
    my ($self, $ordering) = @_;
    push(@{$self->{-orderings}}, $ordering);
}

sub unshift_ordering {
    my ($self, $ordering) = @_;
    unshift(@{$self->{-orderings}}, $ordering);
}

sub set_limit {
    my ($self, $limit) = @_;
    $self->{-limit} = $limit;
}

sub set_distinct {
    my ($self) = @_;
    $self->{-distinct} = 1;
}

1;

=head1 Name

PROP::SQL

=head1 Description

This class relieves the user from the burden of manually building up a
SQL statement by providing methods that allow the addition of
components of a query, and a stringify method that builds the
statement string from all of the constituent components.

The PROP::SQL class is actually an abstract class that should not
be instantiated directly.  Rather, create instances of
PROP::SQL::* classes, e.g. PPO::SQL::Select.

In fact, users will rarely have need of this class hierarchy at all.
Mostly, it is used internal to other classes to do the work of
manipulating objects and relationships.  It is, however, available to
users who wish to construct statements that do not have a nice analog
within the object oriented framework.

=head1 Synopsis

 $stmt = PROP::SQL::Select->new();

 $stmt->add_table('Foo');
 $stmt->push_field('foo');
 $stmt->push_field('bar');
 $stmt->push_conditional_expression('bar > ?');
 $stmt->push_ordering('bar');
 $stmt->set_limit(5000);

 # Where $dbh was obtained from DBI->connect(...)
 $sth = $dbh->prepare($stmt->stringify());
 $sth->execute(3);

=head1 Methods

Note: some of these methods only make sense for certain types of
statements.

=over

=item new

 $stmt = PROP::SQL::Select->new()

This method is the base constructor for all subclasses of
PROP::SQL, and as such is never called directly...  Rather, this
constructor is invoked via a subclass, as illustrated.

=item stringify

 $stmt->stringify()

This method builds and returns the statement string that results from
all of the things specified in the query.

=item add_table

 $stmt->add_table($name);

This method adds a table by the name of $name to the statment.

=item push_field

 $stmt->push_field($name)

This method adds a field by the name of $name to the list of fields
specified by the statement, adding it to the end of the list.

=item unshift_field

 $stmt->unshift_field($name)

This method adds a field by the name of $name to the list of fields
specified by the statement, adding it to the beginning of the list.

=item push_conditional_expression

 $stmt->push_conditional_expression($name)

This method adds a condition by the name of $name to the list of
conditions specified by the statement, adding it to the end of the
list.

=item push_ordering

 $stmt->push_ordering($name)

This method adds a ordering by the name of $name to the list of
orderings specified by the statement, adding it to the end of the
list.

=item unshift_ordering

 $stmt->unshift_ordering($name)

This method adds a ordering by the name of $name to the list of
orderings specified by the statement, adding it to the beginning of
the list.

=item set_limit

 $stmt->set_limit($value);

This method sets the limit specified by the statement.

=item set_distinct

 $stmt->set_distinct()

Invocation of this method indicates that you are only intersted in the
distinct results of this statement.  This probably only makes sense in
the context of a select statement, so maybe it ought to live in the
Select subclass.

=back

=head1 Author

Andrew Gibbs (awgibbs@awgibbs.com,andrew.gibbs@nist.gov)

=head1 Legalese

This software was developed at the National Institute of Standards and
Technology by employees of the Federal Government in the course of
their official duties. Pursuant to title 17 Section 105 of the United
States Code this software is not subject to copyright protection and
is in the public domain. PROP is an experimental system. NIST
assumes no responsibility whatsoever for its use by other parties, and
makes no guarantees, expressed or implied, about its quality,
reliability, or any other characteristic. We would appreciate
acknowledgement if the software is used.  This software can be
redistributed and/or modified freely provided that any derivative
works bear some notice that they are derived from it, and any modified
versions bear some notice that they have been modified.
