/usr/local/CPAN/PAB3/PAB3/DB/DataSet.pm
package PAB3::DB::DataSet;
# =============================================================================
# Perl Application Builder
# Module: PAB3::DB::DataSet (Alpha)
# Represents data retrieved using PAB3::DB.
# Use "perldoc PAB3::DB::DataSet" for documentation
# =============================================================================
use strict;
no strict 'subs';
use warnings;
use Carp ();
use PAB3::DB ();
use vars qw($VERSION);
BEGIN {
$VERSION = '1.0.0';
*append = \&insert;
*append_record = \&insert_record;
}
use constant {
# CommandType constants
ctQuery => 1,
ctTable => 2,
ctStoredProcedure => 3,
# State constants
dsSetKey => 4,
dsInsert => 3,
dsEdit => 2,
dsBrowse => 1,
};
# using variables instead of constants because it needs less of memory
# (perl/5.8.8)
our $IDA_DB = 0;
our $IDA_CMD_TEXT = 1;
our $IDA_CMD_TYPE = 2;
our $IDA_LIMIT = 3;
our $IDA_OFFSET = 4;
our $IDA_STATE = 5;
our $IDA_ROWCOUNT = 6;
our $IDA_ROWMAX = 7;
our $IDA_ERROR = 8;
our $IDA_RESULT = 9;
our $IDA_FILTER = 10;
our $IDA_POINTER = 11;
our $IDA_SAFEFIELDS = 12;
our $IDA_NAMES = 13;
our $IDA_OPTIONS = 14;
our $IDA_LAST_CMD = 15;
our $IDA_TABLE_INFO = 16;
our $IDA_SAVEPOINTER = 17;
our $IDA_PAB = 18;
our $IDA_OBJECTNAME = 19;
our $IDA_ENUMPOINTER = 20;
our $IDA_INITOFFSET = 21;
our $IDA_SEARCH = 22;
our $IDA_KEY_INFO = 23;
our $IDA_CMD_TEXT_PLAIN = 24;
our $IDA_LASTINDEX = 24;
our $IDA_OPT_CALCLIMIT = 1;
our $IDA_OPT_CALCROWS = 2;
our $DATA_MAX_SIZE = 128 * 1024;
our $LastError = '';
our @EXPORT_CT = qw(ctQuery ctTable ctStoredProcedure);
our @EXPORT_DS = qw(dsInsert dsEdit dsBrowse);
our @EXPORT_OK = ( @EXPORT_DS, @EXPORT_CT );
our %EXPORT_TAGS = (
'ct' => \@EXPORT_CT,
'ds' => \@EXPORT_DS,
'all' => \@EXPORT_OK,
);
require Exporter;
*import = \&Exporter::import;
1;
sub new {
my $proto = shift;
my $class = ref( $proto ) || $proto;
my $this = {
'__I#D#A__' => []
};
my %arg = @_;
if( ! $arg{'db'} || ref( $arg{'db'} ) !~ /PAB3::DB/ ) {
$LastError = 'Cannot create DataSet without a valid "PAB3::DB" handle';
return undef;
}
bless( $this, $class );
my $ida = $this->{'__I#D#A__'};
$ida->[$IDA_DB] = $arg{'db'};
$ida->[$IDA_PAB] = $arg{'pab'};
$ida->[$IDA_OBJECTNAME] = $arg{'object_name'};
$ida->[$IDA_STATE] = dsBrowse;
$ida->[$IDA_ROWCOUNT] = 0;
$ida->[$IDA_ROWMAX] = 0;
$ida->[$IDA_INITOFFSET] = 0;
$ida->[$IDA_OFFSET] = 0;
$ida->[$IDA_POINTER] = -1;
$ida->[$IDA_ENUMPOINTER] = -1;
$ida->[$IDA_SAFEFIELDS] = {};
$ida->[$IDA_LAST_CMD] = '';
if( $arg{'limit'} ) {
$ida->[$IDA_LIMIT] = $arg{'limit'};
$ida->[$IDA_OPTIONS] = 0;
}
else {
$ida->[$IDA_LIMIT] = 1;
$ida->[$IDA_OPTIONS] = $IDA_OPT_CALCLIMIT;
}
if( $arg{'filter'} ) {
$ida->[$IDA_FILTER] = $this->_format_sql( $arg{'filter'} );
}
$this->command_type( $arg{'command_type'} ) if $arg{'command_type'};
$this->command_text( $arg{'command_text'} ) if $arg{'command_text'};
return $this;
}
#sub DESTROY {
# my $this = shift or return;
# &close( $this );
#}
sub open {
my( $this ) = @_;
my( $ida, $sql, $sqlrows, $db, $res, @row );
$ida = $this->{'__I#D#A__'};
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
return 1 if $ida->[$IDA_POINTER] >= $ida->[$IDA_OFFSET] && $ida->[$IDA_RESULT];
$ida->[$IDA_OFFSET] = $ida->[$IDA_INITOFFSET];
$ida->[$IDA_OPTIONS] |= $IDA_OPT_CALCROWS;
( $sqlrows, $sql ) = $this->_prepare_query() or return 0;
$ida->[$IDA_OPTIONS] ^= $IDA_OPT_CALCROWS;
$db = $ida->[$IDA_DB];
$ida->[$IDA_RESULT] = $db->query( $sql ) or return 0;
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
$ida->[$IDA_NAMES] = [ $ida->[$IDA_RESULT]->fetch_names() ];
if( $sqlrows ) {
$res = $db->query( $sqlrows ) or return 0;
( $ida->[$IDA_ROWMAX] ) = $res->fetch_row();
}
else {
$ida->[$IDA_ROWMAX] = $ida->[$IDA_ROWCOUNT];
}
@row = $ida->[$IDA_RESULT]->fetch_row() or return 0;
$this->_apply( \@row );
$ida->[$IDA_POINTER] = $ida->[$IDA_OFFSET];
# calculate buffer
if( ( $ida->[$IDA_OPTIONS] & $IDA_OPT_CALCLIMIT ) != 0 ) {
my @l = $ida->[$IDA_RESULT]->fetch_lengths();
my $len = 0;
$len += $_ foreach @l;
$ida->[$IDA_LIMIT] = int( $DATA_MAX_SIZE / ( $len * 2 ) );
$ida->[$IDA_LIMIT] = 1 if $ida->[$IDA_LIMIT] < 1;
}
return 1;
}
sub close {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
my $db = $ida->[$IDA_DB];
$ida->[$IDA_RESULT] = undef;
$ida->[$IDA_POINTER] = -1;
$ida->[$IDA_ENUMPOINTER] = -1;
$ida->[$IDA_NAMES] = undef;
$ida->[$IDA_ROWCOUNT] = 0;
$ida->[$IDA_INITOFFSET] = 0;
$ida->[$IDA_OFFSET] = 0;
return 1;
}
sub first {
my( $this ) = @_;
my( $ida, $sql, $db, $resid, @row );
$ida = $this->{'__I#D#A__'};
if( $ida->[$IDA_ROWCOUNT] <= 0 ) {
return $this->_set_error( 'No data in dataset' );
}
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
return 1 if $ida->[$IDA_POINTER] == 0;
$db = $ida->[$IDA_DB];
if( $ida->[$IDA_POINTER] > 0 ) {
if( $ida->[$IDA_POINTER] < $ida->[$IDA_ROWCOUNT] ) {
$ida->[$IDA_RESULT]->row_seek( 0 ) or return 0;
}
else {
$ida->[$IDA_OFFSET] = 0;
$sql = $this->_prepare_query() or return 0;
$ida->[$IDA_RESULT] = $db->query( $sql ) or return 0;
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
}
}
@row = $ida->[$IDA_RESULT]->fetch_row() or return 0;
$this->_apply( \@row );
$ida->[$IDA_POINTER] = 0;
return 1;
}
sub last {
my( $this ) = @_;
my( $ida, $sql, $db, $resid, @row );
$ida = $this->{'__I#D#A__'};
if( $ida->[$IDA_ROWCOUNT] <= 0 ) {
return $this->_set_error( 'No data in dataset' );
}
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
return 1 if $ida->[$IDA_POINTER] == $ida->[$IDA_ROWMAX] - 1;
$db = $ida->[$IDA_DB];
if( $ida->[$IDA_POINTER] < $ida->[$IDA_ROWMAX] - $ida->[$IDA_ROWCOUNT] - 1 ) {
$ida->[$IDA_OFFSET] = $ida->[$IDA_ROWMAX] - $ida->[$IDA_LIMIT] - 1;
$sql = $this->_prepare_query() or return 0;
$ida->[$IDA_RESULT] = $db->query( $sql ) or return 0;
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
}
$ida->[$IDA_RESULT]->row_seek( $ida->[$IDA_ROWCOUNT] - 1 ) or return 0;
@row = $ida->[$IDA_RESULT]->fetch_row();
$this->_apply( \@row );
$ida->[$IDA_POINTER] = $ida->[$IDA_ROWMAX] - 1;
return 1;
}
sub next {
my( $this ) = @_;
my( @row, $ida, $sql, $db );
$ida = $this->{'__I#D#A__'};
if( $ida->[$IDA_ROWCOUNT] <= 0 ) {
return $this->_set_error( 'No data in dataset' );
}
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
return 0 if $this->eof();
if( $ida->[$IDA_POINTER] == $ida->[$IDA_OFFSET] + $ida->[$IDA_ROWCOUNT] - 1 ) {
if( $ida->[$IDA_POINTER] == $ida->[$IDA_ROWMAX] - 1 ) {
return -1;
}
$db = $ida->[$IDA_DB];
$ida->[$IDA_OFFSET] += $ida->[$IDA_ROWCOUNT];
$sql = $this->_prepare_query() or return 0;
$ida->[$IDA_RESULT] = $db->query( $sql ) or return 0;
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
}
@row = $ida->[$IDA_RESULT]->fetch_row()
or return 0;
$ida->[$IDA_POINTER] ++;
$this->_apply( \@row );
return 1;
}
sub prior {
my( $this ) = @_;
my( @row, $ida, $sql, $db );
$ida = $this->{'__I#D#A__'};
if( $ida->[$IDA_ROWCOUNT] <= 0 ) {
return $this->_set_error( 'No data in dataset' );
}
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
$db = $ida->[$IDA_DB];
if( $ida->[$IDA_POINTER] == $ida->[$IDA_OFFSET] ) {
if( $ida->[$IDA_POINTER] == 0 ) {
return -1;
}
$ida->[$IDA_OFFSET] -= $ida->[$IDA_LIMIT];
$ida->[$IDA_OFFSET] = 0 if $ida->[$IDA_OFFSET] < 0;
$sql = $this->_prepare_query() or return 0;
$ida->[$IDA_RESULT] = $db->query( $sql ) or return 0;
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
}
else {
$db->row_seek(
$ida->[$IDA_RESULT],
$ida->[$IDA_POINTER] - $ida->[$IDA_OFFSET] - 1
) or return 0;
}
@row = $ida->[$IDA_RESULT]->fetch_row()
or return 0;
$ida->[$IDA_POINTER] --;
$this->_apply( \@row );
return 1;
}
sub move_by {
my( $this, $offset ) = @_;
my( $ida, $db, $sql );
$ida = $this->{'__I#D#A__'};
if( $ida->[$IDA_ROWCOUNT] <= 0 ) {
return $this->_set_error( 'No data in dataset' );
}
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
$db = $ida->[$IDA_DB];
$offset = $ida->[$IDA_POINTER] + $offset;
$offset = 0 if $offset < 0;
$offset = $ida->[$IDA_ROWMAX] - 1 if $offset >= $ida->[$IDA_ROWMAX];
return 1 if $offset == $ida->[$IDA_POINTER];
if( $offset < $ida->[$IDA_OFFSET]
|| $offset >= $ida->[$IDA_OFFSET] + $ida->[$IDA_LIMIT]
) {
$ida->[$IDA_OFFSET] = $offset - ( $offset % $ida->[$IDA_LIMIT] );
$ida->[$IDA_OFFSET] = 0 if $ida->[$IDA_OFFSET] < 0;
$sql = $this->_prepare_query() or return 0;
$ida->[$IDA_RESULT] = $db->query( $sql ) or return 0;
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
if( $offset != $ida->[$IDA_OFFSET] ) {
$ida->[$IDA_RESULT]->row_seek( $offset - $ida->[$IDA_OFFSET] )
or return 0;
}
}
else {
$ida->[$IDA_RESULT]->row_seek( $offset - $ida->[$IDA_OFFSET] )
or return 0;
}
$ida->[$IDA_POINTER] = $offset;
return 1;
}
sub edit {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
$this->_verify_dataset( 1 ) or return 0;
return 1 if $ida->[$IDA_STATE] != dsBrowse;
if( $ida->[$IDA_ROWCOUNT] <= 0 ) {
return $this->_set_error( 'No data to edit' );
}
$ida->[$IDA_STATE] = dsEdit;
return 1;
}
sub insert {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
$this->_verify_dataset( 1 ) or return 0;
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
$ida->[$IDA_SAVEPOINTER] = $ida->[$IDA_POINTER];
$ida->[$IDA_POINTER] = $ida->[$IDA_ROWMAX];
$ida->[$IDA_STATE] = dsInsert;
foreach ( @{$ida->[$IDA_NAMES]} ) {
delete $this->{ $_ };
}
$ida->[$IDA_SAFEFIELDS] = {};
return 1;
}
sub insert_record {
my( $this, $ida, $i, $names, $sfields );
$this = shift;
$ida = $this->{'__I#D#A__'};
$this->_verify_dataset( 1 ) or return 0;
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
$this->_table_info() or return 0;
$ida->[$IDA_SAVEPOINTER] = $ida->[$IDA_POINTER];
$ida->[$IDA_POINTER] = $ida->[$IDA_ROWMAX];
$ida->[$IDA_STATE] = dsInsert;
$ida->[$IDA_SAFEFIELDS] = {};
$names = $ida->[$IDA_NAMES];
$sfields = $ida->[$IDA_SAFEFIELDS];
for $i( 0 .. $#{$names} ) {
$sfields->{$names->{$i}} = $this->{$names->{$i}} = $_[$i];
}
return $this->post();
}
sub post {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
return 1 if $ida->[$IDA_STATE] == dsBrowse;
$this->_verify_dataset( 1 ) or return 0;
$this->_table_info() or return 0;
if( $ida->[$IDA_STATE] == dsEdit ) {
# update data
$this->_do_update() or return 0;
}
elsif( $ida->[$IDA_STATE] == dsInsert ) {
# insert data
$this->_do_insert() or return 0;
}
$ida->[$IDA_STATE] = dsBrowse;
return 1;
}
sub cancel {
my $this = shift;
my( $ida, $sfields, $num_fields, $i, $key );
$ida = $this->{'__I#D#A__'};
return 1 if $ida->[$IDA_STATE] == dsBrowse;
$this->_verify_dataset( 1 ) or return 0;
if( $ida->[$IDA_STATE] == dsInsert ) {
$ida->[$IDA_POINTER] = $ida->[$IDA_SAVEPOINTER];
}
$sfields = $ida->[$IDA_SAFEFIELDS];
$num_fields = scalar @{ $ida->[$IDA_NAMES] };
for $i( 0 .. $num_fields - 1 ) {
$key = $ida->[$IDA_NAMES][$i];
$this->{$key} = $sfields->{$key};
}
return 1;
}
sub delete {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
$this->_verify_dataset( 1 ) or return 0;
$this->_table_info() or return 0;
if( $ida->[$IDA_POINTER] > $ida->[$IDA_ROWMAX] ) {
return $this->setError( 'Nothing to delete from here' );
}
$this->_do_delete() or return 0;
$ida->[$IDA_ROWCOUNT] --;
$ida->[$IDA_ROWMAX] --;
$ida->[$IDA_STATE] = dsBrowse;
$this->next() or return 0;
$ida->[$IDA_POINTER] --;
return 1;
}
sub bof {
my( $this ) = @_;
my $ida = $this->{'__I#D#A__'};
return $ida->[$IDA_POINTER] <= $ida->[$IDA_INITOFFSET];
}
sub eof {
my( $this ) = @_;
my $ida = $this->{'__I#D#A__'};
if( ( $ida->[$IDA_OPTIONS] & $IDA_OPT_CALCLIMIT ) == 0 ) {
# using hard limit
return $ida->[$IDA_POINTER] - $ida->[$IDA_OFFSET] >= $ida->[$IDA_ROWCOUNT] - 1;
}
return $ida->[$IDA_POINTER] >= $ida->[$IDA_ROWMAX] - 1;
}
sub fill {
my $this = shift;
my( $ida, $i, $field, $ref );
$ida = $this->{'__I#D#A__'};
if( $ida->[$IDA_STATE] == dsBrowse ) {
return $this->setError( 'Cannot use "fill" in BrowseMode' );
}
$ref = ref( $_[0] );
if( $ref eq 'HASH' ) {
foreach( keys %{$_[0]} ) {
$this->{$_} = $_[0]->{$_};
}
}
elsif( $ref eq 'ARRAY' ) {
$i = 0;
foreach( @{ $_[0] } ) {
$field = $ida->[$IDA_NAMES][$i ++];
$this->{$field} = $_;
}
}
else {
$i = 0;
foreach( @_ ) {
$field = $ida->[$IDA_NAMES][$i ++];
$this->{$field} = $_;
}
}
#$ida->[$IDA_STATUS] = 0;
return 1;
}
sub refresh {
my( $this ) = @_;
return $this->_resync();
}
sub command_type {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
if( defined $_[0] ) {
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
$ida->[$IDA_CMD_TYPE] = $_[0] >= 1 && $_[0] <= 3 ? $_[0] : 1;
if( $ida->[$IDA_ROWCOUNT] ) {
$this->open() or return 0;
}
}
return $ida->[$IDA_CMD_TYPE];
}
sub command_text {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
if( defined $_[0] ) {
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
$ida->[$IDA_CMD_TEXT_PLAIN] = $_[0];
$ida->[$IDA_CMD_TEXT] = $this->_format_sql( $_[0] );
if( $ida->[$IDA_ROWCOUNT] ) {
$this->open() or return 0;
}
}
return $ida->[$IDA_CMD_TEXT];
}
sub record_count {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
return $ida->[$IDA_ROWMAX];
}
sub current_record {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
return $ida->[$IDA_POINTER];
}
sub fields {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
if( defined $_[0] ) {
return $ida->[$IDA_NAMES]->[$_[0]];
}
return @{$ida->[$IDA_NAMES]};
}
sub field_count {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
return scalar @{$ida->[$IDA_NAMES]};
}
sub filter {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
if( defined $_[0] ) {
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
$ida->[$IDA_FILTER] = $this->_format_sql( $_[0] );
# if( $ida->[$IDA_ROWCOUNT] ) {
# $this->open() or return 0;
# }
}
return $ida->[$IDA_FILTER];
}
sub limit {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
if( defined $_[0] ) {
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
# return $ida->[$IDA_LIMIT] if $ida->[$IDA_LIMIT] == $_[0];
$ida->[$IDA_LIMIT] = $_[0] > 0 ? $_[0] : 1;
if( ( $ida->[$IDA_OPTIONS] & $IDA_OPT_CALCLIMIT ) != 0 ) {
$ida->[$IDA_OPTIONS] ^= $IDA_OPT_CALCLIMIT;
}
# if( $ida->[$IDA_ROWCOUNT] ) {
# $this->open() or return 0;
# }
}
return $ida->[$IDA_LIMIT];
}
sub offset {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
if( defined $_[0] ) {
if( $ida->[$IDA_STATE] != dsBrowse ) {
$this->post() or return 0;
}
# return $ida->[$IDA_OFFSET] if $ida->[$IDA_OFFSET] == $_[0];
$ida->[$IDA_INITOFFSET] = $_[0];
# $ida->[$IDA_OFFSET] = $_[0];
# if( $ida->[$IDA_ROWCOUNT] ) {
# $this->open() or return 0;
# }
}
return $ida->[$IDA_OFFSET];
}
sub locate {
my( $this, $ida, $db, $i, $tmp, $res, $sqlrows, $oo );
$this = shift;
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
if( ! ref( $_[0] ) ) {
$ida->[$IDA_SEARCH] = $db->quote_id( $_[0] )
. ( defined $_[1] ? ' = ' . $db->quote( $_[1] ) : ' IS NULL' );
}
else {
$tmp = '';
$_[1] ||= [];
for $i( 0 .. $#{$_[0]} ) {
$tmp .= ' AND ' if $i;
$tmp .= $db->quote_id( $_[0]->[$i] )
. ( defined $_[1]->[$i] ? ' = ' . $db->quote( $_[1]->[$i] ) : ' IS NULL' );
}
$ida->[$IDA_SEARCH] = $tmp;
}
$oo = $ida->[$IDA_OFFSET];
$ida->[$IDA_OFFSET] = 0;
$ida->[$IDA_OPTIONS] |= $IDA_OPT_CALCROWS;
( $sqlrows, $tmp ) = $this->_prepare_query() or return 0;
$ida->[$IDA_OPTIONS] ^= $IDA_OPT_CALCROWS;
$res = $db->query( $tmp ) or return 0;
if( $res->num_rows() ) {
$ida->[$IDA_RESULT] = $res;
$ida->[$IDA_ROWCOUNT] = $res->num_rows();
$ida->[$IDA_NAMES] = [ $res->fetch_names() ];
if( $sqlrows ) {
$res = $db->query( $sqlrows ) or return 0;
( $ida->[$IDA_ROWMAX] ) = $res->fetch_row();
}
else {
$ida->[$IDA_ROWMAX] = $ida->[$IDA_ROWCOUNT];
}
my @row = $ida->[$IDA_RESULT]->fetch_row() or return 0;
$this->_apply( \@row );
$ida->[$IDA_POINTER] = $ida->[$IDA_OFFSET];
}
else {
$ida->[$IDA_OFFSET] = $oo;
return 0;
}
return 1;
}
sub find_key {
my( $this, $ida, $db, $i, $num_fields, $field, @pk, @uk, $tmp, $oo, $res,
$sqlrows );
$this = shift;
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
$this->_table_info() or return 0;
$num_fields = scalar @{ $ida->[$IDA_TABLE_INFO] };
for $i( 0 .. $num_fields - 1 ) {
$field = $ida->[$IDA_TABLE_INFO][$i];
if( $field->[$PAB3::DB::DB_FIELD_PRIKEY] ) {
push @pk, $field->[$PAB3::DB::DB_FIELD_NAME];
}
elsif( $field->[$PAB3::DB::DB_FIELD_UNIKEY] ) {
push @uk, $field->[$PAB3::DB::DB_FIELD_NAME];
}
}
if( @pk ) {
for $i( 0 .. $#pk ) {
$tmp .= ' AND ' if $i;
$tmp .= $db->quote_id( $pk[$i] )
. ( defined $_[$i] ? ' = ' . $db->quote( $_[$i] ) : ' IS NULL' );
}
$ida->[$IDA_SEARCH] = $tmp;
}
elsif( @uk ) {
for $i( 0 .. $#uk ) {
$tmp .= ' AND ' if $i;
$tmp .= $db->quote_id( $uk[$i] )
. ( defined $_[$i] ? ' = ' . $db->quote( $_[$i] ) : ' IS NULL' );
}
$ida->[$IDA_SEARCH] = $tmp;
}
else {
return 0;
}
$oo = $ida->[$IDA_OFFSET];
$ida->[$IDA_OFFSET] = 0;
$ida->[$IDA_OPTIONS] |= $IDA_OPT_CALCROWS;
( $sqlrows, $tmp ) = $this->_prepare_query() or return 0;
$ida->[$IDA_OPTIONS] ^= $IDA_OPT_CALCROWS;
$res = $db->query( $tmp ) or return 0;
if( $res->num_rows() ) {
$ida->[$IDA_RESULT] = $res;
$ida->[$IDA_ROWCOUNT] = $res->num_rows();
$ida->[$IDA_NAMES] = [ $res->fetch_names() ];
if( $sqlrows ) {
$res = $db->query( $sqlrows ) or return 0;
( $ida->[$IDA_ROWMAX] ) = $res->fetch_row();
}
else {
$ida->[$IDA_ROWMAX] = $ida->[$IDA_ROWCOUNT];
}
my @row = $ida->[$IDA_RESULT]->fetch_row() or return 0;
$this->_apply( \@row );
$ida->[$IDA_POINTER] = $ida->[$IDA_OFFSET];
}
else {
$ida->[$IDA_OFFSET] = $oo;
return 0;
}
return 1;
}
sub index_defs {
my( $this, $ida, $db, %index, $key, @index );
$this = shift;
$ida = $this->{'__I#D#A__'};
if( $ida->[$IDA_CMD_TYPE] != ctTable ) {
return $this->_set_error( 'Not available on a non TABLE dataset' );
}
$db = $ida->[$IDA_DB];
return $ida->[$IDA_KEY_INFO] if $ida->[$IDA_KEY_INFO];
foreach( $db->show_index( $ida->[$IDA_CMD_TEXT_PLAIN] ) ) {
$key = $_->[$PAB3::DB::DB_INDEX_NAME];
if( ! $index{$key} ) {
$index{$key} = {
'name' => $key,
'fields' => $_->[$PAB3::DB::DB_INDEX_COLUMN],
'type' => $_->[$PAB3::DB::DB_INDEX_TYPE]
};
push @index, $index{$key};
}
else {
$index{$key}->{'fields'} .= ';' . $_->[$PAB3::DB::DB_INDEX_COLUMN];
}
}
$ida->[$IDA_KEY_INFO] = \@index;
return $ida->[$IDA_KEY_INFO];
}
sub register_loop {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
# my( $loopid [, $objectname [, $clspab]] )
my $object = $_[1] || $ida->[$IDA_OBJECTNAME];
my $pab = $_[2] || $ida->[$IDA_PAB];
if( ! $_[0] || ! $object || ! $pab || ref( $pab ) !~ /PAB/ ) {
&Carp::croak( 'Usage: register_loop( $loopid [, $objectname [, $clspab]] )' );
}
$pab->register_loop( $_[0], 'next', PAB3::FUNC, undef, undef, $object )
or return $this->_set_error( $pab->error() );
return 1;
}
sub register_loop_enum {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
# my( $loopid [, $record_name [, $objectname [, $clspab]]] )
my $object = $_[2] || $ida->[$IDA_OBJECTNAME];
my $pab = $_[3] || $ida->[$IDA_PAB];
if( ! $_[0] || ! $object || ! $pab || ref( $pab ) !~ /PAB/ ) {
&Carp::croak(
'Usage: register_loop_enum( $loopid [ $recordname [, $objectname [, $clspab]]] )'
);
}
$pab->register_loop( $_[0], '_enum', PAB3::FUNC, $_[1], PAB3::SCALAR, $object )
or return $this->_set_error( $pab->error() );
return 1;
}
sub register_loop_mapped {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
# my( $loopid [, $record_name [, $objectname [, $clspab]]] )
my $object = $_[2] || $ida->[$IDA_OBJECTNAME];
my $pab = $_[3] || $ida->[$IDA_PAB];
if( ! $_[0] || ! $object || ! $pab || ref( $pab ) !~ /PAB/ ) {
&Carp::croak(
'Usage: register_loop_mapped( $loopid [, $record_name [, $objectname [, $clspab]]] )'
);
}
$pab->register_loop( $_[0], '_enum_mapped', PAB3::FUNC, $_[1], PAB3::SCALAR, $object )
or return $this->_set_error( $pab->error() );
$pab->add_hashmap( $_[0], $_[1], $ida->[$IDA_NAMES] )
or return $this->_set_error( $pab->error() );
return 1;
}
sub _enum {
my $this = shift;
my( $ida, $sql, $sqlrows, $db, $res, %row );
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
if( $ida->[$IDA_ENUMPOINTER] < $ida->[$IDA_OFFSET] ) {
if( $ida->[$IDA_OFFSET] == $ida->[$IDA_INITOFFSET] && $ida->[$IDA_RESULT] ) {
$ida->[$IDA_RESULT]->row_seek( 0 )
or return $this->_set_error( $db->error() );
%row = $ida->[$IDA_RESULT]->fetch_hash()
or return $this->_set_error( $db->error() );
$ida->[$IDA_ENUMPOINTER] = $ida->[$IDA_OFFSET];
return \%row;
}
$ida->[$IDA_OFFSET] = $ida->[$IDA_INITOFFSET];
$ida->[$IDA_OPTIONS] |= $IDA_OPT_CALCROWS;
( $sqlrows, $sql ) = $this->_prepare_query() or return 0;
$ida->[$IDA_OPTIONS] ^= $IDA_OPT_CALCROWS;
$ida->[$IDA_RESULT] = $db->query( $sql )
or return $this->_set_error( $db->error() );
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
$ida->[$IDA_NAMES] = [ $ida->[$IDA_RESULT]->fetch_names() ];
if( $sqlrows ) {
$res = $db->query( $sqlrows )
or return $this->_set_error( $db->error() );
( $ida->[$IDA_ROWMAX] ) = $res->fetch_row();
}
else {
$ida->[$IDA_ROWMAX] = $ida->[$IDA_ROWCOUNT];
}
%row = $ida->[$IDA_RESULT]->fetch_hash()
or return $this->_set_error( $db->error() );
$ida->[$IDA_ENUMPOINTER] = $ida->[$IDA_OFFSET];
# calculate buffer
if( ( $ida->[$IDA_OPTIONS] & $IDA_OPT_CALCLIMIT ) != 0 ) {
my @l = $ida->[$IDA_RESULT]->fetch_lengths();
my $len = 0;
$len += $_ foreach @l;
$ida->[$IDA_LIMIT] = int( $DATA_MAX_SIZE / ( $len * 2 ) );
$ida->[$IDA_LIMIT] = 1 if $ida->[$IDA_LIMIT] < 1;
}
return \%row;
}
else {
if( ( $ida->[$IDA_OPTIONS] & $IDA_OPT_CALCLIMIT ) == 0 ) {
goto resetenum
if $ida->[$IDA_ENUMPOINTER] - $ida->[$IDA_OFFSET] >= $ida->[$IDA_ROWCOUNT] - 1;
}
if( $ida->[$IDA_ENUMPOINTER] == $ida->[$IDA_OFFSET] + $ida->[$IDA_ROWCOUNT] - 1 ) {
goto resetenum
if $ida->[$IDA_ENUMPOINTER] >= $ida->[$IDA_ROWMAX] - 1;
$ida->[$IDA_OFFSET] += $ida->[$IDA_ROWCOUNT];
$sql = $this->_prepare_query() or return 0;
$ida->[$IDA_RESULT] = $db->query( $sql )
or return $this->_set_error( $db->error() );
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
}
%row = $ida->[$IDA_RESULT]->fetch_hash()
or return $this->_set_error( $db->error() );
$ida->[$IDA_ENUMPOINTER] ++;
return \%row;
}
resetenum:
$ida->[$IDA_ENUMPOINTER] = $ida->[$IDA_INITOFFSET];
return 0;
}
sub _enum_mapped {
my $this = shift;
my( $ida, $sql, $sqlrows, $db, $res, @row );
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
if( $ida->[$IDA_ENUMPOINTER] < $ida->[$IDA_OFFSET] ) {
if( $ida->[$IDA_OFFSET] == $ida->[$IDA_INITOFFSET] && $ida->[$IDA_RESULT] ) {
$ida->[$IDA_RESULT]->row_seek( 0 )
or return $this->_set_error( $db->error() );
@row = $ida->[$IDA_RESULT]->fetch_row()
or return $this->_set_error( $db->error() );
$ida->[$IDA_ENUMPOINTER] = $ida->[$IDA_OFFSET];
return \@row;
}
$ida->[$IDA_OFFSET] = $ida->[$IDA_INITOFFSET];
$ida->[$IDA_OPTIONS] |= $IDA_OPT_CALCROWS;
( $sqlrows, $sql ) = $this->_prepare_query() or return 0;
$ida->[$IDA_OPTIONS] ^= $IDA_OPT_CALCROWS;
$ida->[$IDA_RESULT] = $db->query( $sql )
or return $this->_set_error( $db->error() );
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
$ida->[$IDA_NAMES] = [ $ida->[$IDA_RESULT]->fetch_names() ];
if( $sqlrows ) {
$res = $db->query( $sqlrows )
or return $this->_set_error( $db->error() );
( $ida->[$IDA_ROWMAX] ) = $res->fetch_row();
}
else {
$ida->[$IDA_ROWMAX] = $ida->[$IDA_ROWCOUNT];
}
@row = $ida->[$IDA_RESULT]->fetch_row()
or return $this->_set_error( $db->error() );
$ida->[$IDA_ENUMPOINTER] = $ida->[$IDA_OFFSET];
# calculate buffer
if( ( $ida->[$IDA_OPTIONS] & $IDA_OPT_CALCLIMIT ) != 0 ) {
my @l = $ida->[$IDA_RESULT]->fetch_lengths();
my $len = 0;
$len += $_ foreach @l;
$ida->[$IDA_LIMIT] = int( $DATA_MAX_SIZE / ( $len * 2 ) );
$ida->[$IDA_LIMIT] = 1 if $ida->[$IDA_LIMIT] < 1;
}
return \@row;
}
else {
if( ( $ida->[$IDA_OPTIONS] & $IDA_OPT_CALCLIMIT ) == 0 ) {
goto resetenum
if $ida->[$IDA_ENUMPOINTER] - $ida->[$IDA_OFFSET] >= $ida->[$IDA_ROWCOUNT] - 1;
}
if( $ida->[$IDA_ENUMPOINTER] == $ida->[$IDA_OFFSET] + $ida->[$IDA_ROWCOUNT] - 1 ) {
goto resetenum
if $ida->[$IDA_ENUMPOINTER] >= $ida->[$IDA_ROWMAX] - 1;
$ida->[$IDA_OFFSET] += $ida->[$IDA_ROWCOUNT];
$sql = $this->_prepare_query() or return 0;
$ida->[$IDA_RESULT] = $db->query( $sql )
or return $this->_set_error( $db->error() );
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
}
@row = $ida->[$IDA_RESULT]->fetch_row()
or return $this->_set_error( $db->error() );
$ida->[$IDA_ENUMPOINTER] ++;
return \@row;
}
resetenum:
$ida->[$IDA_ENUMPOINTER] = $ida->[$IDA_INITOFFSET];
return 0;
}
sub error {
my $this = shift;
return $LastError if ! ref( $this );
my $ida = $this->{'__I#D#A__'};
return $ida->[$IDA_ERROR] if $ida->[$IDA_ERROR];
return $ida->[$IDA_DB]->error();
}
sub _set_error {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
$ida->[$IDA_ERROR] = $_[0];
&Carp::croak( $_[0] );
return 0;
}
sub _apply {
my $this = shift;
my( $ida, $sfields, $i, $field, $ref );
$ida = $this->{'__I#D#A__'};
$sfields = $ida->[$IDA_SAFEFIELDS];
$ref = ref( $_[0] );
if( $ref eq 'HASH' ) {
foreach( keys %{$_[0]} ) {
$this->{$_} = $_[0]->{$_};
$sfields->{$_} = $_[0]->{$_};
}
}
elsif( $ref eq 'ARRAY' ) {
$i = 0;
foreach( @{ $_[0] } ) {
$field = $ida->[$IDA_NAMES][$i ++];
$this->{$field} = $_;
$sfields->{$field} = $_;
}
}
else {
$i = 0;
foreach( @_ ) {
$field = $ida->[$IDA_NAMES][$i ++];
$this->{$field} = $_;
$sfields->{$field} = $_;
}
}
return 1;
}
sub _resync {
my( $this, $type ) = @_;
my( $ida, $db, $sql, $sqlrows, $res );
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
$ida->[$IDA_OPTIONS] |= $IDA_OPT_CALCROWS if $type == 1;
( $sqlrows, $sql ) = $this->_prepare_query() or return 0;
$ida->[$IDA_OPTIONS] ^= $IDA_OPT_CALCROWS if $type == 1;
$ida->[$IDA_RESULT] = $db->query( $sql ) or return 0;
$ida->[$IDA_ROWCOUNT] = $ida->[$IDA_RESULT]->num_rows();
if( $type == 1 ) {
if( $sqlrows ) {
$res = $db->query( $sqlrows ) or return 0;
( $ida->[$IDA_ROWMAX] ) = $res->fetch_row();
}
else {
$ida->[$IDA_ROWMAX] = $ida->[$IDA_ROWCOUNT];
}
}
if( $ida->[$IDA_POINTER] >= $ida->[$IDA_ROWMAX] ) {
$ida->[$IDA_POINTER] = $ida->[$IDA_ROWMAX] - 1;
}
return 1;
}
sub _do_update {
my $this = shift;
my(
$ida, $num_fields, $field, $key, $i, @update, @save, $clausel, $db,
$sfields, $sql, $hr, @pk, @uk
);
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
$sfields = $ida->[$IDA_SAFEFIELDS];
$num_fields = scalar @{ $ida->[$IDA_TABLE_INFO] };
for $i( 0 .. $num_fields - 1 ) {
$field = $ida->[$IDA_TABLE_INFO][$i];
$key = $field->[$PAB3::DB::DB_FIELD_NAME];
if( ! defined $this->{$key} ) {
if( $field->[$PAB3::DB::DB_FIELD_NULLABLE] ) {
if( defined $sfields->{$key} ) {
push @update,
$db->quote_id( $key ) . ' = NULL';
$save[$i] = undef;
}
}
elsif( ! defined $sfields->{$key} ) {
push @update,
$db->quote_id( $key )
. ' = ' . $db->quote( $field->[$PAB3::DB::DB_FIELD_DEFAULT] );
$save[$i] = $field->[$PAB3::DB::DB_FIELD_DEFAULT];
}
}
elsif( ! defined $sfields->{$key}
|| $sfields->{$key} ne $this->{$key}
) {
push @update,
$db->quote_id( $key )
. ' = ' . $db->quote( $this->{$key} );
$save[$i] = $this->{$key};
}
else {
$save[$i] = $this->{$key};
}
if( $field->[$PAB3::DB::DB_FIELD_PRIKEY] ) {
push @pk, $key;
}
elsif( $field->[$PAB3::DB::DB_FIELD_UNIKEY] ) {
push @uk, $key;
}
}
return 1 if ! @update;
@pk = @uk if ! @pk;
$clausel = '';
if( @pk ) {
foreach $key( @pk ) {
if( exists( $sfields->{$key} ) ) {
$clausel .= ' AND ' if $clausel;
$clausel .= $db->quote_id( $key )
. ' = ' . $db->quote( $sfields->{$key} );
}
}
}
if( ! $clausel ) {
foreach $key( keys %$sfields ) {
if( exists( $sfields->{$key} ) ) {
$clausel .= ' AND ' if $clausel;
$clausel .= $db->quote_id( $key )
. ' = ' . $db->quote( $sfields->{$key} );
}
}
}
return if ! $clausel;
$sql =
'UPDATE ' .
$ida->[$IDA_CMD_TEXT] .
' SET ' .
join( ', ', @update ) .
' WHERE ' .
$clausel
;
$db->query( $sql ) or return 0;
for $i( 0 .. $num_fields - 1 ) {
$ida->[$IDA_SAFEFIELDS]->{ $ida->[$IDA_NAMES][$i] } = $save[$i];
}
return 1;
}
sub _do_insert {
my $this = shift;
my( $db, $sql, $ida, $num_fields, $field, $key, $i, @ainc, $sfields,
@key, @insert, @mark, $hr, $stmt
);
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
$sfields = $ida->[$IDA_SAFEFIELDS];
$num_fields = scalar @{ $ida->[$IDA_NAMES] };
for $i( 0 .. $num_fields - 1 ) {
$field = $ida->[$IDA_TABLE_INFO][$i];
$key = $field->[$PAB3::DB::DB_FIELD_NAME];
if( $field->[$PAB3::DB::DB_FIELD_IDENTITY] && ! $this->{$key} ) {
push @ainc, $key;
next;
}
push @key, $db->quote_id( $key );
push @mark, '?';
if( ! defined $this->{$key} ) {
if( $field->[$PAB3::DB::DB_FIELD_NULLABLE] ) {
push @insert, undef;
}
else {
push @insert, $field->[$PAB3::DB::DB_FIELD_DEFAULT];
}
}
else {
push @insert, $this->{$key};
}
$sfields->{$key} = $this->{$key};
}
$sql =
'INSERT INTO ' .
$ida->[$IDA_CMD_TEXT] .
' ( ' . join( ', ', @key ) . ' ) ' .
'VALUES( ' . join( ', ', @mark ) . ' )';
$stmt = $db->prepare( $sql ) or return 0;
$stmt->execute( @insert ) or return 0;
foreach( @ainc ) {
$i = $db->insert_id( $_, $ida->[$IDA_CMD_TEXT_PLAIN] );
if( defined $i && $i > 0 ) {
$this->{$_} = $sfields->{$_} = $i;
}
}
$ida->[$IDA_OFFSET] = $ida->[$IDA_ROWMAX] - $ida->[$IDA_LIMIT];
$this->_resync( 1 ) or return 0;
return 1;
}
sub _do_delete {
my $this = shift;
my( $db, $sql, $ida, $key, $field, $num_fields, $i, @pk, @uk, $clausel );
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
$num_fields = scalar @{ $ida->[$IDA_TABLE_INFO] };
for $i( 0 .. $num_fields - 1 ) {
$field = $ida->[$IDA_TABLE_INFO][$i];
$key = $field->[$PAB3::DB::DB_FIELD_NAME];
if( $field->[$PAB3::DB::DB_FIELD_PRIKEY] ) {
push @pk, $key;
}
elsif( $field->[$PAB3::DB::DB_FIELD_UNIKEY] ) {
push @uk, $key;
}
}
@pk = @uk if ! @pk;
if( @pk ) {
$clausel = '';
foreach $key( @pk ) {
if( defined $this->{$key} ) {
$clausel .= ' AND ' if $clausel;
$clausel .= $db->quote_id( $key )
. ' = ' . $db->quote( $this->{$key} );
}
}
}
if( ! $clausel ) {
$clausel = '';
foreach $key( @{ $ida->[$IDA_NAMES] } ) {
if( defined $this->{$key} ) {
$clausel .= ' AND ' if $clausel;
$clausel .= $db->quote_id( $key )
. ' = ' . $db->quote( $this->{$key} );
}
}
}
if( $clausel ) {
$sql =
'DELETE FROM '
. $ida->[$IDA_CMD_TEXT]
. ' WHERE '
. $clausel;
$db->query( $sql ) or return 0;
return 1;
}
return 0;
}
sub _table_info {
my( $this ) = @_;
my( $ida, $db, $field, @tabinf );
$ida = $this->{'__I#D#A__'};
return 1 if $ida->[$IDA_LAST_CMD] eq $ida->[$IDA_CMD_TEXT];
$db = $ida->[$IDA_DB];
@tabinf = ();
foreach $field( $db->show_fields( $ida->[$IDA_CMD_TEXT] ) ) {
push @tabinf, $field;
}
$ida->[$IDA_TABLE_INFO] = \@tabinf;
$ida->[$IDA_LAST_CMD] = $ida->[$IDA_CMD_TEXT];
return 1;
}
sub _prepare_query {
my( $this ) = @_;
my( $ida, $db, $sql, $sql2 );
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
$this->_verify_dataset() or return 0;
if( $ida->[$IDA_CMD_TYPE] == ctQuery ) {
$sql = $ida->[$IDA_CMD_TEXT];
$sql2 = '';
}
elsif( $ida->[$IDA_CMD_TYPE] == ctTable ) {
$sql = 'SELECT * FROM ' . $db->quote_id( $ida->[$IDA_CMD_TEXT] ) . ' WHERE 1';
if( $ida->[$IDA_FILTER] ) {
$sql .= ' AND ( ' . $ida->[$IDA_FILTER] . ' )';
}
if( $ida->[$IDA_SEARCH] ) {
$sql .= ' AND ( ' . $ida->[$IDA_SEARCH] . ' )';
}
if( ( $ida->[$IDA_OPTIONS] & $IDA_OPT_CALCROWS ) != 0 ) {
$sql2 = $db->sql_calc_rows( $sql );
$sql = $db->sql_pre_calc_rows( $sql );
}
else {
$sql2 = '';
}
$sql = $db->sql_limit( $sql, $ida->[$IDA_LIMIT], $ida->[$IDA_OFFSET] );
}
elsif( $ida->[$IDA_CMD_TYPE] == ctStoredProcedure ) {
return $this->_set_error( "CommandType 'ctSoredProcedure' ist not supported yet" );
}
return ( $sql2, $sql );
}
sub _verify_dataset {
my $this = shift;
my $ida = $this->{'__I#D#A__'};
if( $_[0] ) {
# want edit
if( $ida->[$IDA_CMD_TYPE] != ctTable ) {
return $this->_set_error( 'Cannot modify a non TABLE dataset' );
}
}
if( ! $ida->[$IDA_CMD_TEXT] ) {
return $this->_set_error( '"CommandText" is undefined' );
}
return 1;
}
sub _format_sql {
my $this = shift;
my( $ida, $db, $res, @fs, $step, $lp, $i );
$ida = $this->{'__I#D#A__'};
$db = $ida->[$IDA_DB];
@fs = split( //, $_[0] );
$res = '';
$step = 0;
for $i( 0 .. $#fs ) {
if( $step == 0 ) {
if( $fs[$i] eq '\'' ) {
$step = 1;
}
elsif( $fs[$i] eq '[' ) {
$lp = $i + 1;
$step = 2;
next;
}
$res .= $fs[$i];
}
elsif( $step == 1 ) {
if( $fs[$i] eq '\'' ) {
if( $fs[$i + 1] eq '\'' ) {
$res .= '\'\'';
$i ++;
next;
}
elsif( $fs[$i - 1] eq '\\' ) {
$res .= '\'';
next;
}
else {
$step = 0;
}
}
$res .= $fs[$i];
}
elsif( $step == 2 ) {
if( $fs[$i] eq ']' ) {
$res .= $db->quote_id( substr( $_[0], $lp, $i - $lp ) );
$step = 0;
next;
}
}
}
return $res;
}
__END__