DB-Object

 view release on metacpan or  search on metacpan

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

# -*- perl -*-
##----------------------------------------------------------------------------
## Database Object Interface - ~/lib/DB/Object/Query.pm
## Version v0.10.0
## Copyright(c) 2026 DEGUEST Pte. Ltd.
## Author: Jacques Deguest <jack@deguest.jp>
## Created 2017/07/19
## Modified 2026/03/27
## All rights reserved
## 
## 
## This program is free software; you can redistribute  it  and/or  modify  it
## under the same terms as Perl itself.
##----------------------------------------------------------------------------
package DB::Object::Query;
BEGIN
{
    use strict;
    use warnings;
    use parent qw( DB::Object );
    use vars qw( $VERSION $DEBUG $EXCEPTION_CLASS );
    use DB::Object::Query::Clause;
    use DB::Object::Query::Elements;
    use DB::Object::Query::Element;
    use Scalar::Util qw( weaken );
    use Wanted;
    our $DEBUG           = 0;
    our $EXCEPTION_CLASS = $DB::Object::EXCEPTION_CLASS;
    our $VERSION = 'v0.10.0';
};

use strict;
use warnings;

sub init
{
    my $self = shift( @_ );
    $self->{alias}          = {} unless( CORE::exists( $self->{alias} ) );
    $self->{avoid}          = [] unless( CORE::exists( $self->{avoid} ) );
    $self->{binded}         = [] unless( CORE::exists( $self->{binded} ) );
    # $self->{binded_group}   = [] unless( CORE::exists( $self->{binded_group} ) );
    # $self->{binded_limit}   = [] unless( CORE::exists( $self->{binded_limit} ) );
    # $self->{binded_order}   = [] unless( CORE::exists( $self->{binded_order} ) );
    $self->{binded_types}   = [] unless( CORE::exists( $self->{binded_types} ) );
    $self->{binded_values}  = [] unless( CORE::exists( $self->{binded_values} ) );
    $self->{binded_where}   = [] unless( CORE::exists( $self->{binded_where} ) );
    $self->{dirty}          = 0;
    $self->{elements}       = undef unless( CORE::exists( $self->{elements} ) );
    $self->{enhance}        = 0 unless( CORE::exists( $self->{enhance} ) );
    $self->{from_table}     = [] unless( CORE::exists( $self->{from_table} ) );
    $self->{from_unixtime}  = [] unless( CORE::exists( $self->{from_unixtime} ) );
    $self->{group_by}       = '' unless( CORE::exists( $self->{group_by} ) );
    $self->{having}         = '' unless( CORE::exists( $self->{having} ) );
    $self->{join_fields}    = '' unless( CORE::exists( $self->{join_fields} ) );
    $self->{left_join}      = {} unless( CORE::exists( $self->{left_join} ) );
    $self->{limit}          = '' unless( CORE::exists( $self->{limit} ) );
    $self->{local}          = {} unless( CORE::exists( $self->{local} ) );
    $self->{order_by}       = '' unless( CORE::exists( $self->{order_by} ) );
    $self->{prepare_options}= {} unless( CORE::exists( $self->{prepare_options} ) );
    $self->{query_values}   = undef unless( CORE::exists( $self->{query_values} ) );
    $self->{reverse}        = '' unless( CORE::exists( $self->{reverse} ) );
    $self->{sorted}         = [] unless( CORE::exists( $self->{sorted} ) );
    $self->{table_alias}    = '' unless( CORE::exists( $self->{table_alias} ) );
    $self->{table_object}   = '' unless( CORE::exists( $self->{table_object} ) );
    $self->{unix_timestamp} = [] unless( CORE::exists( $self->{unix_timestamp} ) );
    $self->{where}          = '' unless( CORE::exists( $self->{where} ) );
    $self->{_init_strict_use_sub} = 1;
    $self->{_exception_class}     = $EXCEPTION_CLASS;
    $self->SUPER::init( @_ ) || return( $self->pass_error );
    $self->{constant}       = {};
    $self->{query}          = '';
    $self->{query_reset}    = 0;
    $self->{query_reset_core_keys} = [qw(
        alias binded binded_group binded_limit binded_order binded_types binded_values
        binded_where dirty from_unixtime group_by having limit local order_by reverse
        sorted table_alias unix_timestamp where
        from_table join_fields join_tables left_join prepare_options query_values constant
        query selected_fields tie_order
    )];
    $self->{selected_fields} = '';
    $self->{table_object}   = '';
    $self->{tie_order}      = [];
    unless( $self->{elements} )
    {
        $self->{elements} = $self->new_elements;

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


sub limit
{
    my $self  = shift( @_ );
    my $limit = $self->{limit};
    if( @_ )
    {
        # Returns a DB::Object::Query::Clause
        $limit = $self->_process_limit( @_ ) ||
            return( $self->pass_error );
        if( CORE::length( $limit->metadata->limit // '' ) )
        {
            $limit->generic( CORE::length( $limit->metadata->offset // '' ) ? 'LIMIT ?, ?' : 'LIMIT ?' );
            # %s works for integer, and also for numbered placeholders like $1 or ?1, or regular placeholder like ?
            $limit->value(
                CORE::length( $limit->metadata->offset // '' )
                    ?  sprintf( "LIMIT %s, %s", $limit->metadata->offset, $limit->metadata->limit )
                    : sprintf( "LIMIT %s", $limit->metadata->limit )
            );
        }
    }

    if( !$limit && want( 'OBJECT' ) )
    {
        return( $self->new_null( type => 'object' ) );
    }
    return( $limit );
}

sub local
{
    my $self = shift( @_ );
    $self->{local} ||= {};
    my $local = $self->{local};
    if( @_ )
    {
        my $data = $self->_get_args_as_hash( @_ );
        my $str  = '';
        if( scalar( keys( %$data ) ) )
        {
            my @keys = keys( %$data );
            @$local{ @keys } = @$data{ @keys };
        }
    }
    return( wantarray() ? () : undef() ) if( !$local || !%$local );
    return( %$local ) if( wantarray() );
    my $str = join( ', ', map{ "\@${_} = '" . $local->{ $_ } . "'" } keys( %$local ) );
    # return( "SET $str" );
    return( $str );
}

sub new_clause
{
    my $self = shift( @_ );
    my $opts = $self->_get_args_as_hash( @_ );
    $opts->{debug} = $self->debug if( !exists( $opts->{debug} ) );
    $opts->{query_object} = $self;
    my $o = DB::Object::Query::Clause->new( %$opts ) ||
        return( $self->error( "Unable to create a DB::Object::Query::Clause object: ", DB::Object::Query::Clause->error ) );
    # Weaken the back-reference to the query object to prevent circular reference memory leaks.
    weaken( $o->{query_object} ) if( $o->{query_object} );
    return( $o );
}

sub new_element
{
    my $self = shift( @_ );
    my $opts = $self->_get_args_as_hash( @_ );
    $opts->{debug} = $self->debug if( !exists( $opts->{debug} ) );
    $opts->{query_object} = $self;
    my $elem = DB::Object::Query::Element->new( %$opts ) ||
        return( $self->pass_error( DB::Object::Query::Element->error ) );
    # Weaken the back-reference to the query object to prevent circular reference memory leaks.
    weaken( $elem->{query_object} ) if( $elem->{query_object} );
    return( $elem );
}

sub new_elements
{
    my $self = shift( @_ );
    my $opts = $self->_get_args_as_hash( @_ );
    $opts->{debug} = $self->debug if( !exists( $opts->{debug} ) );
    $opts->{query_object} = $self;
    my $e = DB::Object::Query::Elements->new( %$opts ) ||
        return( $self->pass_error( DB::Object::Query::Elements->error ) );
    # Weaken the back-reference to the query object to prevent circular reference memory leaks.
    weaken( $e->{query_object} ) if( $e->{query_object} );
    return( $e );
}

sub order { return( shift->_group_order( 'order', 'order_by', @_ ) ); }

sub pass_error
{
    my $self = shift( @_ );

    # Propagating an error also taints this query object.
    $self->{dirty} = 1 if( ref( $self ) );

    return( $self->SUPER::pass_error( @_ ) );
}

sub prepare_options { return( shift->_set_get_hash_as_mix_object( 'prepare_options', @_ ) ); }

sub query { return( shift->_set_get_scalar( 'query', @_ ) ); }

sub query_reset { return( shift->_set_get_boolean( 'query_reset', @_ ) ); }

sub query_reset_core_keys { return( shift->_set_get_array_as_object( 'query_reset_core_keys', @_ ) ); }

sub query_reset_keys { return( shift->_set_get_array_as_object( 'query_reset_keys', @_ ) ); }

sub query_type { return( shift->_set_get_scalar( 'query_type', @_ ) ); }

sub query_values { return( shift->_set_get_scalar_as_object( 'query_values', @_ ) ); }

sub replace { return( shift->error( "The replace sql query is not supported by this driver." ) ); }

sub reset
{
    my $self = shift( @_ );
    if( !$self->{query_reset} )
    {
        my $core_keys = $self->query_reset_core_keys;
        my $keys      = $self->query_reset_keys;
        # Make sure the driver's list of keys for query reset is complete by merging this base class keys with the diver's one
        unless( $core_keys == $keys )
        {
            my $new_keys = $keys->merge( $core_keys )->unique->sort;
            $keys = $self->query_reset_keys( $new_keys );
        }
        CORE::delete( @$self{ @$keys } );
        # elements is not in query_reset_core_keys because CORE::delete would leave it undef
        # and there is no re-init pass after reset(). We clear it explicitly here instead,
        # so that _save_bind() on each fresh select() starts from an empty element stack.
        $self->{elements} = $self->new_elements;
        $self->{query_reset}++;
        $self->{enhance} = 1;
    }
    return( $self );
}

sub reset_bind
{
    my $self = shift( @_ );
    my @f = qw( binded binded_group binded_limit binded_order binded_types binded_where );
    foreach my $field ( @f )



( run in 0.634 second using v1.01-cache-2.11-cpan-39bf76dae61 )