DB-Object

 view release on metacpan or  search on metacpan

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

    my $sql = "CREATE DATABASE " . ( $opts->{if_not_exists} ? 'IF NOT EXISTS ' : '' ) . $name;
    if( scalar( @$params ) )
    {
        $sql .= ' ' . join( ' ', @$params );
    }
    my $dbh = $self->{dbh} || return( $self->error( "Could not find database handler." ) );
    my( $sth, $rc );
    # try-catch
    local $@;
    $sth = eval
    {
        $dbh->prepare( $sql );
    };
    if( $@ )
    {
        return( $self->error( "An error occured while prepareing SQL query to create database: ", $@ ) );
    }
    $sth or return( $self->error( "An error occured while prepareing SQL query to create database: ", $dbh->errstr ) );

    $rc = eval
    {
        $dbh->prepare( $sql );
    };
    if( $@ )
    {
        return( $self->error( "An error occured while prepareing SQL query to create database: ", $@ ) );
    }
    $rc or return( $self->error( "An error occured while prepareing SQL query to create database: ", $dbh->errstr ) );

    # try-catch
    eval
    {
        $sth->finish;
    };
    if( $@ )
    {
        return( $self->error( "An unexpected error occurred while trying to finish the SQL query to create database: ", $@, "\n$sql" ) );
    }

    my $ref = {};
    my @keys = qw( host port login passwd opt debug );
    @$ref{ @keys } = @$self{ @keys };
    $ref->{database} = $name;
    $dbh = $self->connect( $ref ) || return( $self->error( "I could create the database \"$name\" but oddly enough, I could not connect to it with user \"$ref->{login}\" on host \"$ref->{host}\" with port \"$ref->{port}\"." ) );
    return( $dbh );
}

sub databases
{
    my $self = shift( @_ );
    # return( $self->error( "Not connected to PostgreSQL server yet. Issue $dbh->connect first." ) ) if( !$self->{ 'dbh' } );
    my $dbh;
    # If there is no connection yet, then create one using the postgres login.
    # There should not be a live user and database just to check what databases there are.
    if( !$self->{dbh} )
    {
        my $con = 
        {
        'database' => 'mysql',
        };
        $con->{mysql_read_default_file} = '/etc/my.cnf' if( -f( '/etc/my.cnf' ) );
        if( CORE::exists( $ENV{ 'DB_MYSQL_CON' } ) )
        {
            @$con{ qw( host login passwd ) } = split( /;/, $ENV{ 'DB_MYSQL_CON' } );
        }
        else
        {
            @$con{ qw( host login passwd ) } = @_;
        }

        # try-catch
        local $@;
        $dbh = eval
        {
            $self->connect( $con );
        };
        if( $@ )
        {
            return( $self->error( "Error trying to connect to the MySQL server: $@" ) );
        }
        $dbh or return( $self->pass_error );
    }
    else
    {
        $dbh = $self;
    }
    my $temp = $dbh->do( "SHOW DATABASES" )->fetchall_arrayref;
    my @dbases = map( $_->[0], @$temp );
    return( @dbases );
}

# NOTE: sub datatype_dict is inherited

# NOTE: sub datatype_to_constant is inherited

# NOTE: sub datatypes is in inherited

sub get_sql_type
{
    my $self = shift( @_ );
    my $type = shift( @_ ) || return( $self->error( "No sql type was provided to get its constant." ) );
    $type = lc( $type );
    if( CORE::exists( $DATATYPES_DICT->{ $type } ) &&
        $type ne $DATATYPES_DICT->{ $type }->{type} )
    {
        $type = $DATATYPES_DICT->{ $type }->{type};
    }
    my $const;
    if( substr( $type, 0, 4 ) eq 'sql_' )
    {
        $const = $self->{dbh}->can( "DBI::\U${type}\E" );
    }
    else
    {
        $const = $self->{dbh}->can( "DBI::SQL_\U${type}\E" );
    }
    return( '' ) if( !defined( $const ) );
    return( $const->() );
}

# Specific to Mysql (Postgres also uses it)

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

    return( $res eq 'NULL' ? undef() : $res );
}

sub variables
{
    my $self = shift( @_ );
    my $type = shift( @_ );
    $self->error( "Variable '$type' is a read-only value." ) if( @_ );
    my $vars = $self->{ 'variables' } ||= {};
    if( !%$vars )
    {
        my $sth = $self->{dbh}->prepare( "SHOW VARIABLES" ) ||
        return( $self->error( "SHOW VARIABLES is not supported." ) );
        $sth->execute();
        my $ref = $self->fetchall_arrayref();
        my %vars = map{ lc( $_->[ 0 ] ) => $_->[ 1 ] } @$ref;
        $vars = \%vars if( %vars );
        $sth->finish();
    }
    my @found = grep{ /$type/i } keys( %$vars );
    return( '' ) if( !scalar( @found ) );
    return( $vars->{ $found[ 0 ] } );
}

