Приглашаем посетить
Отели (hotels.otpusk-info.ru)
WEB_STORE_DB_LIB
###########################################################
# WEB_STORE_DB_LIB.PL
#
# Date Created: 11-15-96
# Date Last Modified: 11-26-96
#
# Copyright Info: This library was written by Gunther Birznieks
# (birzniek@hlsun.redcross.org) having been inspired by countless
# other Perl authors. Feel free to copy, cite, reference, sample,
# borrow, resell or plagiarize the contents. However, if you don't
# mind, please let me know where it goes so that I can at least
# watch and take part in the development of the memes. Information
# wants to be free, support public domain freware. Donations are
# appreciated and will be spent on further upgrades and other public
# domain scripts.
#
# Purpose: This library contains the routines that the
# Web store uses to interface with a flatfile (plain
# ASCII text file) database file.
#
# Special Note: If you wish to interface with a SQL database
# such as mSQL, this is where you do it. Simply replace the
# routines in here with routines that call the database
# engine of your choice.
#
# Main Procedures:
# check_db_with_product_id - The web store takes
# this procedure and double checks that the order
# within the cart matches the product description
# in the database. This is a security check.
#
# submit_query - This routine submits a query to
# the database and returns the results in an array
# for each row returned.
#
############################################################
#
# $sc_db_lib_was_loaded is set to "yes"
# to make sure that the db library
# is not loaded more than once within
# the web store script. The main
# web store script checks to see
# if this variable is set before it
# attempts to require this library.
#
$sc_db_lib_was_loaded = "yes";
############################################################
#
# subroutine: check_db_with_product_id
# Usage:
# $status = &check_db_with_product_id($product_id,
# *db_row);
#
# Parameters:
# $product_id = product id in the cart to check
# *db_row = @db_row passed by reference to
# obtain the row that corresponds to the
# product id.
#
# Output:
# $status = whether the product id is has been
# found in the database. If it has not, then
# we know right away that something went wrong.
#
# @db_row is returned by reference to the calling
# sub routine. It contains the row in the DB that
# matches the product ID. This row can then be
# checked by the Web Store to see if other items such
# as price match the cart item. Each element of
# @db_row is a field in the database.
#
############################################################
sub check_db_with_product_id {
local($product_id, *db_row) = @_;
local($db_product_id);
#
# First we open the data file.
# If the open fails. We call the
# file open error routine in order
# to log the error
#
open(DATAFILE, "$sc_data_file_path") ||
&file_open_error("$sc_data_file_path",
"Read Database",__FILE__,__LINE__);
#
# Each line in the data file
# is read into $line variable.
#
# Then, it is split into
# fields which are placed in
# @db_rows.
#
# If it turns out that the
# product id matches the
# product id in the database
# row, the while loop will
# stop, and the db_row will
# contain the row matching
# the product id.
#
while (($line = <DATAFILE>) &&
($product_id ne $db_product_id)) {
@db_row = split(/\|/,$line);
$db_product_id = $db_row[0];
}
close (DATAFILE);
# return the result of the boolean expression
return ($product_id eq $db_product_id);
} # End of check_db_with_product_id
############################################################
#
# subroutine: submit_query
# Usage:
# ($status, $row_count) = &submit_query(*db_rows);
#
# Parameters:
# *db_rows = an empty array is passed by reference
# so that it can be filled with the contents of
# the rows that satisfy the query.
#
# Output:
# $status = blank is no error. It contains an
# abbreviated name of the error that occured if
# there was a problem with the query such as
# "max_rows_exceeded".
#
# $row_count = amount of rows that satisfied query
# even if the count exceeded the maximum allowed.
# This row count will never actually exceed
# 1 above the row count in the flat file version
# of this routine because it is inefficient to
# keep reading a text file if we do not
# intend to present the user with the subsequent
# information.
#
# *db_rows = an array where each row is a row that
# satisfied the results of the query. The rows
# stop being added to this array if $sc_max_rows
# setup variable is exceeded.
#
# Each row contains the fields in a PIPE delimited
# form.
#
############################################################
sub submit_query
{
local(*database_rows) = @_;
local($status);
local(@fields);
local($row_count);
local(@not_found_criteria);
local($line); # Read line from database
#
# exact_match and case_sensitive
# are special form variables
# which alter the behavior of
# keyword searches (string data
# type with the = operator).
#
# Normally keyword searches are
# case insensitive and are not
# exact match searches.
#
local($exact_match) = $form_data{'exact_match'};
local($case_sensitive) = $form_data{'case_sensitive'};
# We initialize row count to 0.
#
$row_count = 0;
#
# The first thing we need to do is
# open the data file and then check to
# see if there was an error doing this.
#
open(DATAFILE, "$sc_data_file_path") ||
&file_open_error("$sc_data_file_path",
"Read Database",__FILE__,__LINE__);
#
# If there was no error opening it,
# then we read each line into $line
# until the file ends or the row count
# exceeds the maximum rows returned plus
# 1.
#
while(($line = <DATAFILE> ) &&
($row_count < $sc_db_max_rows_returned + 1))
{
chop($line); # Chop off extraneous newline
# Each field is split based on the pipe
# delimiter.
@fields = split(/\|/, $line);
# First, we set not_found to zero
# which indicates that we are assuming
# the criteria was satisfied for the
# row.
#
# Then, for each criteria
# specified in @sc_db_query_criteria,
# we call a routine to apply the
# criteria. If the criteria is
# not satisfied, it keeps returning
# 1 which would increment $not_found.
#
# Thus, $not_found will end up being
# the number of criteria that were
# not found. 0 means success.
#
$not_found = 0;
foreach $criteria (@sc_db_query_criteria)
{
$not_found += &flatfile_apply_criteria(
$exact_match,
$case_sensitive,
*fields,
$criteria);
}
# If not found is 0, and
# the row count has not exceeded
# the amount of rows that we
# promised to return,
# the row is pushed into the
# @db_rows array.
#
if (($not_found == 0) &&
($row_count <= $sc_db_max_rows_returned))
{
push(@database_rows, join("\|", @fields));
}
#
# We always want to increment row count even
# if we exceeded the maximum amount of rows
# being returned.
#
# When not_found = 0, that means that the
# criteria was satisfied for the row.
if ($not_found == 0) {
$row_count++;
}
} # End of while datafile has data
# Finally, we close the datafile when
# we are done with it.
close (DATAFILE);
# We passed database rows by reference so that
# no extra copying of the array is needed when
# we return the status.
#
if ($row_count > $sc_db_max_rows_returned) {
$status = "max_rows_exceeded";
}
# Finally, we return the status and
# the row count.
#
return($status,$row_count);
} # End of submit query
############################################################
#
# subroutine: flatfile_apply_criteria
# Usage:
# $status = &flatfile_apply_criteria(
# $exact_match,
# $case_sensitive,
# *fields,
# $criteria);
#
# Parameters:
# $exact_match = on if the user
# selected to perform exact whole word matches
# on the database strings
# $case_sensitive = on if the user
# selected to perform case sensitive matches
# on the database strings.
# *fields is a reference to the array of fields
# in the current database row that we are
# searching.
# $criteria is the current criteria that we
# are applying to the database row. The criteria
# is gathered from the @sc_query_criteria array
# from the setup file.
#
# Output:
# status indicating whether the criteria was
# not found or not. If it is not found, a 1
# is returned. If it is, then a 0 is returned.
#
############################################################
sub flatfile_apply_criteria
{
local($exact_match, $case_sensitive,
*fields, $criteria) = @_;
# format for the $criteria line
# the criteria is pipe delimited and
# consists of the form variable name
# that the criteria will be matched
# against, the fields in the database
# which will be matched against,
# the operator to use in comparison,
# and finally, the data type that the
# operator should use in the comparison
# (date, number, or string comparison).
#
local($c_name, $c_fields, $c_op, $c_type);
# array of c_fields
local(@criteria_fields);
# flag for whether we found something
local($not_found);
# Value for form field
local($form_value);
# Value for db field
local($db_value);
# Date Comparison Place holders
local($month, $year, $day);
local($db_date, $form_date);
# Place marker for current database
# field index we are looking at
local($db_index);
# list of words in a string for matching
local(@word_list);
# Get criteria information
($c_name, $c_fields, $c_op, $c_type) =
split(/\|/, $criteria);
# The criteria can match more than ONE
# field in the database! Thus, we get the
# index values of the fields in each row
# of the database that the form variable
# will be compared against.
#
# Remember, fields and lists in perl
# start counting at 0.
#
@criteria_fields = split(/,/,$c_fields);
# We get the value of the form.
#
$form_value = $form_data{$c_name};
# There are three cases of comparison
# that will return a value.
#
# Case 1: The form field for the criteria
# was not filled out, so the match is
# considered a success.
#
# Remember, if the user does not
# enter a keyword, we want the search
# to be open-ended. Only restrict the
# search if the user chooses to enter
# a search word into the appropriate
# query field.
if ($form_value eq "")
{
return 0;
}
# Case 2: The data type is a
# number or a date. OR if
# the data type is a string
# and the operator is NOT
# =. So we match against the
# operator directly based on the
# data type. (A string,= match
# is considered a separate case
# below).
#
if (($c_type =~ /date/i) ||
($c_type =~ /number/i) ||
($c_op ne "="))
{
# First, we set not_found to yes.
# We assume that the data did not
# match. If any fields match
# the data submitted by the user,
# then, we will set not_found to no
# later on.
$not_found = "yes";
# Go through each database field
# specified in @criteria_fields
# and compare it
foreach $db_index (@criteria_fields)
{
# Get the value of the field in the
# database that corresponds to the
# index number.
$db_value = $fields[$db_index];
# If the type of data comparison
# we are doing is based on a date compare,
# then we need to convert the date
# into the format YYYYMMDD instead of
# MM/DD/YY. This is because YYYYMMDD is
# easier to compare directly. A date
# in the form YYYYMMDD can use the normal
# >,<,etc.. numerical operators to
# compare against.
#
# 2 digit years are converted to 4
# digit years so that this script
# will still comply with the year 2000
# problem.
#
if ($c_type =~ /date/i)
{
($month, $day, $year) =
split(/\//, $db_value);
$month = "0" . $month
if (length($month) < 2);
$day = "0" . $day
if (length($day) < 2);
if ($year > 50 && $year < 1900) {
$year += 1900;
}
if ($year < 1900) {
$year += 2000;
}
$db_date = $year . $month . $day;
($month, $day, $year) =
split(/\//, $form_value);
$month = "0" . $month
if (length($month) < 2);
$day = "0" . $day
if (length($day) < 2);
if ($year > 50 && $year < 1900) {
$year += 1900;
}
if ($year < 1900) {
$year += 2000;
}
$form_date = $year . $month . $day;
# If any of the date comparisons match
# then a 0 is returned to let the submit_query
# routine know that a match was found.
if ($c_op eq ">") {
return 0 if ($form_date > $db_date); }
if ($c_op eq "<") {
return 0 if ($form_date < $db_date); }
if ($c_op eq ">=") {
return 0 if ($form_date >= $db_date); }
if ($c_op eq "<=") {
return 0 if ($form_date <= $db_date); }
if ($c_op eq "!=") {
return 0 if ($form_date != $db_date); }
if ($c_op eq "=") {
return 0 if ($form_date == $db_date); }
#
# If the data type is a number
# then we perform normal number
# comparisons in Perl.
} elsif ($c_type =~ /number/i) {
if ($c_op eq ">") {
return 0 if ($form_value > $db_value); }
if ($c_op eq "<") {
return 0 if ($form_value < $db_value); }
if ($c_op eq ">=") {
return 0 if ($form_value >= $db_value); }
if ($c_op eq "<=") {
return 0 if ($form_value <= $db_value); }
if ($c_op eq "!=") {
return 0 if ($form_value != $db_value); }
if ($c_op eq "=") {
return 0 if ($form_value == $db_value); }
# If the data type is a string
# then we take the operators and
# apply the corresponding Perl string
# operation. For example, != is ne,
# > is gt, etc.
#
} else { # $c_type is a string
if ($c_op eq ">") {
return 0 if ($form_value gt $db_value); }
if ($c_op eq "<") {
return 0 if ($form_value lt $db_value); }
if ($c_op eq ">=") {
return 0 if ($form_value ge $db_value); }
if ($c_op eq "<=") {
return 0 if ($form_value le $db_value); }
if ($c_op eq "!=") {
return 0 if ($form_value ne $db_value); }
}
} # End of foreach $form_field
} else { # End of case 2, Begin Case 3
# Case 3: The data type is a string and
# the operator is =. This is
# more complex because we need
# to check whether our string
# matching matches whole words
# or is case sensitive.
#
# In otherwords, this is a more
# "fuzzy" search.
#
# arguments: $exact_match, $case_sensitive
# affect the search
# In addition, the form_value will be split
# on whitespace so that white-space separated
# words will be searched separately.
#
# Take the words that were entered and parse them into
# an array of words based on word boundary (\s+ splits on
# whitespace)
@word_list = split(/\s+/,$form_value);
# Again, we go through the fields in the
# database that are checked for this
# particular criteria
# definition.
foreach $db_index (@criteria_fields)
{
# Obtain the value of the database field
# we are currently matching against.
$db_value = $fields[$db_index];
$not_found = "yes";
# $match_word is a place marker for the words
# we are going to be looking for in the database row
# $x is a place marker inside the for loops.
local($match_word) = "";
local($x) = "";
####### START OF KEYWORD SEARCH #####
#
# This routine is the same as the HTML
# Search Engine find_keywords subroutine
#
# Basically, the deal is that as the
# words get found, they get removed
# from the @word_list array.
#
# When the array is empty, we know
# that all the keywords were found.
#
# We will later celebrate this
# event by returning the fact that
# a match was found for this criteria.
#
if ($case_sensitive eq "on") {
if ($exact_match eq "on") {
for ($x = @word_list; $x > 0; $x--) {
# \b matches on word boundary
$match_word = $word_list[$x - 1];
if ($db_value =~ /\b$match_word\b/) {
splice(@word_list,$x - 1, 1);
} # End of If
} # End of For Loop
} else {
for ($x = @word_list; $x > 0; $x--) {
$match_word = $word_list[$x - 1];
if ($db_value =~ /$match_word/) {
splice(@word_list,$x - 1, 1);
} # End of If
} # End of For Loop
} # End of ELSE
} else {
if ($exact_match eq "on") {
for ($x = @word_list; $x > 0; $x--) {
# \b matches on word boundary
$match_word = $word_list[$x - 1];
if ($db_value =~ /\b$match_word\b/i) {
splice(@word_list,$x - 1, 1);
} # End of If
} # End of For Loop
} else {
for ($x = @word_list; $x > 0; $x--) {
$match_word = $word_list[$x - 1];
if ($db_value =~ /$match_word/i) {
splice(@word_list,$x - 1, 1);
} # End of If
} # End of For Loop
} # End of ELSE
}
####### END OF KEYWORD SEARCH #######
} # End of foreach $db_index
# If there is nothing left in the word_list
# we want to say that we found the word
# in the $db_value. Thus, $not_found is set to
# "no" in this case.
if (@word_list < 1)
{
$not_found = "no";
}
} # End of case 3
# If not_found is still equal to yes,
# we return a 1, indicating that the
# criteria was not satisfied
#
# If not_found is not yes, then
# we return that a successful match
# was found (0).
#
if ($not_found eq "yes")
{
return 1;
} else {
return 0;
}
} # End of flatfile_apply_criteria
1; # Returns a true value because it is a library file