Back to documentation
#!/usr/bin/env perl
package ysql;
our $VERSION = '0.038';
# ABSTRACT: Query SQL databases in a Yertl workflow

use ETL::Yertl;
use Pod::Usage::Return qw( pod2usage );
use Getopt::Long qw( GetOptionsFromArray :config pass_through );
use ETL::Yertl::Command::ysql;

$|++; # no buffering

sub main {
    my ( $class, @argv ) = @_;
    my %opt;
    GetOptionsFromArray( \@argv, \%opt,
        'help|h',
        'version',
        'dsn=s',
    );
    return pod2usage(0) if $opt{help};
    if ( $opt{version} ) {
        print "ysql version $ysql::VERSION (Perl $^V)\n";
        return 0;
    }

    eval {
        ETL::Yertl::Command::ysql->main( @argv, \%opt );
    };
    if ( $@ ) {
        # SQL errors do not need usage information
        # XXX: We need to have some kind of error return message
        if ( $@ =~ /^SQL error/ || $@ =~ /^Could not connect/ ) {
            print STDERR "ERROR: $@";
            return 1;
        }
        return pod2usage( "ERROR: $@" );
    }
    return 0;
}

exit __PACKAGE__->main( @ARGV ) unless caller(0);

__END__

=head1 SYNOPSIS

    ysql [<db_name> | --dsn <dsn>] [<query> | <query_name>] [<value>...]

    ysql <db_name> --select <table> [--where <clause>] [--order-by <clause>]
    ysql <db_name> --insert <table>
    ysql <db_name> --delete <table> [--where <clause>]
    ysql <db_name> --count <table> [--where <clause>]

    ysql <db_name> --save <query_name> <query>
    ysql <db_name> --edit <query_name>

    ysql --config <db_name> [--driver <driver>] [--database <database] [--host <host>]
        [--port <port>] [--user <user>] [--password <password>]
    ysql --config <db_name> [--dsn] <dsn> [--user <user>] [--password <password>]
    ysql --config [<db_name>]

    ysql -h|--help|--version

=head1 DESCRIPTION

This program queries a SQL database and prints the result as YAML.

=head2 Reading

Run a query to read data from the database. C<query> is either the name of a saved
query, or SQL (usually a C<SELECT>). C<value> is used to fill in C<?> placeholders.

    $ ysql db_name 'SELECT * FROM person'
    ---
    name: Hazel "Hank" Murphy
    email: captain@example.com
    dept: Captain
    ---
    name: Quinn Quinlan
    email: dr.quinn@example.com
    dept: Science

    $ ysql db_name 'SELECT * FROM person WHERE dept = ?' Science
    ---
    name: Quinn Quinlan
    email: dr.quinn@example.com
    dept: Science

=head2 Writing

If there are documents to read on STDIN, the C<query> will be executed once for
each document read. In order to map keys in the YAML document to fields in the
SQL query, C<ysql> supports named placeholders, which begin with C<$.>. For
example:

    # input.yml
    ---
    name: Stormy Waters
    email: stormy@example.com
    profile:
        dept: Pilot
    ---
    name: Debbie Dupree
    email: debbie@example.com
    profile:
        dept: Wildlife

    $ ysql db_name 'INSERT INTO person ( name, email, dept ) \
                    VALUES ( $.name, $.email, $.profile.dept )' \
                    < input.yml

=head2 Query Helpers

For common, simple SQL queries, there are some helper options that will
help generate the SQL for you.

Instead of C<SELECT * FROM person>, we can use the C<--select> helper:

    $ ysql db_name --select person

And we can combine it with the C<--where> and C<--order-by> helpers:

    $ ysql db_name --select person --where 'dept = "Science"' --order-by name

Instead of C<SELECT COUNT(*) FROM person>, we can use the C<--count> helper,
which also works with the C<--where> helper:

    $ ysql db_name --count person
    $ ysql db_name --count person --where 'dept = "Science"'

And for simple inserts, we can use the C<--insert> helper:

    # input.yml
    ---
    name: Hesh Hipplewhite
    email: hesh@example.com
    dept: Engineering
    ---
    name: Jodene Sparks
    email: overlord@example.com
    dept: Communication

    $ ysql db_name --insert person < input.yml

There is a C<--delete> helper as well, which accepts the C<--where> option:

    $ ysql db_name --delete person --where 'dept = "Wildlife"'

=head1 ARGUMENTS

=head2 db_name

The saved database name to use. Add and edit databases using the C<--config> option.

=head2 query

The SQL query to run against the database.

=head2 query_name

The name of a saved query. Add and edit saved queries using the C<--save> option.

=head1 OPTIONS

=head2 --select <table>

Generate a C<SELECT * FROM table> query for the given C<table>. Also accepts
the C<--where> and C<--order-by> options.

=head2 --insert <table>

Generate an C<INSERT INTO table ( fields ) VALUES ( values )> query for the
given C<table>. For each document read on STDIN, the correct fields and values
will be used.

=head2 --delete <table>

Generate a C<DELETE FROM table> query for the given C<table>. Also accepts
the C<--where> option.

=head2 --where <clause>

Add a C<WHERE> clause to a C<--select> query.

=head2 --order-by <clause> | --sort <clause>

Add an C<ORDER BY> clause to a C<--select> query.

=head2 --config

View, add, and edit database configuration.

=head2 --save <query_name>

Save a query to run later.

=head2 --edit <query_name>

Edit the named query in your text editor (taken from the C<EDITOR> environment
variable).

=head2 --driver <driver>

The database driver to use, corresponding to a DBD::* module. Some examples: C<SQLite>,
C<mysql>, C<Pg>.

=head2 --database <database>

The name of the database. For SQLite, the name of the database file.

=head2 --host <host>

The hostname to connect to. Optional.

=head2 --port <port>

The port to connect to. Optional.

=head2 --user <user>

The user to connect as. Optional.

=head2 --password <password>

The password to connect with. Optional. This is stored in plain text, so beware.

=head2 --dsn <dsn>

The L<DBI> Data Source Name. A string that DBI uses to connect to the database.

Some examples:

    # A SQLite database in "test.db"
    dbi:SQLite:test.db

    # A MySQL database named "example"
    dbi:mysql:example

    # A Postgres database named "foo"
    dbi:Pg:foo

=head2 --drivers

List the available drivers

=head2 -h | --help

Show this help document.

=head2 --version

Print the current ysql and Perl versions.

=head1 ENVIRONMENT VARIABLES

=over 4

=item YERTL_FORMAT

Specify the default format Yertl uses between commands. Defaults to C<yaml>. Can be
set to C<json> for interoperability with other programs.

=back

=head1 SEE ALSO

=over 4

=item L<ETL::Yertl::Help::ysql> - Using ysql to work with SQL databases

=back