DB-Object
view release on metacpan or search on metacpan
lib/DB/Object.pm view on Meta::CPAN
# -*- perl -*-
##----------------------------------------------------------------------------
## Database Object Interface - ~/lib/DB/Object.pm
## Version v1.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.
##----------------------------------------------------------------------------
## This is the subclassable module for driver specific ones.
package DB::Object;
BEGIN
{
require 5.16.0;
use strict;
use warnings;
use warnings::register;
use parent qw( Module::Generic DBI );
use vars qw(
$VERSION $AUTOLOAD $CACHE_DIR $CACHE_SIZE $DRIVER2PACK
$ERROR $DEBUG $MOD_PERL $USE_BIND $USE_CACHE $PLACEHOLDER_REGEXP $SERIALISER
$SERIALISATION_VERSION $EXCEPTION_CLASS
);
use Regexp::Common;
use Scalar::Util qw( blessed weaken );
use DB::Object::Cache::Tables;
use DBI;
use JSON;
use Module::Generic::File qw( sys_tmpdir );
use Module::Generic::Global qw( :const );
use POSIX ();
use Wanted;
our $PLACEHOLDER_REGEXP = qr/(?<![?\w])\?(?![?\w])/;
our $EXCEPTION_CLASS = 'DB::Object::Exception';
our $VERSION = 'v1.10.0';
};
use strict;
use warnings;
our $DEBUG = 0;
# A global setting variable. This is not changed dynamically, and can be set by the user
our $CACHE_SIZE = 10;
our $USE_BIND = 0;
our $USE_CACHE = 0;
our $MOD_PERL = 0;
# A global variable that can be set by the user to serve as a default value
our $CACHE_DIR = '';
if( $INC{ 'Apache/DBI.pm' } &&
substr( $ENV{GATEWAY_INTERFACE} || '', 0, 8 ) eq 'CGI-Perl' )
{
$MOD_PERL++;
}
# Global constant
our $DRIVER2PACK =
{
mysql => 'DB::Object::Mysql',
Pg => 'DB::Object::Postgres',
SQLite => 'DB::Object::SQLite',
};
# Default value
our $SERIALISER = 'Storable';
our $SERIALISATION_VERSION = 1;
sub new
{
my $that = shift( @_ );
my $class = ref( $that ) || $that;
my $self = {};
bless( $self, $class );
return( $self->init( @_ ) );
}
sub init
{
my $self = shift( @_ );
$self->{cache_connections} = 1;
$self->{cache_dir} = sys_tmpdir();
# Cache query on file ? Only works if 'cache_dir' is set too.
$self->{cache_query} = 0;
$self->{cache_size} = $CACHE_SIZE;
$self->{cache_table} = 0;
$self->{driver} = '';
# Auto-decode json data into perl hash
$self->{auto_decode_json} = 1;
lib/DB/Object.pm view on Meta::CPAN
my $args = ( scalar( @_ ) == 1 && ref( $_[0] ) eq 'ARRAY' ) ? [ @{$_[0]} ] : [ @_ ];
return( $class->new(
debug => $self->debug,
query_object => $q,
value => $args,
) );
}
sub _param2hash
{
my $self = shift( @_ );
my $opts = {};
if( scalar( @_ ) )
{
if( $self->_is_hash( $_[0] => 'strict' ) )
{
$opts = shift( @_ );
}
elsif( !( scalar( @_ ) % 2 ) )
{
$opts = { @_ };
}
else
{
return( $self->error( "Uneven number of parameters. I was expecting a hash or a hash reference." ) );
}
}
return( $opts );
}
sub _placeholder_regexp { return( $PLACEHOLDER_REGEXP ) }
# NOTE: _query_object_add needs to reside in DB::Object (called indirectly by no_bind)
sub _query_object_add
{
my $self = shift( @_ );
my $obj = shift( @_ ) || return( $self->error( "No query object was provided" ) );
my $base = $self->base_class;
return( $self->error( "Object provided is not a query object class" ) ) if( ref( $obj ) !~ /^${base}\::Query$/ );
$self->query_object( $obj );
return( $obj );
}
# NOTE: _query_object_create needs to reside in DB::Object (called indirectly by no_bind)
sub _query_object_create
{
my $self = shift( @_ );
my $base = $self->base_class;
my $query_class = "${base}::Query";
$self->_load_class( $query_class ) ||
return( $self->error( "Unable to load Query builder module $query_class: ", $self->error->message ) );
my $o = $query_class->new;
$o->debug( $self->debug );
$o->enhance( $self->{enhance} ) if( CORE::length( $self->{enhance} ) );
if( $self->isa( 'DB::Object::Tables' ) )
{
$o->table_object( $self ) || return( $self->pass_error( $o->error ) );
# Weaken the back-reference from the Query object to its owning Table to break
# the circular reference cycle (Table -> query_object -> Query -> table_object -> Table)
# that prevents Perl's garbage collector from reclaiming old Query objects.
weaken( $o->{table_object} );
$o->database_object( $self->database_object ) || return( $self->pass_error( $o->error ) );
# Also weaken the database_object reference - the dbo is a long-lived singleton
# and does not own Query objects, so a weak reference is safe here.
weaken( $o->{database_object} );
# Set the table alias if any has been set
if( my $table_alias = $self->as )
{
$o->table_alias( $table_alias ) if( defined( $table_alias ) );
}
}
elsif( $self->isa( 'DB::Object' ) )
{
$o->database_object( $self ) || return( $self->pass_error( $o->error ) );
}
return( $o );
}
# NOTE: _query_object_current needs to reside in DB::Object (called indirectly by no_bind)
sub _query_object_current { return( shift->{query_object} ); }
# NOTE: _query_object_get_or_create needs to reside in DB::Object (called indirectly by no_bind)
# If the stack is empty, we create an object, add it and resend it
sub _query_object_get_or_create
{
my $self = shift( @_ );
my $obj = $self->query_object;
if( $obj && $obj->dirty )
{
$self->_query_object_remove( $obj );
undef( $obj );
}
if( !$obj )
{
$obj = $self->_query_object_create || return( $self->pass_error );
$self->query_object( $obj );
}
return( $obj );
}
# NOTE: _query_object_remove needs to reside in DB::Object (called indirectly by no_bind)
sub _query_object_remove
{
my $self = shift( @_ );
my $obj = shift( @_ ) || return( $self->error( "No query object was provided" ) );
my $base = $self->base_class;
# return( $self->error( "Object provided is not a query object class" ) ) if( ref( $obj ) !~ /^${base}\::Query$/ );
return( $self->error( "Object provided is not a query object class" ) ) if( !$obj->isa( "DB::Object::Query" ) );
$self->query_object( undef );
return( $obj );
}
# NOTE: A query object can be owned by a table object, but also by a database object, such as when instantiating operators like ALL, AND, OR
sub _reset_query
{
my $self = shift( @_ );
if( !$self->{query_reset} )
{
$self->{query_reset} = 1;
$self->{enhance} = 1;
my $obj = $self->query_object;
# If current query object is dirty, detach it right away.
( run in 1.208 second using v1.01-cache-2.11-cpan-39bf76dae61 )