HOME


Mini Shell 1.0
DIR:/lib64/cbpolicyd-2.1/awitpt/db/
Upload File :
Current File : //lib64/cbpolicyd-2.1/awitpt/db/dblayer.pm
# Common database layer module
# Copyright (C) 2009-2011, AllWorldIT
# Copyright (C) 2008, LinuxRulz
# Copyright (C) 2005-2007 Nigel Kukard  <nkukard@lbsd.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.



## @class awitpt::db::dblayer
# Database layer module which makes life a bit esier
package awitpt::db::dblayer;

use strict;
use warnings;


# Exporter stuff
require Exporter;
our (@ISA,@EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(
	DBConnect
	DBSelect
	DBDo
	DBLastInsertID
	DBBegin
	DBCommit
	DBRollback
	DBQuote
	DBFreeRes

	DBSelectNumResults
	DBSelectSearch

	hashifyLCtoMC
);

use POSIX qw( strftime );
use Date::Parse;

use awitpt::db::dbilayer;


# Database handle
my $dbh = undef;

# Our current error message
my $error = "";

## @internal
# @fn setError($err)
# This function is used to set the last error for this class
#
# @param err Error message
sub setError
{
	my $err = shift;
	my ($package,$filename,$line) = caller;
	my (undef,undef,undef,$subroutine) = caller(1);

	# Set error
	$error = "$subroutine($line): $err";
}

## @internal
# @fn Error
# Return current error message
#
# @return Last error message
sub Error
{
	my $err = $error;

	# Reset error
	$error = "";

	# Return error
	return $err;
}



## @fn setHandle($handle)
# Initialize database handle
#
# @param handle Set internal database handle we use
sub setHandle
{
		my $handle = shift;

		$dbh = $handle;
}


## @fn DBSelect($query,@params)
# Return database selection results...
#
# @param query Query to run
#
# @return DBI statement handle, undef on error
sub DBSelect
{
	my ($query,@params) = @_;

	my $table_prefix = $dbh->table_prefix();

	# Replace table prefix macro
	$query =~ s/\@TP\@/$table_prefix/g;

	# Prepare query
	my $sth;
	if (!($sth = $dbh->select($query,@params))) {
		setError("Error executing select: ".$dbh->Error());
		return undef;
	}

	return $sth;
}


## @fn DBDo($command)
# Perform a command
#
# @param command Command to run
#
# @return DBI statement handle, undef on error
sub DBDo
{
	my (@params) = @_;


	my $table_prefix = $dbh->table_prefix();

	# Check type of params we have
	if (ref($params[0]) eq 'HASH') {
		my $queryHash = $params[0];
		my $dbType = $dbh->type();

		# Check DB type is defined, if not use *
		if (defined($queryHash->{$dbType})) {
			@params = @{$queryHash->{$dbType}};
		} elsif (defined($queryHash->{'*'})) {
			@params = @{$queryHash->{'*'}};
		} else {
			setError("Error executing, database type in query not fund and no '*' query found");
			return undef;
		}
	}

	# Grab command and data
	my $command = shift(@params);
	my @data = @params;

	# Replace table prefix macro
	$command =~ s/\@TP\@/$table_prefix/g;

	# Prepare query
	my $sth;
	if (!($sth = $dbh->do($command,@data))) {
		setError("Error executing command '$command': ".$dbh->Error());
		return undef;
	}

	return $sth;
}


## @fn DBLastInsertID($table,$column)
# Function to get last insert id
#
# @param table Table to check
# @param column Column to get last insert on
#
# @return Last insert ID or undef on error
sub DBLastInsertID
{
	my ($table,$column) = @_;


	my $res;
	if (!($res = $dbh->lastInsertID(undef,undef,$table,$column))) {
		setError("Error getting last inserted id: ".$dbh->Error());
		return undef;
	}

	return $res;
}


## @fn DBBegin
# Function to begin a transaction
#
# @return 1 on success, undef on error
sub DBBegin
{
	my $res;
	if (!($res = $dbh->begin())) {
		setError("Error beginning transaction: ".$dbh->Error());
		return undef;
	}

	return $res;
}


## @fn DBCommit
# Function to commit a transaction
#
# @return 1 on success, undef on error
sub DBCommit
{
	my $res;
	if (!($res = $dbh->commit())) {
		setError("Error committing transaction: ".$dbh->Error());
		return undef;
	}

	return $res;
}


## @fn DBRollback
# Function to rollback a transaction
#
# @return 1 on success, undef on error
sub DBRollback
{
	my $res;
	if (!($res = $dbh->rollback())) {
		setError("Error rolling back transaction: ".$dbh->Error());
		return undef;
	}

	return $res;
}


## @fn DBQuote($variable)
# Function to quote a database variable
#
# @param variable Variable to quote
#
# @return Quoted variable
sub DBQuote
{
	my $variable = shift;


	return $dbh->quote($variable);
}


## @fn DBFreeRes($sth)
# Function to cleanup DB query
#
# @param sth Statement handle to cleanup
sub DBFreeRes
{
	my $sth = shift;


	if ($sth) {
		$sth->finish();
	}
}


# Function to get table prefix
sub DBTablePrefix
{
	return $dbh->table_prefix();
}


#
# Value Added Functions
#


## @fn DBSelectNumResults($query)
# Return how many results came up from the specific SELECT query
#
# @param query Query to perform, minus "SELECT COUNT(*) AS num_results"
#
# @return Number of results, undef on error
sub DBSelectNumResults
{
	my $query = shift;


	# Prepare query
	my $sth;
	if (!($sth = $dbh->select("SELECT COUNT(*) AS num_results $query"))) {
		setError("Error executing select: ".$dbh->Error());
		return undef;
	}

	# Grab row
	my $row = $sth->fetchrow_hashref();
	if (!defined($row)) {
		setError("Failed to get results from a select: ".$dbh->Error());
		return undef;
	}

	# Pull number
	my $num_results = $row->{'num_results'};
	$sth->finish();

	return $num_results;
}


## @fn DBSelectSearch($query,$search,$filters,$sorts)
# Select results from database and return the total number aswell
#
# @param query Base query
#
# @param search Search hash ref
# @li Filter - Filter based on this...
# [filter] => Array (
#	[0] => Array (
#		[field] => Name
#		[data] => Array (
#			[type] => string
#			[value] => hi there
#		)
#	)
# )
# { 'data' => { 'comparison' => 'gt', 'value' => '5', 'type' => 'numeric' }, 'field' => 'ID' }
# @li Start - Start item number, indexed from 0 onwards
# @li Limit - Limit number of results
# @li Sort - Sort by this item
# @li SortDirection - Sort in this direction, either ASC or DESC
#
# @param filters Filter hash ref
# Hash:  'Column' -> 'Table.DBColumn'
#
# @param sorts Hash ref of valid sort criteria, indexed by what we get, pointing to the DB column in the query
# Hash:  'Column' -> 'Table.DBColumn'
#
# @return Number of results, undef on error
sub DBSelectSearch
{
	my ($query,$search,$filters,$sorts) = @_;


	# Stuff we need to add to the SQL query
	my @where; # Where clauses
	my $sqlWhere = "";
	my $sqlLimit = "";
	my $sqlOffset = "";
	my $sqlOrderBy = "";
	my $sqlOrderByDirection = "";

	# Check if we're searching
	if (defined($search) && ref($search)) {
		# Check it is a hash
		if (ref($search) ne "HASH") {
			setError("Parameter 'search' is not a hashtable");
			return (undef,-1);
		}
		# Check if we need to filter
		if (defined($search->{'Filter'})) {
			# We need filters in order to use filtering
			if (!defined($filters)) {
				setError("Parameter 'search' element 'Filter' requires 'filters' to be defined");
				return (undef,-1);
			}

			# Check type of Filter
			if (ref($search->{'Filter'}) ne "ARRAY") {
				setError("Parameter 'search' element 'Filter' is of invalid type, it must be an ARRAY'");
				return (undef,-1);
			}

			# Loop with filters
			foreach my $item (@{$search->{'Filter'}}) {
				my $data = $item->{'data'};  # value, type, comparison
				my $field = $item->{'field'};
				my $column = $filters->{$field};

				# Check if field is in our allowed filters
				if (!defined($filters->{$field})) {
					setError("Parameter 'search' element 'Filter' has invalid field item '$field' according to 'filters'");
					return (undef,-1);
				}
				# Check data
				if (!defined($data->{'type'})) {
					setError("Parameter 'search' element 'Filter' requires field data element 'type' for field '$field'");
					return (undef,-1);
				}
				if (!defined($data->{'value'})) {
					setError("Parameter 'search' element 'Filter' requires field data element 'value' for field '$field'");
					return (undef,-1);
				}

				# match =, LIKE, IN (
				# matchEnd '' or )
				my $match;
				my $matchEnd = "";
				# value is the DBQuote()'d value
				my $value;

				# Check what type of comparison
				if ($data->{'type'} eq "boolean") {
					$match = '=';
					$value = DBQuote($data->{'value'});


				} elsif ($data->{'type'} eq "date") {

					# The comparison type must be defined
					if (!defined($data->{'comparison'})) {
						setError("Parameter 'search' element 'Filter' requires field data element 'comparison' for date field '$field'");
						return (undef,-1);
					}

					# Check comparison type
					if ($data->{'comparison'} eq "gt") {
						$match = ">";

					} elsif ($data->{'comparison'} eq "lt") {
						$match = "<";

					} elsif ($data->{'comparison'} eq "eq") {
						$match = "=";

					} elsif ($data->{'comparison'} eq "ge") {
						$match = ">=";

					} elsif ($data->{'comparison'} eq "le") {
						$match = "<=";
					}
					# Convert to ISO format
					my $unixtime = str2time($data->{'value'});
					my @d = localtime($unixtime);
					$value = DBQuote(sprintf("%4d-%02d-%02d", $d[5]+1900, $d[4]+1, $d[3]));


				} elsif ($data->{'type'} eq "list") {
					# Quote all values
					my @valueList;
					foreach my $i (split(/,/,$data->{'value'})) {
						push(@valueList,DBQuote($i));
					}

					$match = "IN (";
					# Join up 'xx','yy','zz'
					$value = join(',',@valueList);
					$matchEnd = ")";


				} elsif ($data->{'type'} eq "numeric") {

					# The comparison type must be defined
					if (!defined($data->{'comparison'})) {
						setError("Parameter 'search' element 'Filter' requires field data element 'comparison' for numeric field '$field'");
						return (undef,-1);
					}

					# Check comparison type
					if ($data->{'comparison'} eq "gt") {
						$match = ">";

					} elsif ($data->{'comparison'} eq "lt") {
						$match = "<";

					} elsif ($data->{'comparison'} eq "eq") {
						$match = "=";

					} elsif ($data->{'comparison'} eq "ge") {
						$match = ">=";

					} elsif ($data->{'comparison'} eq "le") {
						$match = "<=";
					}

					$value = DBQuote($data->{'value'});


				} elsif ($data->{'type'} eq "string") {
					$match = "LIKE";
					$value = DBQuote("%".$data->{'value'}."%");

				}

				# Add to list
				push(@where,"$column $match $value $matchEnd");
			}

			# Check if we have any WHERE clauses to add ...
			if (@where > 0) {
				# Check if we have WHERE clauses in the query
				if ($query =~ /\sWHERE\s/i) {
					# If so start off with AND
					$sqlWhere .= "AND ";
				} else {
					$sqlWhere = "WHERE ";
				}
				$sqlWhere .= join(" AND ",@where);
			}
		}

		# Check if we starting at an OFFSET
		if (defined($search->{'Start'})) {
			# Check if Start is valid
			if ($search->{'Start'} < 0) {
				setError("Parameter 'search' element 'Start' invalid value '".$search->{'Start'}."'");
				return (undef,-1);
			}

			$sqlOffset = sprintf("OFFSET %i",$search->{'Start'});
		}

		# Check if results will be LIMIT'd
		if (defined($search->{'Limit'})) {
			# Check if Limit is valid
			if ($search->{'Limit'} < 1) {
				setError("Parameter 'search' element 'Limit' invalid value '".$search->{'Limit'}."'");
				return (undef,-1);
			}

			$sqlLimit = sprintf("LIMIT %i",$search->{'Limit'});
		}

		# Check if we going to be sorting
		if (defined($search->{'Sort'})) {
			# We need sorts in order to use sorting
			if (!defined($sorts)) {
				setError("Parameter 'search' element 'Filter' requires 'filters' to be defined");
				return (undef,-1);
			}

			# Check if sort is defined
			if (!defined($sorts->{$search->{'Sort'}})) {
				setError("Parameter 'search' element 'Sort' invalid item '".$search->{'Sort'}."' according to 'sorts'");
				return (undef,-1);
			}

			# Build ORDER By
			$sqlOrderBy = "ORDER BY ".$sorts->{$search->{'Sort'}};

			# Check for sort ORDER
			if (defined($search->{'SortDirection'})) {

				# Check for valid directions
				if (lc($search->{'SortDirection'}) eq "asc") {
					$sqlOrderByDirection = "ASC";

				} elsif (lc($search->{'SortDirection'}) eq "desc") {
					$sqlOrderByDirection = "DESC";

				} else {
					setError("Parameter 'search' element 'SortDirection' invalid value '".$search->{'SortDirection'}."'");
					return (undef,-1);
				}
			}
		}
	}

	# Select row count, pull out   "SELECT .... "  as we replace this in the NumResults query
	(my $queryCount = $query) =~ s/^\s*SELECT\s.*\sFROM/FROM/is;
	my $numResults = DBSelectNumResults("$queryCount $sqlWhere");
	if (!defined($numResults)) {
		return undef;
	}

	# Add Start, Limit, Sort, Direction
	my $sth = DBSelect("$query $sqlWhere $sqlOrderBy $sqlOrderByDirection $sqlLimit $sqlOffset");
	if (!defined($sth)) {
		return undef;
	}

	return ($sth,$numResults);
}


# Convert a lower case array to mixed case
sub hashifyLCtoMC
{
	my ($record,@entries) = @_;


	# If we undefined, return
	return undef if (!defined($record));

	my $res;

	# Loop with each item, assign from lowecase database record to our result
	foreach my $entry (@entries) {
		$res->{$entry} = $record->{lc($entry)};
	}

	return $res;
}




1;
# vim: ts=4