# https://dev.mysql.com/doc/refman/8.0/en/show-variables.html
sub version
{
    my $self  = shift( @_ );
    # If we already have the information, let's use our cache instead of making a query
    return( $self->{_db_version} ) if( length( $self->{_db_version} ) );
    my $sql = 'SELECT @@innodb_version';
    my $sth = $self->do( $sql ) || return( $self->error( "Unable to issue the sql statement '$sql' to get the server version: ", $self->errstr ) );
    my $ver = $sth->fetchrow;
    $sth->finish;
    # We cache it
    $self->{_db_version} = version->parse( $ver );
    return( $ver );
}

sub _connection_options
{
    my $self  = shift( @_ );
    my $param = shift( @_ );
    # This should really not be an option. This decode utf8 in database
    $param->{mysql_enable_utf8} = 1 if( !CORE::exists( $param->{mysql_enable_utf8} ) );
    my @mysql_params = grep( /^mysql_/, keys( %$param ) );
    my $opt = $self->SUPER::_connection_options( $param );
    @$opt{ @mysql_params } = @$param{ @mysql_params };
    return( $opt );
}

# NOTE: sub _connection_params2hash_driver is not necessary here. We use our parent's one.

sub _connection_parameters
{
    my $self  = shift( @_ );
    my $param = shift( @_ );
    my $core = [qw( db login passwd host port driver database server opt uri debug cache_connections cache_dir cache_query cache_table unknown_field use_cache )];
    my @mysql_params = grep( /^mysql_/, keys( %$param ) );
    # See DBD::mysql for the list of valid parameters
    # E.g.: mysql_client_found_rows, mysql_compression mysql_connect_timeout mysql_write_timeout mysql_read_timeout mysql_init_command mysql_skip_secure_auth mysql_read_default_file mysql_read_default_group mysql_socket mysql_ssl mysql_ssl_client_key...
    push( @$core, @mysql_params );
    return( $core );
}

sub _dsn
{
    my $self = shift( @_ );
    # "DBI:mysql:database=$sql_db;host=$sql_host;port=$sql_port;mysql_read_default_file=/etc/my.cnf"
    my @params = ( sprintf( 'dbi:%s:database=%s', @$self{ qw( driver database ) } ) );
    if( $self->{host} )
    {
        $self->_load_class( 'Net::IP' ) || return( $self->pass_error );
        my $ip = Net::IP->new( $self->{host} );
        if( $ip )
        {
            if( $ip->version == 6 )
            {
                push( @params, sprintf( 'host=[%s]', $ip->ip ) );
            }
            else
            {
                push( @params, sprintf( 'host=%s', $ip->ip ) );
            }
        }
        else
        {
            push( @params, sprintf( 'host=%s', $self->{host} ) );
        }
    }
    # my @params = sprintf( "dbi:%s:database=%s;host=%s", @$self{ qw( driver database server ) } );
    push( @params, sprintf( 'port=%d', $self->{port} ) ) if( $self->{port} );
    push( @params, sprintf( 'mysql_read_default_file=%s', $self->{mysql_read_default_file} ) ) if( $self->{mysql_read_default_file} );
    return( join( ';', @params ) );
}

sub _placeholder_regexp { return( $PLACEHOLDER_REGEXP ) }

# NOTE: DESTROY
DESTROY
{
    # <https://perldoc.perl.org/perlobj#Destructors>
    CORE::local( $., $@, $!, $^E, $? );
    CORE::return if( ${^GLOBAL_PHASE} eq 'DESTRUCT' );
    my $self = CORE::shift( @_ );
    CORE::return if( !CORE::defined( $self ) );
    my $class = ref( $self ) || $self;
    if( $self->{sth} )
    {
        print( STDERR "DESTROY(): Terminating sth '$self' for query:\n$self->{query}\n" ) if( $DEBUG || $self->{debug} );
        $self->{sth}->finish();
    }
    elsif( $self->{dbh} && $class eq 'DB::Object' )
    {
        local( $SIG{__WARN__} ) = sub { };
        # $self->{ 'dbh' }->disconnect();
        if( $DEBUG || $self->{debug} )
        {
            my( $pack, $file, $line, $sub ) = ( caller(0) )[0, 1, 2, 3];
            my( $pack2, $file2, $line2, $sub2 ) = ( caller(1) ) [0, 1, 2, 3];
            print( STDERR "DESTROY database handle ($self) [$self->{query}]\ncalled within sub '$sub' ($sub2) from package '$pack' ($pack2) in file '$file' ($file2) at line '$line' ($line2).\n" );
        }
        $self->disconnect();
    }
    my $locks = $self->{_locks};
    if( $locks && $self->_is_array( $locks ) )
    {
        foreach my $name ( @$locks )
        {
            $self->unlock( $name );
        }
    }
}

1;
# NOTE: POD
__END__

=encoding utf8

=head1 NAME

DB::Object::Mysql - Mysql Database Object

=head1 SYNOPSIS

    use DB::Object;

    my $dbh = DB::Object->connect({
    driver => 'mysql',
    conf_file => 'db-settings.json',
    database => 'webstore',
    host => 'localhost',



( run in 1.176 second using v1.01-cache-2.11-cpan-ceb78f64989 )