DB-Object

 view release on metacpan or  search on metacpan

lib/DB/Object/Statement.pm  view on Meta::CPAN

    if( !scalar( grep{ /^$type$/i } @allowed ) )
    {
        return( $self->error( "You may not flag statement of type \U$type\E to be ignored:\n$query" ) );
    }
    # Incompatible. Do not bother going further
    return( $self ) if( $query =~ /^\s*(?:$allowed)\s+(?:DELAYED|LOW_PRIORITY|HIGH_PRIORITY)\s+/i );
    return( $self ) if( $type eq 'ALTER' && $query !~ /^\s*$type\s+TABLE\s+/i );

    $query =~ s/^(\s*)($allowed)(\s+)/$1$2 IGNORE /;
    # my $sth = $self->prepare( $query ) ||
    # $self->{ 'query' } = $query;
    # saving parameters to bind later on must have been done previously
    my $sth = $self->_cache_this( $query ) ||
    return( $self->error( "Error while preparing new ignored query:\n$query" ) );
    if( !defined( wantarray() ) )
    {
        $sth->execute() ||
        return( $self->error( "Error while executing new ignored query:\n$query" ) );
    }
    return( $sth );
}

sub join
{
    my $self = shift( @_ );
    my $data = shift( @_ );
    my $on;
    if( @_ )
    {
        $on = ( scalar( @_ ) == 1 && ref( $_[0] ) ) ? shift( @_ ) : [ @_ ];
    }
    my $q = $self->query_object || return( $self->error( "No query formatter object was set" ) );
    my $tbl_o = $q->table_object || return( $self->error( "No table object is set in query object." ) );
    my $query = $q->query ||
        return( $self->error( "No query prepared for join with another table." ) );
    if( $query !~ /^[[:blank:]]*SELECT[[:blank:]]+/i )
    {
        return( $self->error( "You may not perform a join on a query other than select." ) );
    }
    my $constant = $q->constant;
    # Constant is set and query object marked as final, which means this statement has already been processed as a join and so we skip all further processing.
    if( scalar( keys( %$constant ) ) && $q->final )
    {
        return( $self );
    }
    my $table      = $tbl_o->table;
    my $db         = $tbl_o->database;
    my $multi_db   = $tbl_o->prefix_database;
    my $alias      = $tbl_o->as;
    my $new_fields = '';
    my $new_table  = '';
    my $new_db     = '';
    my $class      = ref( $self );
    my $q_source   = $q->clone;
    # clone() does a shallow copy - $q_source->{elements} would share the same object
    # as $q->{elements}. Any subsequent _save_bind() on $q would then corrupt the
    # element list of the cached statement. We reset elements here so $q_source gets
    # its own fresh object, populated by _query_components below.
    $q_source->{elements} = $q_source->new_elements;
    # clone() copies the hash shallowly, which means {table_object} is a strong reference
    # in the clone even though _query_object_create() weakens it for freshly created query
    # objects. If left strong, $q_source permanently retains the table object, which in turn
    # retains every new query object created by _reset_query() on each execute() cycle,
    # causing an ~8 MB per-iteration memory leak. Weaken it here to restore correct ownership.
    Scalar::Util::weaken( $q_source->{table_object} ) if( $q_source->{table_object} && !Scalar::Util::isweak( $q_source->{table_object} ) );
    Scalar::Util::weaken( $q_source->{database_object} ) if( $q_source->{database_object} && !Scalar::Util::isweak( $q_source->{database_object} ) );

    my $q_target;
    # On the duplicated table object, add the current table in the join
    $q_source->join_tables( $tbl_o ) if( !$q_source->join_tables->length );
    # $data is a DB::Object::Postgres::Statement object - we get all its parameter and merge them with ours
    # if( ref( $data ) && ref( $data ) eq $class )
    if( ref( $data ) && $self->_is_a( $data, $class ) )
    {
        $q_target = $data->query_object;
    }
    # $data is the table name
    else
    {
        my $join_tbl;
        if( $self->_is_object( $data ) && $data->isa( 'DB::Object::Tables' ) )
        {
            $join_tbl = $data;
        }
        elsif( $self->_is_object( $data ) )
        {
            return( $self->error( "I was expecting either a table name as a scalar or a table object, but instead got \"$data\" (", ref( $data ), ")." ) );
        }
        else
        {
            return( $self->error( "No such table \"$data\" exists in database \"$db\"." ) ) if( !$self->database_object->table_exists( $data ) );
            $join_tbl = $self->database_object->table( $data );
            return( $self->error( "Could not get a table object from \"$data\"." ) ) if( !$join_tbl );
        }
        $join_tbl->prefixed( $db ne $join_tbl->database_object->database ? 3 : 1 );
        my $sth_tmp = $join_tbl->select || return( $self->pass_error( $join_tbl->error ) );
        $q_target = $sth_tmp->query_object || 
            return( $self->error( "Could not get a query object out of the dummy select query I made from table \"$data\"." ) );
        $new_fields = $q_target->selected_fields;
        # NOTE: 2021-08-22: If we reset it here, we lose the table aliasing
        # $join_tbl->reset;

        # $join_tbl->prefixed( $db ne $join_tbl->database_object->database ? 3 : 1 ) unless( $join_tbl->prefixed );
        $new_table = $join_tbl->prefix;
        $join_tbl->reset;
        # We assume this table is part of our same database
        $new_db     = $db;
        # my $db_data = $self->getdefault( $new_table );
        # $new_fields = $db_data->format_statement();
        $new_fields = '';
    }
    # TODO: check this or remove it
    # $q_target->table_object->prefixed( $db ne $q_target->database_object->database ? 3 : 1 );
    $new_fields = $q_target->selected_fields;
    $new_table  = $q_target->table_object->name;
    # $new_table  = $q_target->table_object->prefix;
    $new_db     = $q_target->database_object->database;
    $q_source->join_tables->push( $q_target->table_object );
    if( $q->where && $q_target->where )
    {
        # We must NOT call $q_source->where( AND(...) ) here because _where_having would
        # reconstruct the WHERE via process_where_condition, losing the bind elements that
        # were built up in $q->where. Instead we build a merged Clause directly, copying
        # the existing elements from $q->where so they are available to _save_bind below.
        my $merged = $q_source->new_clause;
        $merged->merge( $self->AND( $q->where, $q_target->new_clause({ value => '( ' . ( $q_target->where ) . ' )' }) ) );



( run in 2.763 seconds using v1.01-cache-2.11-cpan-437f7b0c052 )