DBD::iPod::parser - parse iPod SQL queries


DBD-iPod documentation Contained in the DBD-iPod distribution.

Index


Code Index:

NAME

Top

DBD::iPod::parser - parse iPod SQL queries

SYNOPSIS

Top

Go away.

DESCRIPTION

Top

Shamelessly copy/pasted from Darren Chamberlain's DBD::Google::parser. Darren says:

"This package needs to subclass SQL::Parser, in order that the functions defined can be used. WIth SQL::Parser 1.005, the SELECT_CLAUSE method needs to be overridden.

"Jeff Zucker tells me that SQL::Parser 1.006 is coming out soon, and that it will support more functions and such. There might need to be some logic in here to ensure that an incompatible version of SQL::Parser is not being used."

AUTHOR

Top

Author <allenday@ucla.edu>

SEE ALSO

Top

SQL::Parser, DBD::Google::parser.

COPYRIGHT AND LICENSE

Top

APPENDIX

Top

The rest of the documentation details each of the object methods. Internal methods are usually preceded with a '_'. Methods are in alphabetical order for the most part.


DBD-iPod documentation Contained in the DBD-iPod distribution.

package DBD::iPod::parser;
use strict;
use base qw(SQL::Parser);
our $VERSION = '0.01';

use vars qw($FIELD_RE $FUNC_RE);

use Carp qw(carp);
use Data::Dumper;
use SQL::Parser;

# Package-scoped variables
# These are not lexicals so that they can be used in tests
$FIELD_RE = '[a-zA-Z][a-zA-Z0-9_]';
$FUNC_RE  = qr/$FIELD_RE*/; # methods?
$FIELD_RE = qr/$FIELD_RE*/;

my @default_columns = sort qw(
                              bitrate
                              fdesc
                              stoptime
                              songs
                              time
                              srate
                              rating
                              cdnum
                              cds
                              playcount
                              starttime
                              id
                              prerating
                              volume
                              songnum
                              path
                              genre
                              filesize
                              artist
                              album
                              comment
                              title
                              uniq
                             );

my %allowed_columns = map { $_ => 1 } @default_columns;
for my $dc (@default_columns) {
  $dc =~ s/([A-Z])/_\l$1/g;
  $allowed_columns{$dc} = 1;
}

# All functions are passed two items: the Net::Google::Search
# instanace and the text to be fiddled with.
my %functions = (
    'default'       => sub { $_[1]                  },
    'count'         => \&count,
);
$functions{''} = $functions{'default'};

# ----------------------------------------------------------------------
# new(@stuff)
# 
# Override SQL::Parser's new method, but only so that default values
# can be used.
# ----------------------------------------------------------------------
sub new { return shift->SUPER::new("iPod", @_) }

# ----------------------------------------------------------------------
# SELECT_CLAUSE($sql)
#
# Parses the SELECT portion of $sql, which contains only the pieces
# between SELECT and WHERE.  Understands the following syntax:
#
#   field
#
#   field AS alias
#
#   field AS "alias"
#
#   function(field)
#
#   function(field) AS alias
#
#   function(field) AS "alias"
#
# ----------------------------------------------------------------------
sub SELECT_CLAUSE {
  my ($self, $sql) = @_;
  #warn "Got: \$sql => '$sql'\n";
  my ($columns, $limit, @columns, @limit, $where, $parsed);

  # Internal data structures, given shorter names
  my $column_names =  $self->{'struct'}->{'column_names'}     = [ ];
  my $ORG_NAME     =  $self->{'struct'}->{'ORG_NAME'}         = { };
  my $functions    =  $self->{'struct'}->{'column_functions'} = { };
  my $aliases      =  $self->{'struct'}->{'column_aliases'}   = { };
  my $errstr       = \$self->{'struct'}->{'errstr'};

  # columns
  while ($sql =~ /\G

                                                # Field name, including possible function
                                                (?:
                                                    ($FUNC_RE\s*\([^)]+\))    # $1 => function
                                                |
                                                    ($FIELD_RE)               # $2 => field name
                                                | (\*)                      # $3 => '*'
                                                )

                                                # possible alias
                                                (?:
                                                        \s+
                                                        [aA][sS]
                                                        \s+
                                                        (['"]?)                 # $4 => possibly quoted
                                                            \s*
                                                            ($FIELD_RE)           # $5 => alias (no spaces allowed!)
                                                            \s*
                                                        \4?
                                                )?
                                                \s*
                                                ,?
                                                \s*
                                              /xsmg) {
    my $alias = $5 || "";
    my $function = $1 || "";

    #warn "\$function => '$function'\n\$alias => '$alias'\n";

    if (defined $3) {
      # SELECT * -> expanded to all column names
      my $df = $functions{'default'};
      for (@default_columns) {
        my $uc_ = uc $_;

        push @$column_names   => $_  ;
        $ORG_NAME->{  $uc_ }  =  $_  ;
        $functions->{ $uc_ }  =  $df ;
        $aliases->{   $uc_ }  =  $_  ;
      }
    }
    elsif ($function) {
      # SELECT foo(bar)
      my $original = $function;
      $original =~ /($FUNC_RE)\s*\((.*?)\)/;

      # XXX $n here might contains arguments; needs to be
      # passed to String::Shellquote to extract tokens
      my ($f, $n) = ($1, $2);
      $n =~ s/(^\s*|\s*$)//g;
      $f = "" unless defined $f;

      unless ($allowed_columns{$n}) {
        $$errstr = "Unknown column name '$n'";
        return;
      }

      # Possible cases include:
      #   1. No function defined
      #   2. Function defined that we know about
      if (defined $functions{$f}) {
        # Common case:
        #
        #   SELECT html_strip(title) FROM google ...
        #
        # A pre-defined function.
        $f = $functions{$f};
      }
      else {
        # No function:
        #
        #   SELECT title FROM google ...
        $f = $functions{'default'};
      }

      my $uc = uc $n;
      push @$column_names, $n;
      $ORG_NAME->{  $uc } = $n;
      $functions->{ $uc } = $f;
      $aliases->{   $uc } = $alias ? $alias : $n;
    }
    elsif (defined $2) {
      my $lc = lc $2;
      my $uc = uc $2;
      if ($allowed_columns{$lc}) {
        push @$column_names, $lc;
        $ORG_NAME->{  $uc } = $lc;
        $functions->{ $uc } = $functions{'default'};
        $aliases->{   $uc } = $alias ? $alias : $lc;
      } else {
        $$errstr = "Unknown column name '$2'";
        return;
      }
    }
  }

  1;
}

1;

__END__