ActivePerl Documentation
|
NAMESQL::Statement - SQL parsing and processing engine
SUPPORTED PLATFORMS
SYNOPSIS
require SQL::Statement;
# Create a parser
my($parser) = SQL::Statement->new('Ansi');
# Parse an SQL statement
$@ = '';
my ($stmt) = eval {
SQL::Statement->new("SELECT id, name FROM foo WHERE id > 1",
$parser);
};
if ($@) {
die "Cannot parse statement: $@";
}
# Query the list of result columns;
my $numColums = $stmt->columns(); # Scalar context
my @columns = $stmt->columns(); # Array context
# @columns now contains SQL::Statement::Column instances
# Likewise, query the tables being used in the statement:
my $numTables = $stmt->tables(); # Scalar context
my @tables = $stmt->tables(); # Array context
# @tables now contains SQL::Statement::Table instances
# Query the WHERE clause; this will retrieve an
# SQL::Statement::Op instance
my $where = $stmt->where();
# Evaluate the WHERE clause with concrete data, represented
# by an SQL::Eval object
my $result = $stmt->eval_where($eval);
# Execute a statement:
$stmt->execute($data, $params);
DESCRIPTIONFor installing the module, see INSTALLATION below. The SQL::Statement module implements a small, abstract SQL engine. This module is not usefull itself, but as a base class for deriving concrete SQL engines. The implementation is designed to work fine with the DBI driver DBD::CSV, thus probably not so well suited for a larger environment, but I'd hope it is extendable without too much problems. By parsing an SQL query you create an SQL::Statement instance. This instance offers methods for retrieving syntax, for WHERE clause and statement evaluation.
Creating a parser objectWhat's accepted as valid SQL, depends on the parser object. There is a set of so-called features that the parsers may have or not. Usually you start with a builtin parser:
my $parser = SQL::Parser->new($name, [ \%attr ]);
Currently two parsers are builtin: The Ansi parser implements a proper subset of ANSI SQL. (At least I hope so. :-) The SQL::Statement parser is used by the DBD:CSV driver. You can query or set individual features. Currently available are:
To enable or disable a feature, for example select.join, use the following:
# Enable feature
$parser->feature("select", "join", 1);
# Disable feature
$parser->feature("select", "join", 0);
Of course you can query features:
# Query feature
my $haveSelectJoin = $parser->feature("select", "join");
The
$parser = SQL::Statement->new('Ansi',
{ 'create' => { 'type_text' => 1,
'type_real' => 1,
'type_blob' => 1 },
'select' => { 'join' => 0 }});
Parsing a queryA statement can be parsed with
my $stmt = SQL::Statement->new($query, $parser);
In case of syntax errors or other problems, the method throws a Perl exception. Thus, if you want to catch exceptions, the above becomes
$@ = '';
my $stmt = eval { SQL::Statement->new($query, $parser) };
if ($@) { print "An error occurred: $@"; }
The accepted SQL syntax is restricted, though easily extendable. See SQL syntax below. See Creating a parser object above.
Retrieving query informationThe following methods can be used to obtain information about a query:
To illustrate the above, consider the following WHERE clause:
WHERE NOT (id > 2 AND name = 'joe') OR name IS NULL
We can represent this clause by the following tree:
(id > 2) (name = 'joe')
\ /
NOT AND
\ (name IS NULL)
\ /
OR
Thus the WHERE clause would return an SQL::Statement::Op instance with
the The Of course there's a ready-for-use method for WHERE clause evaluation:
Evaluating a WHERE clauseThe WHERE clause evaluation depends on an object being used for fetching parameter and column values. Usually this can be an SQL::Eval object, but in fact it can be any object that supplies the methods
$val = $eval->param($paramNum);
$val = $eval->column($table, $column);
See the SQL::Eval manpage for a detailed description of these methods. Once you have such an object, you can call a
$match = $stmt->eval_where($eval);
Evaluating queriesSo far all methods have been concrete. However, the interface for
executing and evaluating queries is abstract. That means, for using
them you have to derive a subclass from SQL::Statement that implements
at least certain missing methods and/or overwrites others. See the
Something that all methods have in common is that they simply throw a Perl exception in case of errors.
SQL syntaxThe SQL::Statement module is far away from ANSI SQL or something similar, it is designed for implementing the DBD::CSV module. See the DBD::CSV(3) manpage. I do not want to give a formal grammar here, more an informal description: Read the statement definition in sql_yacc.y, if you need something precise. The main lexical elements of the grammar are:
What it offers is the following:
CREATEThis is the CREATE TABLE command:
CREATE TABLE $table ( $col1 $type1, ..., $colN $typeN,
[ PRIMARY KEY ($col1, ... $colM) ] )
The column names are $col1, ... $colN. The column types can be
DROPVery simple:
DROP TABLE $table
INSERTThis can be
INSERT INTO $table [ ( $col1, ..., $colN ) ]
VALUES ( $val1, ... $valN )
DELETE
DELETE FROM $table [ WHERE $where_clause ]
See SELECT below for a decsription of $where_clause
UPDATE
UPDATE $table SET $col1 = $val1, ... $colN = $valN
[ WHERE $where_clause ]
See SELECT below for a decsription of $where_clause
SELECT
SELECT [DISTINCT] $col1, ... $colN FROM $table
[ WHERE $where_clause ] [ ORDER BY $ocol1, ... $ocolM ]
The $where_clause is based on boolean expressions of the form $val1 $op $val2, with $op being one of '=', '<>', '>', '<', '>=', '<=', 'LIKE', 'CLIKE' or IS. You may use OR, AND and brackets to combine such boolean expressions or NOT to negate them.
INSTALLATIONLike most other Perl modules, you simply do a
perl Makefile.PL
make (nmake or dmake, if you are using Win32)
make test (Let me know, if any tests fail)
make install
Known problems are:
INTERNALSInternally the module is splitted into three parts:
Perl-independent C partThis part, contained in the files You probably ask, why Perl independence? Well, first of all, I think this is a valuable target in itself. But the main reason was the impossibility to use the Perl headers inside bison generated code. The Perl headers export almost the complete Yacc interface to XS, for whatever reason, thus redefining constants and structures created by your own bison code. :-(
Perl-dependent C partThis is contained in
Perl partBesides some stub functions for retrieving statement data, this is mainly the query processing with the exception of WHERE clause evaluation.
The sql_stmt_t structureThis structure is designed for optimal performance. A typical query
will be parsed with only 4 or 5 The statement stores its tokens in the values array. The array elements
are of type sql_val_t, a union, that can represent the most interesting
tokens; for example integers and reals are stored in the data.i and
data.d parts of the union, strings are stored in the data.str part,
columns in the data.col part and so on. Arrays are allocated in chunks
of 64 elements, thus a single The sql_stmt_t structure contains other arrays: columns, tables,
rowvals, order, ... representing the data returned by the columns(),
tables(), Arrays are initialized with the _InitArray call in SQL_Statement_Prepare and deallocated with _DestroyArray in SQL_Statement_Destroy. Array elements are obtained by calling _AllocData, which returns an index. The number -1 is used for errors or as a NULL value.
The WHERE clause evaluationA WHERE clause is evaluated by calling SQL_Statement_EvalWhere(). This function is in the Perl independent part, but it needs the possibility to retrieve data from the Perl part, for example column or parameter values. These values are retrieved via callbacks, stored in the sql_eval_t structure. The field stmt->evalData points to such a structure. Of course the calling method can extend the sql_eval_t structure (like eval_where in Statement.xs does) to include private data not used by SQL_Statement_EvalWhere.
FeaturesDifferent parsers are implemented via the sql_parser_t structure. This is mainly a set of yes/no flags. If you'd like to add features, do the following: First of all, extend the sql_parser_t structure. If your feature is part of a certain statement, place it into the statements section, for example ``select.join''. Otherwise choose a section like ``misc'' or ``general''. (There's no particular for the section design, but structure never hurts.) Second, add your feature to sql_yacc.y. If your feature needs to extend the lexer, do it like this:
if (FEATURE(misc, myfeature) {
/* Scan your new symbols */
...
}
See the BOOL symbol as an example. If you need to extend the parser, do it like this:
my_new_rule:
/* NULL, old behaviour, doesn't use my feature */
| my_feature
{ YFEATURE(misc, myfeature); }
;
Thus all parsers not having FEATURE(misc, myfeature) set will produce a parse error here. Again, see the BOOL symbol for an example. Third thing is to extend the builtin parsers. If they support your feature, add a 1, otherwise a 0. Currently there are two builtin parsers: The ansiParser in sql_yacc.y and the sqlEvalParser in Statement.xs. Finally add support for your feature to the
MULTITHREADINGThe complete module code is reentrant. In particular the parser is
created with Statement handles cannot be shared among threads, at least not, if you don't grant serialized access. Per-thread handles are always safe.
AUTHOR AND COPYRIGHTThis module is Copyright (C) 1998 by
Jochen Wiedmann
Am Eisteich 9
72555 Metzingen
Germany
Email: joe@ispsoft.de
Phone: +49 7123 14887
All rights reserved. You may distribute this module under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.
SEE ALSODBI(3), the DBD::CSV(3) manpage
|