home *** CD-ROM | disk | FTP | other *** search
Text File | 2002-10-23 | 81.8 KB | 3,254 lines |
- #23456789012345678901234567890123456789012345678901234567890123456789012345
- #
- # Copyright (c) 2001,2002, Dean Arnold
- #
- # You may distribute under the terms of the Artistic License, as
- # specified in the Perl README file.
- #
- # History:
- #
- # 0.80 2002-Sep-13 D. Arnold
- # enhanced syntax in support of DBIx::Chart
- # programmable fonts
- # added chart_type_map to permit external
- # type specs for parameterized datasources
- # add BORDER property
- #
- # 0.73 2002-Sep-11 D. Arnold
- # fix error reporting from ::Plot
- # fix the SYNOPSIS
- #
- # 0.72 2002-Aug-17 D. Arnold
- # fix legend placement
- #
- # 0.71 2002-Aug-12 D. Arnold
- # fix LINEWIDTH property to be local
- # add ANCHORED property
- # fixed VERSION value
- #
- # 0.70 2002-Jun-01 D. Arnold
- # added quadtree plots
- # added cumulative (aka stacked) barcharts
- # fix for individual graph SHOWVALUES
- # added support for official DBI array binding
- # added LINEWIDTH property
- # added chart_map_modifier attribute
- # added installation tests
- #
- # 0.63 2002-May-16 D. Arnold
- # Fix for Gantt date axis alignment
- #
- # 0.62 2002-Apr-22 D. Arnold
- # Fix for numeric month validation
- #
- # 0.61 2001-Mar-14 D. Arnold
- # Fix for multicolor histos
- # Replace hyphenated properties with
- # underscores
- # Support quoted color and shape names
- # Support IN (...) syntax for color, shape, and icon lists
- # added 'dot' shape (contributed by Andrea Spinelli)
- #
- # 0.60 2001-Jan-12 D. Arnold
- # Temporal datatypes
- # Appl. defined colors
- # Histograms
- # composite images (derived tables)
- # Gantt charts
- #
- # 0.52 2001-Dec-14 D. Arnold
- # Fixed 2-D barchart crashes
- #
- # 0.51 2001-Dec-01 D. Arnold
- # Support multicolor single range barcharts
- # Support for 3D piecharts
- # Support for temporal datatypes
- #
- # 0.50 2001-Oct-29 D. Arnold
- # Add ICON(ICONS) property
- # Add COLORS synonym
- # Add FONT property
- # Add GRIDCOLOR property
- # Add TEXTCOLOR property
- # Add Z-AXIS property
- # Add IMAGEMAP output type
- #
- # 0.43 2001-Oct-11 P. Scott
- # Allow a 'gif' (or any future format supported by
- # GD::Image) FORMAT and GIF logos, added use Carp.
- #
- # 0.42 2001-Sep-29 D. Arnold
- # fix to support X-ORIENT='HORIZONTAL' on candlestick and
- # symbolic domains
- #
- # 0.41 2001-Jun-01 D. Arnold
- # fix to strip quotes from string literal in INSERT stmt
- # fix for literal data index in prepare of INSERT
- #
- # 0.40 2001-May-09 D. Arnold
- # fix for final column definition in CREATE TABLE
- # added Y-MIN, Y-MAX
- #
- # 0.21 2001-Mar-17 D. Arnold
- # Remove newlines from SQL stmts in prepare().
- #
- # 0.20 2001-Mar-12 D. Arnold
- # Coded.
- #
- require 5.6.0;
- use strict;
-
- our %mincols = (
- 'PIECHART', 2,
- 'BARCHART', 2,
- 'HISTOGRAM', 2,
- 'POINTGRAPH', 2,
- 'LINEGRAPH', 2,
- 'AREAGRAPH', 2,
- 'CANDLESTICK', 3,
- 'SURFACEMAP', 3,
- 'BOXCHART', 1,
- 'GANTT', 3,
- 'QUADTREE', '3'
- );
-
- our %binary_props = (
- 'SHOWGRID', 1,
- 'X_LOG', 1,
- 'Y_LOG', 1,
- 'THREE_D', 1,
- 'SHOWPOINTS', 1,
- 'KEEPORIGIN', 1,
- 'CUMULATIVE', 1,
- 'STACK', 1,
- 'ANCHORED', 1,
- 'BORDER', 1
- );
-
- our %num_props = (
- 'SHOWVALUES', 1
- );
-
- our %string_props = (
- 'X_AXIS', 1,
- 'Y_AXIS', 1,
- 'Z_AXIS', 1,
- 'TITLE', 1,
- 'SIGNATURE', 1,
- 'LOGO', 1,
- 'X_ORIENT', 1,
- 'FORMAT', 1,
- 'TEMPLATE', 1,
- 'MAPURL', 1,
- 'MAPSCRIPT', 1,
- 'MAPNAME', 1,
- 'MAPTYPE', 1
- );
-
- our %trans_props = (
- 'X-AXIS', 'X_AXIS',
- 'Y-AXIS', 'Y_AXIS',
- 'Z-AXIS', 'Z_AXIS',
- 'X-LOG', 'X_LOG',
- 'Y-LOG', 'Y_LOG',
- '3-D', 'THREE_D',
- 'Y-MAX', 'Y_MAX',
- 'Y-MIN', 'Y_MIN',
- 'COLORS', 'COLOR',
- 'ICONS', 'ICON',
- 'SHAPES', 'SHAPE',
- 'X-ORIENT', 'X_ORIENT',
- 'CUMULATIVE', 'STACK'
- );
-
- our %valid_props = (
- 'SHOWVALUES', 1,
- 'SHOWPOINTS', 1,
- 'BACKGROUND', 1,
- 'KEEPORIGIN', 1,
- 'SIGNATURE', 1,
- 'SHOWGRID', 1,
- 'X-AXIS', 1,
- 'Y-AXIS', 1,
- 'Z-AXIS', 1,
- 'X_AXIS', 1,
- 'Y_AXIS', 1,
- 'Z_AXIS', 1,
- 'TITLE', 1,
- 'COLOR', 1,
- 'COLORS', 1,
- 'WIDTH', 1,
- 'HEIGHT', 1,
- 'SHAPE', 1,
- 'SHAPES', 1,
- 'X-ORIENT', 1,
- 'X_ORIENT', 1,
- 'FORMAT', 1,
- 'LOGO', 1,
- 'X-LOG', 1,
- 'Y-LOG', 1,
- '3-D', 1,
- 'Y-MAX', 1,
- 'Y-MIN', 1,
- 'X_LOG', 1,
- 'Y_LOG', 1,
- 'THREE_D', 1,
- 'Y_MAX', 1,
- 'Y_MIN', 1,
- 'ICON', 1,
- 'ICONS', 1,
- 'FONT', 1,
- 'TEMPLATE', 1,
- 'GRIDCOLOR', 1,
- 'TEXTCOLOR', 1,
- 'MAPURL', 1,
- 'MAPSCRIPT', 1,
- 'MAPNAME', 1,
- 'MAPTYPE', 1,
- 'CUMULATIVE', 1,
- 'STACK', 1,
- 'LINEWIDTH', 1,
- 'ANCHORED', 1,
- 'BORDER', 1
- );
-
- our %valid_colors = (
- white => [255,255,255],
- lgray => [191,191,191],
- gray => [127,127,127],
- dgray => [63,63,63],
- black => [0,0,0],
- lblue => [0,0,255],
- blue => [0,0,191],
- dblue => [0,0,127],
- gold => [255,215,0],
- lyellow => [255,255,0],
- yellow => [191,191,0],
- dyellow => [127,127,0],
- lgreen => [0,255,0],
- green => [0,191,0],
- dgreen => [0,127,0],
- lred => [255,0,0],
- red => [191,0,0],
- dred => [127,0,0],
- lpurple => [255,0,255],
- purple => [191,0,191],
- dpurple => [127,0,127],
- lorange => [255,183,0],
- orange => [255,127,0],
- pink => [255,183,193],
- dpink => [255,105,180],
- marine => [127,127,255],
- cyan => [0,255,255],
- lbrown => [210,180,140],
- dbrown => [165,42,42],
- transparent => [1,1,1]
- );
-
- our @dfltcolors = qw( red green blue yellow purple orange
- dblue cyan dgreen lbrown );
-
- our %valid_shapes = (
- 'fillsquare', 1,
- 'opensquare', 2,
- 'horizcross', 3,
- 'diagcross', 4,
- 'filldiamond', 5,
- 'opendiamond', 6,
- 'fillcircle', 7,
- 'opencircle', 8,
- 'icon', 9,
- 'dot', 10);
-
- {
- package DBD::Chart;
-
- use DBI;
- use DBI qw(:sql_types);
-
- # Do NOT @EXPORT anything.
- $DBD::Chart::VERSION = '0.80';
-
- $DBD::Chart::drh = undef;
- $DBD::Chart::err = 0;
- $DBD::Chart::errstr = '';
- $DBD::Chart::state = '00000';
- %DBD::Chart::charts = (); # defined chart list;
- # hash of (name, property hash)
- $DBD::Chart::seqno = 1; # id for each CREATEd chart so we don't access
- # stale names
-
- sub driver {
- #
- # if we've already been init'd, don't do it again
- #
- return $DBD::Chart::drh if $DBD::Chart::drh;
- my($class, $attr) = @_;
- $class .= '::dr';
-
- $DBD::Chart::drh = DBI::_new_drh($class,
- {
- 'Name' => 'Chart',
- 'Version' => $DBD::Chart::VERSION,
- 'Err' => \$DBD::Chart::err,
- 'Errstr' => \$DBD::Chart::errstr,
- 'State' => \$DBD::Chart::state,
- 'Attribution' => 'DBD::Chart by Dean Arnold'
- });
- DBI->trace_msg("DBD::Chart v.$DBD::Chart::VERSION loaded on $^O\n", 1);
- #
- # generate the base colormap
- #
- my %table = ();
- $table{columns} = {
- 'NAME' => 0,
- 'REDVALUE' => 1,
- 'GREENVALUE' => 2,
- 'BLUEVALUE' => 3 };
- $table{types} = [ SQL_VARCHAR, SQL_INTEGER, SQL_INTEGER, SQL_INTEGER ];
- $table{precisions} = [ 30, 4, 4, 4 ];
- $table{scales} = [ 0, 0, 0, 0 ];
- $table{version} = 1.0;
- my @ary = ( [ ], [ ], [ ], [ ] );
- foreach my $color (keys(%valid_colors)) {
- push(@{$ary[0]}, $color);
- push(@{$ary[1]}, $valid_colors{$color}->[0]);
- push(@{$ary[2]}, $valid_colors{$color}->[1]);
- push(@{$ary[3]}, $valid_colors{$color}->[2]);
- }
- $table{data} = \@ary;
- $DBD::Chart::charts{COLORMAP} = \%table;
-
- return $DBD::Chart::drh;
- }
-
- 1;
- }
-
- #
- # check on attributes
- #
- { package DBD::Chart::dr; # ====== DRIVER ======
- $DBD::Chart::dr::imp_data_size = 0;
-
- # we use default connect()
-
- sub disconnect_all { }
- sub DESTROY { undef }
-
- 1;
- }
-
- { package DBD::Chart::db; # ====== DATABASE ======
- $DBD::Chart::db::imp_data_size = 0;
- use Carp;
-
- use DBI qw(:sql_types);
- use constant SQL_INTERVAL_HR2SEC => 110;
- #
- # for compatibility between DBI pre 1.200
- # and new DBI
-
- my %typeval = (
- 'CHAR', SQL_CHAR,
- 'VARCHAR', SQL_VARCHAR,
- 'INT', SQL_INTEGER,
- 'SMALLINT', SQL_SMALLINT,
- 'TINYINT', SQL_TINYINT,
- 'FLOAT', SQL_FLOAT,
- 'DEC', SQL_DECIMAL,
- 'DATE', SQL_DATE,
- 'TIMESTAMP', SQL_TIMESTAMP,
- 'INTERVAL', SQL_INTERVAL_HR2SEC,
- 'TIME', SQL_TIME
- );
-
- my %typeszs = (
- 'CHAR', 1,
- 'VARCHAR', 32000,
- 'INT', 4,
- 'SMALLINT', 2,
- 'TINYINT', 1,
- 'FLOAT', 8,
- 'DEC', 4,
- 'DATE', 4,
- 'TIMESTAMP', 26,
- 'INTERVAL', 26,
- 'TIME', 16
- );
-
- my %inv_pieprop = (
- 'SHAPE', 1,
- 'SHAPES', 1,
- 'SHOWGRID', 1,
- 'SHOWPOINTS', 1,
- 'X-AXIS', 1,
- 'Y-AXIS', 1,
- 'Z-AXIS', 1,
- 'X_AXIS', 1,
- 'Y_AXIS', 1,
- 'Z_AXIS', 1,
- 'SHOWVALUES', 1,
- 'X-LOG', 1,
- 'Y-LOG', 1,
- 'Y-MAX', 1,
- 'Y-MIN', 1,
- 'X_LOG', 1,
- 'Y_LOG', 1,
- 'Y_MAX', 1,
- 'Y_MIN', 1,
- 'ICON', 1,
- 'ICONS', 1,
- 'CUMULATIVE', 1,
- 'STACK', 1,
- 'LINEWIDTH', 1,
- 'ANCHORED', 1
- );
-
- my %inv_quadtree = (
- 'SHAPE', 1,
- 'SHAPES', 1,
- 'SHOWGRID', 1,
- 'SHOWPOINTS', 1,
- 'X-AXIS', 1,
- 'Y-AXIS', 1,
- 'Z-AXIS', 1,
- 'X_AXIS', 1,
- 'Y_AXIS', 1,
- 'Z_AXIS', 1,
- 'SHOWVALUES', 1,
- 'X-LOG', 1,
- 'Y-LOG', 1,
- 'Y-MAX', 1,
- 'Y-MIN', 1,
- 'X_LOG', 1,
- 'Y_LOG', 1,
- 'Y_MAX', 1,
- 'Y_MIN', 1,
- 'ICON', 1,
- 'ICONS', 1,
- 'CUMULATIVE', 1,
- 'STACK', 1,
- 'ANCHORED', 1,
- 'LINEWIDTH', 1
- );
-
- my %inv_barprop = (
- 'SHAPE', 1,
- 'SHAPES', 1,
- 'SHOWPOINTS', 1,
- 'X-LOG', 1,
- 'X_LOG', 1,
- 'LINEWIDTH', 1
- );
-
- my %inv_candle = (
- 'X_LOG', 1,
- 'THREE_D', 1,
- 'X-LOG', 1,
- 'ANCHORED', 1,
- '3-D', 1
- );
-
- #
- # defaults for simple queries
- my %dfltprops = (
- 'SHAPE', undef,
- 'WIDTH', 300,
- 'HEIGHT', 300,
- 'SHOWGRID', 0,
- 'SHOWPOINTS', 0,
- 'SHOWVALUES', 0,
- 'X_AXIS', 'X axis',
- 'Y_AXIS', 'Y axis',
- 'Z_AXIS', undef,
- 'TITLE', '',
- 'COLORS', \@dfltcolors,
- 'X_LOG', 0,
- 'Y_LOG', 0,
- 'THREE_D', 0,
- 'BACKGROUND', 'white',
- 'SIGNATURE', undef,
- 'LOGO', undef,
- 'X_ORIENT', 'DEFAULT',
- 'FORMAT', 'PNG',
- 'KEEPORIGIN', 0,
- 'Y_MAX', undef,
- 'Y_MIN', undef,
- 'ICONS', undef,
- 'FONT', undef,
- 'GRIDCOLOR', 'black',
- 'TEXTCOLOR', 'black',
- 'TEMPLATE', undef,
- 'MAPURL', undef,
- 'MAPSCRIPT', undef,
- 'MAPNAME', undef,
- 'MAPTYPE', undef,
- 'STACK', undef,
- 'ANCHORED', 1,
- 'LINEWIDTH', undef,
- 'BORDER', 1
- );
- #
- # default globals for composite queries
- my %dfltglobals = (
- 'WIDTH', 300,
- 'HEIGHT', 300,
- 'SHOWGRID', 0,
- 'X_AXIS', 'X axis',
- 'Y_AXIS', 'Y axis',
- 'TITLE', '',
- 'X_LOG', 0,
- 'Y_LOG', 0,
- 'THREE_D', 0,
- 'BACKGROUND', 'white',
- 'SIGNATURE', undef,
- 'LOGO', undef,
- 'X_ORIENT', 'DEFAULT',
- 'FORMAT', 'PNG',
- 'KEEPORIGIN', 0,
- 'FONT', undef,
- 'GRIDCOLOR', 'black',
- 'TEXTCOLOR', 'black',
- 'TEMPLATE', undef,
- 'MAPURL', undef,
- 'MAPSCRIPT', undef,
- 'MAPNAME', undef,
- 'MAPTYPE', undef,
- 'BORDER', 1
- );
- #
- # default subquery props for composite queries
- my %dfltcomposites = (
- 'SHAPE', undef,
- 'SHOWPOINTS', 0,
- 'SHOWVALUES', 0,
- 'COLORS', \@dfltcolors,
- 'ICONS', undef,
- 'STACK', undef,
- 'ANCHORED', 1,
- 'LINEWIDTH', undef
- );
- #
- # map of compatible chart types in composite
- # images
- my %compatibility = (
- 'PIECHART', undef,
- 'QUADTREE', undef,
- 'BOXCHART',
- {
- 'BARCHART' => 1,
- 'POINTGRAPH' => 1,
- 'LINEGRAPH' => 1,
- 'AREAGRAPH' => 1,
- 'CANDLESTICK' => 1,
- 'BOXCHART' => 1
- },
- 'HISTOGRAM', { 'HISTOGRAM' => 1 },
- 'SURFACEMAP', { 'SURFACEMAP' => 1 },
- 'BARCHART',
- {
- 'BARCHART' => 1,
- 'POINTGRAPH' => 1,
- 'LINEGRAPH' => 1,
- 'AREAGRAPH' => 1,
- 'CANDLESTICK' => 1,
- 'BOXCHART' => 1
- },
-
- 'POINTGRAPH',
- {
- 'BARCHART' => 1,
- 'POINTGRAPH' => 1,
- 'LINEGRAPH' => 1,
- 'AREAGRAPH' => 1,
- 'CANDLESTICK' => 1,
- 'BOXCHART' => 1
- },
- 'LINEGRAPH',
- {
- 'BARCHART' => 1,
- 'POINTGRAPH' => 1,
- 'LINEGRAPH' => 1,
- 'AREAGRAPH' => 1,
- 'BOXCHART' => 1,
- 'CANDLESTICK' => 1
- },
- 'AREAGRAPH',
- {
- 'BARCHART' => 1,
- 'POINTGRAPH' => 1,
- 'LINEGRAPH' => 1,
- 'AREAGRAPH' => 1,
- 'BOXCHART' => 1,
- 'CANDLESTICK' => 1
- },
- 'CANDLESTICK',
- {
- 'BARCHART' => 1,
- 'POINTGRAPH' => 1,
- 'LINEGRAPH' => 1,
- 'AREAGRAPH' => 1,
- 'BOXCHART' => 1,
- 'CANDLESTICK' => 1
- }
- );
- #
- # map the global properties for composites
- my %global_props = (
- 'BACKGROUND', 1,
- 'KEEPORIGIN', 1,
- 'SIGNATURE', 1,
- 'SHOWGRID', 1,
- 'X_AXIS', 1,
- 'Y_AXIS', 1,
- 'Z_AXIS', 1,
- 'TITLE', 1,
- 'WIDTH', 1,
- 'HEIGHT', 1,
- 'X_ORIENT', 1,
- 'FORMAT', 1,
- 'LOGO', 1,
- 'X_LOG', 1,
- 'Y_LOG', 1,
- 'THREE_D', 1,
- 'Y_MAX', 1,
- 'Y_MIN', 1,
- 'TEMPLATE', 1,
- 'GRIDCOLOR', 1,
- 'TEXTCOLOR', 1,
- 'MAPURL', 1,
- 'MAPSCRIPT', 1,
- 'MAPNAME', 1,
- 'MAPTYPE', 1,
- 'BORDER', 1
- );
-
- sub check_color {
- my ($color) = @_;
-
- my $table = $DBD::Chart::charts{COLORMAP};
- my $col1 = $table->{data}->[0];
- my $c;
- foreach $c (@$col1) {
- return 1 if ($color eq $c);
- }
- return undef;
- }
-
- sub parse_col_defs {
- my ($req, $cols, $typeary, $typelen, $typescale) = @_;
- #
- # normalize
- #
- $req = uc $req;
- $req =~s/(\S),/$1 ,/g;
- $req =~s/,(\S)/, $1/g;
- $req =~s/(\S)\(/$1 \(/g;
- $req =~s/(\S)\)/$1 \)/g;
-
- $req=~s/\s+NOT\s+NULL//ig;
- $req =~s/\bLONG\s+VARCHAR\b/ VARCHAR(32000)/g;
- $req =~s/\bCHAR\s+VARYING\b/ VARCHAR/g;
- $req =~s/\bDOUBLE\s+PRECISION\b/ FLOAT /g;
- $req =~s/\bNUMERIC\b/ DEC /g;
- $req =~s/\bREAL\b/ FLOAT /g;
- $req =~s/\bCHARACTER\b/ CHAR /g;
- $req =~s/\bINTEGER\b/ INT /g;
- $req =~s/\bDECIMAL\b/ DEC /g;
- #
- # normalize a bit more
- #
- $req =~s/\(\s+/\(/g;
- $req =~s/\s+\)/\)/g;
- $req =~s/\((\d+)\s*\,\s*(\d+)\)/\($1\;$2\)/g;
- $req =~s/\s\((\d+)/\($1/g;
- #
- # extract each declaration in the list
- #
- my @reqdecs = split(',', $req);
- my $decl = '';
- my $typecnt = 0;
- my $decsz = 0;
- my $decscal = 0;
- my $name = '';
- %$cols = ();
- @$typelen = ();
- @$typeary = ();
- @$typescale = ();
- my $i = 0;
- foreach $decl (@reqdecs) {
-
- $_ = $decl;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Column $1 already defined.",
- return undef
- if ((/^\s*(\S+)\s+/) && ($$cols{$1}));
-
- $name = $1;
- $$cols{$name} = $i++;
-
- push(@$typelen, $typeszs{$decl}),
- push(@$typescale, 0),
- push(@$typeary, $typeval{$decl}),
- next
- if (($decl) = /^\s*\S+\s+(TIMESTAMP|SMALLINT|INTERVAL|TINYINT|VARCHAR|FLOAT|CHAR|DATE|TIME|INT|DEC)\s*$/i);
-
- push(@$typelen, $decsz),
- push(@$typescale, 0),
- push(@$typeary, $typeval{$decl}),
- next
- if (($decl, $decsz) = /^\s*\S+\s+(VARCHAR|CHAR)\s*\((\d+)\)\s*$/i);
-
- push(@$typelen, $decsz),
- push(@$typescale, 0),
- push(@$typeary, SQL_DECIMAL),
- next
- if ((($decsz) = /^\s*\S+\s+DEC\s*\((\d+)\)\s*$/i) &&
- ($decsz < 19) && ($decsz > 0));
- #
- # handle scaled decimal declarations
- #
- push(@$typelen, $decsz),
- push(@$typescale, $decscal),
- push(@$typeary, SQL_DECIMAL),
- next
- if ((($decsz, $decscal) =
- /^\s*\S+\s+DEC\s*\((\d+);(\d+)\)\s*$/i) &&
- ($decsz < 19) && ($decsz > 0) && ($decscal < $decsz));
-
- # if we get here, we've got something bogus
- $DBD::Chart::err = -1;
- $_=~s/;/,/;
- $DBD::Chart::errstr = "Invalid column definition $_"; ;
- return undef;
- }
- return $i;
- }
-
- sub restore_strings {
- my ($prop, $t, $strlits) = @_;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "$prop property requires a string.",
- return undef
- unless ($t=~/^<\d+>/);
- #
- # in case it was an empty string, restore the quotes
- my $str = '\'';
- $str .= $$strlits[$1]. '\'',
- $t = $2
- while ($t=~/^<(\d+)>(.*)$/);
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "$prop property requires a string.",
- return undef
- if ($t ne '');
-
- $str=~s/''/'/g;
- $str=~s/^'(.*)'$/$1/g;
- return $str;
- }
-
- sub parse_props {
- my ($ctype, $t, $numphs, $is_subquery, $strlits) = @_;
-
- my %props = $is_subquery ? %dfltcomposites : ($ctype eq 'IMAGE' ? %dfltglobals : %dfltprops);
- my ($prop, $op);
- $t=~s/\s*AND\s*/\r/ig;
- my @preds = split("\r", $t);
-
- foreach (@preds) {
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Unrecognized property declaration.",
- return (undef, $t)
- unless (($prop, $op, $t)=/^([^\s=]+)\s*(=|IN)\s*(.+)$/i);
-
- $prop = uc $prop;
- $op = uc $op;
- $t=~s/\s*$//;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Unrecognized property $prop.",
- return (undef, $t)
- unless $valid_props{$prop};
- #
- # translate the property if it has synonym
- $prop = $trans_props{$prop} if $trans_props{$prop};
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Property $prop not valid with valuelist.",
- return (undef, $t)
- if (($op eq 'IN') && ($prop!~/^COLOR|SHAPE|ICON|FONT$/));
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Property $prop not valid in subquery.",
- return (undef, $t)
- if ($is_subquery && $global_props{$prop});
- #
- # got a placeholder
- #
- $props{ $prop } = "?$$numphs",
- $$numphs++,
- next
- if ($t eq '?');
-
- if ($binary_props{$prop}) {
- #
- # make sure its zero or 1
- #
- $props{ $prop } = $t,
- next
- if (($t == 1) || ($t == 0));
-
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = "Invalid value for $prop property.";
- return (undef, $t);
- }
- if ($prop eq 'SHOWVALUES') {
- $props{ $prop } = $t,
- next
- if (($t=~/^\d+$/) && ($t >= 0) && ($t <= 100));
-
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = "Invalid value for $prop property.";
- return (undef, $t);
- }
- if ($string_props{$prop}) {
-
- $props{$prop} = restore_strings($prop, $t, $strlits);
- return (undef, $t)
- unless defined($props{$prop});
- next;
- }
- if (($prop eq 'WIDTH') || ($prop eq 'HEIGHT')) {
-
- $props{ $prop } = $t,
- next
- if (($t=~/^\d+$/) && ($t >= 10) && ($t <= 100000));
-
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = "Invalid value for $prop property.";
- return (undef, $t);
- }
-
- if ($prop eq 'LINEWIDTH') {
-
- $props{ $prop } = $t,
- next
- if (($t=~/^\d+$/) && ($t > 0) && ($t <= 100));
-
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = 'Invalid value for LINEWIDTH property.';
- return (undef, $t);
- }
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Y_MAX and Y_MIN deprecated as of release 0.50.',
- next
- if (($prop eq 'Y_MAX') || ($prop eq 'Y_MIN'));
-
- if (($prop eq 'BACKGROUND') || ($prop eq 'GRIDCOLOR') ||
- ($prop eq 'TEXTCOLOR')) {
-
- $t = restore_strings($prop, $t, $strlits)
- if ($t=~/<\d+>/);
- $t = lc $t;
- $props{$prop} = $t,
- next
- if (check_color($t) ||
- (($prop eq 'BACKGROUND') && ($t eq 'transparent')));
-
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = "Invalid value for $prop property.";
- return (undef, $t);
- }
-
- if (($prop eq 'COLOR') || ($prop eq 'SHAPE') || ($prop eq 'FONT')) {
- my @colors = ();
- $props{ $prop } = \@colors;
-
- $t = restore_strings($prop, $t, $strlits)
- if ($t=~/^<\d+>$/);
- push(@colors, $t),
- next
- unless ($t=~/^\(([^\)]+)\)$/);
-
- $t = lc $1;
- $t=~s/\s+//g;
- @colors = split(',', $t);
- for (my $i = 0; $i <= $#colors; $i++) {
- next if (uc $colors[$i] eq 'NULL');
- $colors[$i] = "?$$numphs",
- $$numphs++,
- next
- if ($colors[$i] eq '?');
-
- next unless ($colors[$i]=~/^<\d+>$/);
- $colors[$i] = restore_strings($prop, $colors[$i], $strlits);
- }
- next;
- }
- if ($prop eq 'ICON') {
- my @icons = ();
- $props{ $prop } = \@icons;
-
- $t = restore_strings($prop, $t, $strlits)
- if ($t=~/^<\d+>$/);
-
- $icons[0] = $t,
- next
- unless ($t=~/^\(([^\)]+)\)$/);
-
- $t = $1;
- $t=~s/\s+//g;
- @icons = split(',', $t);
- for (my $i = 0; $i <= $#icons; $i++) {
- next if (uc $icons[$i] eq 'NULL');
- $icons[$i] = "?$$numphs",
- $$numphs++,
- next
- if ($icons[$i] eq '?');
- next unless ($icons[$i]=~/^<\d+>$/);
- $icons[$i] = restore_strings($prop, $icons[$i], $strlits);
- }
- }
- } # end while
-
- if (defined($props{COLOR})) {
- my $colors = $props{COLOR};
- foreach $prop (@$colors) {
- next unless defined($prop);
- next if check_color($prop);
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Unknown color $prop.",
- return (undef, $t)
- }
- }
- if (defined($props{SHAPE})) {
- my $shapes = $props{SHAPE};
- foreach $prop (@$shapes) {
- next unless defined($prop);
- next if ($valid_shapes{$prop} || ($prop eq 'null'));
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = "Unknown point shape $prop.";
- return (undef, $t);
- }
- }
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Invalid value for 'X_ORIENT' property.",
- return (undef, $t)
- if (($props{X_ORIENT}) &&
- ($props{X_ORIENT}!~/^(HORIZONTAL|VERTICAL|DEFAULT)$/i));
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Invalid value for 'MAPTYPE' property.",
- return (undef, $t)
- if (($props{MAPTYPE}) && ($props{MAPTYPE}!~/^(HTML|PERL)$/i));
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Only alphanumerics and _ allowed for 'MAPNAME' property.",
- return (undef, $t)
- if (($props{MAPNAME}) && ($props{MAPNAME}=~/\W/));
-
- return (\%props, $t);
- }
-
- sub parse_predicate {
- my ($collist, $predcol, $predop, $predval, $numphs, $ccols, $ctypes) = @_;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Invalid predicate.',
- return undef
- unless ($collist=~/^([^\s\=<>]+)\s*(<>|<=|>=|=|>|<)\s*(.*)$/);
-
- my $tname = uc $1;
- $$predop = $2;
- $collist = $3;
- $$predcol = $$ccols{$tname};
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = "Unknown column $tname.",
- return undef
- unless defined($$predcol);
-
- $$predval = '?',
- $$numphs++,
- return 1
- if ($collist=~/^\s*\?\s*$/i);
- #
- # start pessimistically
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = "Invalid value for column $tname.";
-
- return undef
- if ((($$ctypes[$$predcol] == SQL_FLOAT) ||
- ($$ctypes[$$predcol] == SQL_DECIMAL)) &&
- ($collist!~/^[\+\-]?\d+(\.\d+(E[+|-]?\d+)?)$/i));
-
- return undef
- if ((($$ctypes[$$predcol] == SQL_INTEGER) ||
- ($$ctypes[$$predcol] == SQL_SMALLINT) ||
- ($$ctypes[$$predcol] == SQL_TINYINT))&&
- ($collist!~/^[\+\-]?\d+$/));
-
- return undef
- if (($$ctypes[$$predcol] == SQL_DATE) &&
- ($collist!~/^'\d+[\-\/\.](\d+|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)[\-\/\.]\d+'$/i));
-
- return undef
- if (($$ctypes[$$predcol] == SQL_TIMESTAMP) &&
- ($collist!~/^'\d+[\-\/\.](\d+|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)[\-\/\.]\d+\s+\d+:\d+:\d+(.\d+)?'$/i));
-
- return undef
- if (($$ctypes[$$predcol] == SQL_TIME) &&
- ($collist!~/^'\d+:\d+:\d+(.\d+)?'$/));
-
- return undef
- if (($$ctypes[$$predcol] == SQL_INTERVAL_HR2SEC) &&
- ($collist=~/^'[\+\-]?\d+:\d+:\d+(.\d+)?'$/));
-
- $DBD::Chart::err = 0,
- $DBD::Chart::errstr = '',
- $$predval = $collist,
- return 1
- if (($$ctypes[$$predcol] != SQL_CHAR) &&
- ($$ctypes[$$predcol] != SQL_VARCHAR));
-
- return undef
- unless ($collist=~/^('[^']*')(.*)$/);
-
- $$predval = $1;
- $collist = $2;
-
- $$predval .= $1,
- $collist= $2
- while ($collist=~/^('[^']*')(.*)$/);
-
- $DBD::Chart::err = 0;
- $DBD::Chart::errstr = '';
- return 1;
- }
-
- sub validate_time {
- my ($time) = @_;
- my ($hr, $min, $sec) = split(':', $time);
- return (($hr >= 0) && ($hr < 24) && ($min >= 0) && ($min < 60) && ($sec >= 0) && ($sec < 60));
- }
-
- sub validate_interval {
- #
- # eventually support full intervals (years, months, days...)
- #
- my ($hr, $min, $sec, $subsec) = @_;
- return undef if (defined($hr) && ($min > 60));
- return undef if (defined($min) && ($sec > 60));
- #
- # convert to seconds only float value
- #
- $sec += $hr * 3600 if $hr;
- $sec += $min * 60 if $min;
- $sec .= $subsec if $subsec;
- return $sec;
- }
-
- sub validate_value {
- my ($coltype, $remnant, $cprec, $errstr) = @_;
-
- $$remnant = $4,
- return $1
- if ((($coltype == SQL_FLOAT) ||
- ($coltype == SQL_DECIMAL)) &&
- ($$remnant=~/^([\+\-]?\d+(\.\d+(E[+|-]?\d+)?)?)\s*,\s*(.*)$/i));
-
- $$remnant = $2,
- return $1
- if ((($coltype == SQL_INTEGER) ||
- ($coltype == SQL_SMALLINT) ||
- ($coltype == SQL_TINYINT)) &&
- ($$remnant=~/^([\+\-]?\d+)\s*,\s*(.*)$/i));
-
- if ($coltype == SQL_DATE) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless ($$remnant=~/^'((\d+)([\-\.\/])(\w+)([\-\.\/])(\d+))'\s*,\s*(.*)$/i);
-
- my ($date, $yr, $sep1, $mo, $sep2, $day) = ($1, $2, $3, uc $4, $5, $6);
- $$remnant = $7;
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless (((($mo=~/^\d+$/) && ($mo > 0) && ($mo < 13)) ||
- ($mo=~/^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/i)) &&
- ($day < 32) && ($day > 0));
- #
- # should probably verify date is valid!
- #
- return $date;
- }
- if ($coltype == SQL_INTERVAL_HR2SEC) {
- #
- # currently only support intervals up to hourly precision
- #
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless ($$remnant=~/^'([\-\+]?(\d+:)?(\d+:)?(\d+)(\.\d+)?)'\s*,\s*(.*)$/);
- my ($time, $hr, $min, $sec, $subsec) = ($2, $3, $4, $5);
- $$remnant = $6;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless defined(validate_interval($hr, $min, $sec, $subsec));
- return $time;
- }
- if ($coltype == SQL_TIME) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless ($$remnant=~/^'(\d\d?:\d\d:\d\d(\.\d+)?)'\s*,\s*(.*)$/i);
- my ($time, $subsec) = ($1, $2);
- $$remnant = $3;
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless validate_time($time);
- #
- # NOTE: we discard subseconds here
- # should we permit AM/PM indications ?
- #
- return $time;
- }
- if ($coltype == SQL_TIMESTAMP) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless ($$remnant=~/^'((\d+)([\-\.\/])(\w+)([\-\.\/])(\d+)\s+(\d\d?:\d\d:\d\d(\.\d+)?))'\s*,\s*(.*)$/i);
- my ($tmstamp, $yr, $sep1, $mo, $sep2, $day, $time, $subsec) = ($1, $2, $3, uc $4, $5, $6, $7, $8);
- $$remnant = $9;
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless (((($mo=~/^\d+$/) && ($mo > 0) && ($mo < 13)) ||
- ($mo=~/^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/i)) &&
- ($day < 32) && ($day > 0) && validate_time($time));
- #
- # should probably verify date is valid!
- # and convert to seconds since epoc (or some other baseline value)
- # NOTE: we discard subseconds here
- #
- return $tmstamp;
- }
- if (($coltype == SQL_CHAR) || ($coltype == SQL_VARCHAR)) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $errstr,
- return undef
- unless ($$remnant=~/^'([^']*)'(.*)$/);
-
- my $str = $1;
- $$remnant= $2;
-
- $str .= '\'' . $1,
- $$remnant= $2
- while ($$remnant=~/^'([^']*)'(.*)$/);
-
- $$remnant=~s/^\s*,\s*//;
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- "String value exceeds defined length.",
- return undef
- if (length($str) > $cprec);
-
- return $str;
- }
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = $errstr;
- return undef;
- }
-
- sub prepare {
- my($dbh, $statement, $attrs)= @_;
- my $i;
- my $tstmt = $statement;
- $tstmt=~s/^\s*(.+);?\s*$/$1/;
- $tstmt=~s/\n/ /g;
- #
- # validate that its a CREATE, DROP, INSERT, or SELECT
- #
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Only CREATE { TABLE | CHART }, DROP { TABLE | CHART }, ' .
- 'SELECT, INSERT, UPDATE, or DELETE statements supported.',
- return undef
- if ($tstmt!~/^(SELECT|CREATE|INSERT|UPDATE|DELETE|DROP)\s+(.+)$/i);
-
- my ($cmd, $remnant) = ($1, $2);
- $cmd = uc $cmd;
- my ($filenm, $collist, $tcols);
- if ($cmd=~/(CREATE|DROP)/) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Only CREATE { TABLE | CHART }, DROP { TABLE | CHART }, ' .
- 'SELECT, INSERT, UPDATE, or DELETE statements supported.',
- return undef
- if ($remnant!~/^(TABLE|CHART)\s+(CHART\.)?(\w+)\s*(.*)$/i);
-
- ($filenm, $remnant) = ($3, $4);
- $filenm = uc $filenm;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Unrecognized DROP statement.',
- return undef
- if (($cmd eq 'DROP') && ($remnant ne ''));
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Cannot CREATE/DROP COLORMAP table.',
- return undef
- if ($filenm eq 'COLORMAP');
- }
- elsif ($cmd eq 'UPDATE') {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Invalid UPDATE statement.',
- return undef
- unless ($remnant=~/^(CHART\.)?(\w+)\s+SET\s+(.+)$/i);
-
- ($filenm, $remnant) = ($2, $3);
- $filenm = uc $filenm;
- }
- elsif ($cmd eq 'DELETE') {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Invalid DELETE statement.',
- return undef
- unless ($remnant=~/^FROM\s+(CHART\.)?(\w+)\s*(.*)$/i);
-
- ($filenm, $remnant) = ($2, $3);
- $filenm = uc $filenm;
- if ($remnant ne '') {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Invalid DELETE statement.',
- return undef
- unless ($remnant=~/^WHERE\s+(.+)$/i);
-
- $remnant = $1;
- }
- }
- elsif ($cmd eq 'INSERT') {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Invalid INSERT statement.',
- return undef
- if ($remnant!~/^INTO\s+(CHART\.)?(\w+)\s+VALUES\s*\(\s*(.+)\s*\)$/i);
- ($filenm, $remnant) = ($2, $3);
- $filenm = uc $filenm;
- }
-
- my $chart;
- if (($cmd ne 'CREATE') && ($cmd ne 'SELECT')) {
- $chart = $DBD::Chart::charts{$filenm};
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $filenm . ' does not exist.',
- return undef
- unless $chart;
- }
-
- my ($ccols, $ctypes, $cprecs, $cscales);
- $ccols = $$chart{columns}, # a hashref (name, position)
- $ctypes = $$chart{types}, # an arrayref of types
- $cprecs = $$chart{precisions}, # an arrayref of precisions
- $cscales = $$chart{scales}, # an arrayref of scales
- if (($cmd eq 'UPDATE') || ($cmd eq 'INSERT') || ($cmd eq 'DELETE'));
-
- my %cols = ();
- my @typeary = ();
- my @typelens = ();
- my @typescale = ();
-
- my $numphs = 0;
- my @dtypes = (); # list of chart types
- my @dcharts = (); # list of per-chart datasources
- my @dnames = (); # list of per-chart names
- my @dprops = (); # list of per-chart properties
- my @dcols = (); # list of per-chart datasource column names
- my %dversions = (); # list of per-chart datasource versions
- my %setcols = ();
- my @parmcols = ();
- my ($tname, $props, $cnum, $predicate, $ctype);
- my $imagemap = undef;
- my ($predcol, $predop, $predval) = ('','','');
-
- if ($cmd eq 'CREATE') {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- $filenm . ' has already been CREATEd.',
- return undef
- if ($chart);
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Unrecognized CREATE statement.',
- return undef
- if ($remnant!~/^\((.+)\)$/);
-
- $remnant = $1;
- my $colcnt = parse_col_defs($remnant, \%cols, \@typeary,
- \@typelens, \@typescale);
- return undef if (! $colcnt);
- }
- elsif ($cmd eq 'DROP') { }
- elsif ($cmd eq 'INSERT') {
- #
- # normalize valuelist so we can count ph's
- #
- $remnant .= ',';
- $cnum = -1;
- while ($remnant ne '') {
- $cnum++;
-
- $remnant = $1,
- push(@parmcols, $cnum),
- $numphs++,
- next
- if ($remnant=~/^\?\s*,\s*(.*)$/);
-
- $remnant = $1,
- $setcols{$cnum} = undef,
- next
- if ($remnant=~/^NULL\s*,\s*(.*)$/i);
-
- $setcols{$cnum} = validate_value($$ctypes[$cnum], \$remnant,
- $$cprecs[$cnum], "Invalid value for column at position $cnum.");
- return undef
- unless defined($setcols{$cnum});
- }
- $DBD::Chart::errstr =
- 'Value list does not match column definitions.',
- $DBD::Chart::err = -1,
- return undef
- if ($cnum+1 != scalar(keys(%$ccols)));
- }
- elsif ($cmd eq 'UPDATE') {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Unrecognized UPDATE statement.',
- return undef
- if ($remnant!~/^(.+)\s+WHERE\s+(.+)$/i);
-
- $collist = $1;
- $predicate = $2;
- #
- # scan SET list to count ph's and validate literals
- #
- $collist .= ',';
- $tname = '';
- while ($collist ne '') {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Invalid SET clause.',
- return undef
- if ($collist!~/^([^\s\=]+)\s*\=\s*(.+)$/);
-
- $tname = uc $1;
- $collist = $2;
- $cnum = $$ccols{$tname};
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- "Unknown column $tname in UPDATE statement.",
- return undef
- unless defined($cnum);
-
- $collist = $1,
- push(@parmcols, $cnum),
- $numphs++,
- next
- if ($collist=~/^\?\s*,\s*(.*)$/);
-
- $collist = $1,
- $setcols{$cnum} = undef,
- next
- if ($collist=~/^NULL\s*,\s*(.*)$/i);
-
- $setcols{$cnum} = validate_value($$ctypes[$cnum], \$collist,
- $$cprecs[$cnum], "Invalid value for column $tname.");
- return undef
- unless defined($setcols{$cnum});
- }
- #
- # get predicate; only 1 allowed
- #
- if ($predicate ne '') {
- return undef unless
- parse_predicate($predicate, \$predcol, \$predop, \$predval,
- \$numphs, $ccols, $ctypes);
- }
- }
- elsif ($cmd eq 'DELETE') {
- #
- # get predicate; only 1 allowed
- #
- return undef unless
- parse_predicate($remnant, \$predcol, \$predop, \$predval,
- \$numphs, $ccols, $ctypes);
- }
- else { # must be SELECT
- if ($remnant=~/^\*\s+FROM\s+(CHART\.)?COLORMAP\s+(WHERE\s+NAME\s*=\s*(.+))?$/i) {
- #
- # its a COLORMAP query, handle special
- #
- my $charttype = 'COLORMAP';
- my $flds = '*';
- my $pred = 'NAME = ' . uc $3;
- my($outer, $sth) = DBI::_new_sth($dbh, {
- 'Statement' => $statement,
- });
- $dversions{COLORMAP} = 1;
- $sth->{chart_dbh} = $dbh;
- $sth->{chart_cmd} = $cmd;
- $sth->{chart_name} = 'COLORMAP';
- $sth->{chart_qnames} = undef;
- $sth->{chart_charttypes} = [ 'COLORMAP' ];
- $sth->{chart_sources} = [ 'COLORMAP' ];
- $sth->{chart_properties} = [ $pred ];
- $sth->{chart_version} = \%dversions;
- $sth->{chart_imagemap} = undef;
- $sth->STORE('NUM_OF_FIELDS', 4);
- $sth->STORE('NUM_OF_PARAMS', 1)
- if ($pred=~/^\s*\?\s*$/);
- $sth->{NAME} = [ 'Name', 'RedValue', 'BlueValue', 'GreenValue' ];
- $sth->{TYPE} = [ SQL_VARCHAR, SQL_INTEGER, SQL_INTEGER, SQL_INTEGER ];
- $sth->{PRECISION} = [ 30, 4, 4, 4 ];
- $sth->{SCALE} = [ 0, 0, 0, 0 ];
- $sth->{NULLABLE} = [ undef, undef, undef, undef ];
- return $outer;
- }
- #
- # normalize the query to isolate subqueries
- # replace all literal strings before processing
- #
- my @strlits = ();
- my $num = 0;
- push(@strlits, $1),
- $remnant=~s/'.*?'/<$num>/,
- $num++
- while ($remnant=~/'(.*?)'/);
-
- $remnant=~s/\)\s+WHERE\s+/\rWHERE /i;
- $remnant=~s/\)(\s+(\w+))\s+WHERE\s+/$1\rWHERE /i;
- $remnant=~s/\s+FROM\s+\(\s*SELECT\s*/\r/i; # isolate first subquery
- $remnant=~s/\s*\)\s*,\s*\(\s*SELECT\s*/\r/ig; # isolate individual queries
- $remnant=~s/\s*\)(\s+(\w+))\s*,\s*\(\s*SELECT\s*/$1\r/ig; # isolate individual queries
- my @queries = split("\r", $remnant);
-
- if ($#queries > 0) {
- #
- # accumulate subquery names
- foreach $i (1..$#queries) {
- next
- unless ($queries[$i]=~/\s+(\w+)$/);
- $dnames[$i] = uc $1;
- $queries[$i]=~s/\s+(\w+)$//;
- }
- }
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Invalid composite chart specification.',
- return undef
- unless (($#queries == 0) ||
- (($queries[0]=~/^IMAGE(\s*,\s*IMAGEMAP)?$/i) && ($queries[1]!~/^WHERE/i)));
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'No subqueries provided for composite chart.',
- return undef
- if (($#queries == 0) &&
- ($queries[0]=~/^IMAGE(\s*,\s*IMAGEMAP)?$/i));
-
- my $is_composite = 1 if $#queries;
- if ($is_composite) {
- #
- # get global properties
- #
- $imagemap = 1 if ($queries[0]=~/^IMAGE(\s*\(\s*\*\s*\))?\s*,\s*IMAGEMAP$/i);
- push @dtypes, 'IMAGE';
- push @dcharts, undef;
- push @dcols, undef;
- shift @queries;
- $remnant = ($queries[$#queries]=~/^WHERE/i) ? pop(@queries) : undef;
- $dprops[0] = \%dfltglobals;
- if (($remnant) && ($remnant=~/^WHERE\s+(.+)$/i)) {
- #
- # process format properties
- #
- ($props, $remnant) = parse_props('IMAGE', $1, \$numphs, undef, \@strlits);
- return undef if (! $props);
- $dprops[0] = $props;
- }
- }
- foreach $remnant (@queries) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Unrecognized SELECT statement.',
- return undef
- unless ($remnant=~/^(CANDLESTICK|SURFACEMAP|POINTGRAPH|HISTOGRAM|LINEGRAPH|AREAGRAPH|PIECHART|BARCHART|BOXCHART|QUADTREE|GANTT)(\s*\(\s*([^\)]+)\))?(\s*,\s*IMAGEMAP)?\s+FROM\s+(\?|\w+)\s*(.*)$/i);
-
- $ctype = uc $1;
- my $colnames = uc $3;
- $imagemap = uc $4 unless ($imagemap || (! $4));
- $filenm = uc $5;
- $remnant = $6;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'IMAGEMAP not valid in subquery.',
- return undef
- if ($is_composite && $4);
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Incompatible chart types in composite image.',
- return undef
- if (($is_composite) && ($#dtypes > 0) &&
- (! $compatibility{$dtypes[1]}->{$ctype}));
- #
- # collect any column-list values
- #
- my $cols = [ '*' ];
- $colnames=$1
- if ($colnames && ($colnames=~/^\s*(.+)\s*$/));
- @$cols = split(',', $colnames)
- if ($colnames && ($colnames ne '*'));
- $$cols[$_]=~s/^\s*(.+)\s*$/$1/ foreach (0..$#$cols);
-
- if ($filenm ne '?') {
- $chart = $DBD::Chart::charts{$filenm};
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $filenm . ' does not exist.',
- return undef
- unless $chart;
-
- $ctypes = $$chart{types};
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $ctype .
- ' chart requires at least ' .
- $mincols{$ctype} . ' columns.',
- return undef
- if (scalar(@$ctypes) < $mincols{$ctype});
- #
- # validate any column list
- #
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $ctype .
- ' chart requires at least ' .
- $mincols{$ctype} . ' columns.',
- return undef
- if (scalar(@$ctypes) < $mincols{$ctype});
-
- $dversions{$filenm} = $$chart{version};
- }
- else {
- $filenm = "?$numphs";
- $numphs++;
- }
- $imagemap = 1
- if ($imagemap);
- push(@dtypes, $ctype);
- push(@dcharts, $filenm);
- push(@dcols, $cols);
- if ($remnant=~/^WHERE\s+(.+)$/i) {
- #
- # process format properties
- #
- ($props, $remnant) = parse_props($ctype, $1, \$numphs, $is_composite, \@strlits);
- return undef if (! $props);
- push(@dprops, $props);
- }
- else {
- push(@dprops, ($is_composite ? \%dfltcomposites : \%dfltprops));
- }
- } # end foreach query
- }
-
- my($outer, $sth) = DBI::_new_sth($dbh, {
- 'Statement' => $statement,
- });
-
- $sth->STORE('NUM_OF_PARAMS', $numphs);
- $sth->{chart_dbh} = $dbh;
- $sth->{chart_cmd} = $cmd;
- $sth->{chart_name} = $filenm;
-
- $sth->{chart_precisions} = \@typelens,
- $sth->{chart_types} = \@typeary,
- $sth->{chart_scales} = \@typescale,
- $sth->{chart_columns} = \%cols
- if ($cmd eq 'CREATE');
-
- $sth->{chart_predicate} = [ $predcol, $predop, $predval ]
- if ((($cmd eq 'UPDATE') || ($cmd eq 'DELETE')) &&
- (defined($predcol)));
-
- $sth->{chart_version} = $$chart{version},
- $sth->{chart_param_cols} = \@parmcols
- if (($cmd eq 'UPDATE') || ($cmd eq 'DELETE') || ($cmd eq 'INSERT'));
-
- $sth->{chart_columns} = \%setcols
- if (($cmd eq 'UPDATE') || ($cmd eq 'INSERT'));
-
- if ($cmd eq 'SELECT') {
- $sth->{chart_charttypes} = \@dtypes;
- $sth->{chart_sources} = \@dcharts;
- $sth->{chart_columns} = \@dcols;
- $sth->{chart_properties} = \@dprops;
- $sth->{chart_version} = \%dversions;
- $sth->{chart_imagemap} = $imagemap;
- $sth->{chart_qnames} = \@dnames;
- $sth->{chart_map_modifier} = $attrs->{chart_map_modifier}
- if ($attrs && $attrs->{chart_map_modifier} &&
- ref $attrs->{chart_map_modifier} &&
- (ref $attrs->{chart_map_modifier} eq 'CODE'));
- #
- # added external name/type/precision/scale mapping
- # app will provide [ { NAME => [ ], TYPE => [ ], PRECISION => [ ], SCALE => [ ] }, ... ]
- # (one hashref per param'd datasource)
- # this is mostly to support DBD::CSV, DBD::File
- #
- $sth->{chart_type_map} = $attrs->{chart_type_map}
- if ($attrs && $attrs->{chart_type_map} &&
- ref $attrs->{chart_type_map} &&
- (ref $attrs->{chart_type_map} eq 'ARRAY'));
-
- if ($imagemap) {
- $sth->STORE('NUM_OF_FIELDS', 2);
- $sth->{NAME} = [ '', '' ];
- $sth->{TYPE} = [ SQL_VARBINARY, SQL_VARCHAR ];
- $sth->{PRECISION} = [ undef, undef ];
- $sth->{SCALE} = [ 0, 0 ];
- $sth->{NULLABLE} = [ undef, undef ];
- }
- else {
- $sth->STORE('NUM_OF_FIELDS', 1);
- $sth->{NAME} = [ '' ];
- $sth->{TYPE} = [ SQL_VARBINARY ];
- $sth->{PRECISION} = [ undef ];
- $sth->{SCALE} = [ 0 ];
- $sth->{NULLABLE} = [ undef ];
- }
- }
-
- $outer;
- }
-
- sub FETCH {
- my ($dbh, $attrib) = @_;
- return $dbh->{$attrib} if ($attrib=~/^chart_/);
- return 1 if $attrib eq 'AutoCommit';
- return $dbh->DBD::_::db::FETCH($attrib);
- }
-
- sub STORE {
- my ($dbh, $attrib, $value) = @_;
- $dbh->{$attrib} = $value and return 1 if ($attrib=~/^chart_/);
- if ($attrib eq 'AutoCommit') {
- return 1 if $value; # is already set
- croak("Can't disable AutoCommit");
- }
-
- return $dbh->DBD::_::db::STORE($attrib, $value);
- }
-
- sub disconnect {
- my $dbh = shift;
-
- $dbh->STORE(Active => 0);
- my $fname = $dbh->{chart_name};
- return 1 if (! $fname);
- delete $DBD::Chart::charts{$fname};
-
- 1;
- }
-
- sub DESTROY {
- #
- # close any open file here
- #
- my $dbh = shift;
- $dbh->disconnect if ($dbh->{Active});
- 1;
- }
-
- 1;
- }
-
- {
- package DBD::Chart::st; # ====== STATEMENT ======
- use DBI qw(:sql_types);
- use Carp;
- use Time::Local;
-
- $DBD::Chart::st::imp_data_size = 0;
-
- use GD;
- use DBD::Chart::Plot;
-
- use constant SQL_INTERVAL_HR2SEC => 110;
-
- my %strpredops = (
- '=', 'eq',
- '<>', 'ne',
- '<', 'lt',
- '<=', 'le',
- '>', 'gt',
- '>=', 'ge'
- );
-
- my %numpredops = (
- '=', '==',
- '<>', '!=',
- '<', '<',
- '<=', '<=',
- '>', '>',
- '>=', '>='
- );
-
- my %numtype = (
- SQL_INTEGER, 1,
- SQL_SMALLINT, 1,
- SQL_TINYINT, 1,
- SQL_DECIMAL, 1,
- SQL_FLOAT, 1
- );
-
- my %symboltype = (
- SQL_CHAR, 1,
- SQL_VARCHAR, 1
- );
-
- my %timetype = (
- SQL_DATE, 'YYYY-MM-DD',
- SQL_TIME, 'HH:MM:SS',
- SQL_TIMESTAMP, 'YYYY-MM-DD HH:MM:SS',
- SQL_INTERVAL_HR2SEC, '+HH:MM:SS'
- );
-
- my %month = ( 'JAN', 0, 'FEB', 1, 'MAR', 2, 'APR', 3, 'MAY', 4, 'JUN', 5,
- 'JUL', 6, 'AUG', 7, 'SEP', 8, 'OCT', 9, 'NOV', 10, 'DEC', 11);
-
- my @quadcolors = qw(
- black blue purple green red orange yellow white
- );
-
- sub check_color {
- my ($color) = @_;
-
- my $table = $DBD::Chart::charts{COLORMAP};
- my $col1 = $table->{data}->[0];
- my $c;
- foreach $c (@$col1) {
- return 1 if ($color eq $c);
- }
- return undef;
- }
-
- sub get_colormap {
- my $table = $DBD::Chart::charts{COLORMAP};
- my ($color, $r, $g, $b) = @{$table->{data}};
- my %map;
- for (my $i = 0; $i <= $#$color; $i++) {
- $map{$$color[$i]} = [ $$r[$i], $$g[$i], $$b[$i] ];
- }
- return \%map;
- }
-
- sub validate_value {
- my ($p, $ttype, $parmsts, $k, $i) = @_;
-
- return 1
- if (($ttype == SQL_CHAR) || ($ttype == SQL_VARCHAR));
-
- return 1
- if (($p=~/^[\-\+]?\d+$/) &&
- (($ttype == SQL_INTEGER) ||
- (($ttype == SQL_SMALLINT) && ($p > -32768) && ($p < 32768)) ||
- (($ttype == SQL_TINYINT) && ($p > -128) && ($p < 128)))
- );
-
- return 1
- if ((($ttype == SQL_FLOAT) || ($ttype == SQL_DECIMAL)) &&
- ($p=~/^[\-\+]?\d+(\.\d+(E[\-\+]?\d+)?)?$/i));
-
- if (($ttype == SQL_DATE) &&
- ($p=~/^(\d+)[\-\.\/](\w+)[\-\.\/](\d+)$/i)) {
-
- my ($yr, $mo, $day) = ($1, uc $2, $3);
- return 1
- if (((($mo=~/^\d+$/) && ($mo > 0) && ($mo < 13)) ||
- ($mo=~/^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/i)) &&
- ($day < 32) && ($day > 0));
- }
- if (($ttype == SQL_INTERVAL_HR2SEC) &&
- ($p=~/^[\-\+]?((\d+):)?((\d+):)?(\d+)(\.\d+)?/)) {
- my ($hr, $min, $sec, $subsec) = ($2, $4, $5, $6);
- return 1
- if (((! $min) || ($min < 60)) && ($sec < 60));
- }
- if (($ttype == SQL_TIME) &&
- ($p=~/^(\d+):(\d+):(\d+)(\.\d+)?$/)) {
- my ($hr, $min, $sec, $subsec) = ($1, $2, $3, $4);
- return 1
- if (($hr < 24) && ($min < 60) && ($sec < 60));
- }
- if (($ttype == SQL_TIMESTAMP) &&
- ($p=~/^(\d+)[\-\.\/](\w+)[\-\.\/](\d+)\s+(\d+):(\d+):(\d+)(\.\d+)?$/i)) {
- my ($yr, $mo, $day, $hr, $min, $sec, $subsec) = ($1, $2, uc $3, $4, $5, $6, $7);
- return 1
- if (((($mo=~/^\d+$/) && ($mo > 0) && ($mo < 13)) ||
- ($mo=~/^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/i)) &&
- ($day < 32) && ($day > 0) &&
- ($hr < 24) && ($min < 60) && ($sec < 60));
- }
-
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr =
- "Supplied value not compatible with target field at parameter $i.";
- if ($parmsts) {
- $$parmsts[$k] =
- "Supplied value not compatible with target field at parameter $i.",
- return undef
- if (ref $parmsts eq 'ARRAY');
- $$parmsts{$k} =
- "Supplied value not compatible with target field at parameter $i."
- }
- return undef;
- }
-
- sub validate_properties {
- my ($props, $parms) = @_;
- foreach my $prop (keys(%$props)) {
- next if ((! $$props{$prop}) || ($$props{$prop} !~/^\?(\d+)$/));
- my $phnum = $1;
- my $t = $$parms[$phnum];
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Insufficient parameters provided.',
- return undef
- if ($phnum > scalar(@$parms));
-
- $$props{$prop} = $$parms[$phnum];
-
- next if (($binary_props{$prop}) && ($t=~/^(0|1)$/));
-
- next if ($string_props{$prop});
-
- next if ((($prop eq 'WIDTH') || ($prop eq 'HEIGHT')) &&
- (($t=~/^\d+$/) && ($t >= 10) && ($t <= 100000)));
-
- next if ((($prop eq 'BACKGROUND') || ($prop eq 'GRIDCOLOR') ||
- ($prop eq 'TEXTCOLOR')) && (check_color($t)));
-
- next if (($prop eq 'X_ORIENT') &&
- ($t=~/^(HORIZONTAL|VERTICAL|DEFAULT)$/i));
-
- next if (($prop eq 'COLOR') && (check_color($t)));
-
- next if (($prop eq 'SHAPE') && ($valid_shapes{$t}));
- #
- # invalid property parameter value
- #
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr = "Invalid value for $prop property.";
- return undef;
- }
- return 1;
- }
- #
- # official DBI array binding i/f
- #
- sub execute_array {
- my($sth, $attribs, @bind_values) = @_;
-
- $sth->bind_param_status($$attribs{ArrayTupleStatus}) if $$attribs{ArrayTupleStatus};
-
- if (@bind_values) {
- $sth->bind_param_array($_, $bind_values[$_])
- foreach (1..@bind_values);
- }
-
- return $sth->execute();
- }
-
- sub execute {
- my($sth, @bind_values) = @_;
- my $parms = (@bind_values) ?
- \@bind_values : $sth->{chart_params};
-
- my ($i, $j, $k, $p, $t);
- my ($predval, $is_parmref, $data, $pctype, $is_parmary, $ttype);
- my ($paramcols, $maxary, $chart, $props, $predtype);
- my ($columns, $types, $precs, $scales, $verify, $numcols);
-
- my $cmd = $sth->{chart_cmd};
- my $dbh = $sth->{chart_dbh};
- my $name = $sth->{chart_name};
- my $typeary = $sth->{chart_types};
- $precs = $sth->{chart_precisions};
- $scales = $sth->{chart_scales};
-
- my $cols = $sth->{chart_columns}
- if ($cmd eq 'CREATE');
-
- my $setcols = $sth->{chart_columns}
- if (($cmd eq 'UPDATE') || ($cmd eq 'INSERT'));
-
- my $predicate = $sth->{chart_predicate}
- if (($cmd eq 'UPDATE') || ($cmd eq 'DELETE'));
-
- if ($cmd eq 'CREATE') {
- #
- # save the description info
- #
- my @ary;
- for ($i = 0; $i < scalar(keys(%$cols)); $i++) {
- my @colary = ();
- push(@ary, \@colary);
- }
-
- $DBD::Chart::charts{$name} = {
- 'columns' => $cols,
- 'types' => $typeary,
- 'precisions' => $precs,
- 'scales' => $scales,
- 'version' => $DBD::Chart::seqno++,
- 'data' => \@ary
- };
- return -1;
- }
-
- if ($cmd eq 'DROP') {
- $chart = $DBD::Chart::charts{$name};
- delete $$chart{columns};
- delete $$chart{types};
- delete $$chart{precisions};
- delete $$chart{scales};
- my $ary = $$chart{data};
- if ($ary) {
- foreach my $g (@$ary) {
- @$g = ();
- }
- }
- delete $$chart{data};
- delete $DBD::Chart::charts{$name};
- return -1;
- }
-
- my $parmsts = $sth->{chart_parmsts};
- if ($cmd ne 'SELECT') {
- #
- # validate our chart info in case a DROP was executed
- # between prepare and execute
- #
- $chart = $DBD::Chart::charts{$name};
- $DBD::Chart::errstr = "Chart $name does not exist.",
- $DBD::Chart::err = -1,
- return undef
- unless $chart;
- #
- # verify that the chart versions are identical
- #
- $DBD::Chart::errstr =
- "Prepared version of $chart differs from current version.",
- $DBD::Chart::err = -1,
- return undef
- unless ($$chart{version} == $sth->{chart_version});
- #
- # get the record description
- #
- $columns = $$chart{columns};
- $types = $$chart{types};
- $precs = $$chart{precisions};
- $scales = $$chart{scales};
- $data = $$chart{data};
- #
- # check for param arrays or inout params
- #
- ($is_parmref, $is_parmary, $maxary) = (0, 0, 1);
- $verify = ($sth->{chart_noverify}) ? 0 : 1;
-
- $DBD::Chart::errstr =
- 'Number of parameters supplied does not match number required.',
- $DBD::Chart::err = -1,
- return undef
- if (($sth->{NUM_OF_PARAMS}) && ((! $parms) ||
- (scalar(@$parms) != $sth->{NUM_OF_PARAMS})));
-
- $parmsts = $sth->{chart_parmsts};
- $predicate = $sth->{chart_predicate};
- $predtype = $$types[$$predicate[0]] if ($predicate);
- $paramcols = $sth->{chart_param_cols};
- $numcols = scalar(@$paramcols);
- if (($verify) && ($parms)) {
- $p = $$parms[0];
- $is_parmref = 1 if ((ref $$parms[0]));
- $is_parmary = 1
- if (($is_parmref) && (ref $$parms[0] eq 'ARRAY'));
- $maxary = scalar(@$p) if ($is_parmary);
- for ($i = 1; $i < $sth->{NUM_OF_PARAMS}; $i++) {
- my $p = $$parms[$i];
- $DBD::Chart::errstr =
- 'All parameters must be of same type (scalar, scalarref, or arrayref).',
- $DBD::Chart::err = -1,
- return undef
- if ( (($is_parmref) && (! (ref $p) ) ) ||
- ((! $is_parmref) && (ref $p)));
-
-
- $DBD::Chart::errstr =
- 'All parameters must be of same type (scalar, scalarref, or arrayref).',
- $DBD::Chart::err = -1,
- return undef
- if ((($is_parmary) && ((! (ref $p)) || (ref $p ne 'ARRAY'))) ||
- ((! $is_parmary) && (ref $p) && (ref $p eq 'ARRAY')));
- #
- # validate param arrays are consistent
- #
- $DBD::Chart::errstr =
- 'All parameter arrays must be the same size.',
- $DBD::Chart::err = -1,
- return undef
- if (($is_parmary) && (scalar(@$p) != $maxary));
- }
- #
- # validate param values before we apply them
- #
- for ($k = 0; $k < $maxary; $k++) {
- for ($i = 0; $i < $numcols; $i++) {
- $ttype = $$types[$$paramcols[$i]];
- $p = $$parms[$i];
- $p = (($is_parmary) ? $$p[$k] : $$p) if ($is_parmref);
- next if (! defined($p));
- #
- # verify param types and literals are compatible with target fields
- #
- return undef unless validate_value($p, $ttype, $parmsts, $k, $i);
- }
- #
- # predicates always come last, so they'll be last param
- #
- if (($predicate) && ($$predicate[2] eq '?')) {
- $ttype = $$types[$$predicate[0]];
- $p = $$parms[$i];
- $p = (($is_parmary) ? $$p[$k] : $$p) if ($is_parmref);
- #
- # verify param types and literals are compatible with target fields
- #
- if (! defined($p))
- {
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr =
- 'NULL values not allowed in predicates.';
- if ($parmsts) {
- $$parmsts[$k] =
- 'NULL values not allowed in predicates.',
- return undef
- if (ref $parmsts eq 'ARRAY');
- $$parmsts{$k} =
- 'NULL values not allowed in predicates.';
- }
- return undef;
- }
-
- return undef unless validate_value($p, $ttype, $parmsts, $k, $i);
- }
- }
- }
- }
- #
- # for COLORMAP, we need to validate before applying
- #
- if ($name eq 'COLORMAP') {
- #
- # check literals
- #
- foreach $i (keys(%$setcols)) {
- my $v = $$setcols{$i};
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'NULL values not valid for COLORMAP fields.',
- return undef
- unless defined($v);
-
- next unless $i; # only proceed for RGB values
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Invalid value for COLORMAP component field.',
- return undef
- if (($v < 0) || ($v > 255));
- }
- #
- # then check params
- #
- for ($j = 0; $j < scalar(@$paramcols); $j++) {
- $i = $$paramcols[$j];
-
- for ($k = 0; $k < $maxary; $k++) {
-
- $p = $$parms[$j];
- $p = (($is_parmary) ? $$p[$k] : $$p) if ($is_parmref);
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'NULL values not valid for COLORMAP fields.',
- return undef
- unless defined($p);
- #
- # need to push this error on the param status list (if one exists)
- #
- next unless $i; # only proceed for RGB components
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- "Invalid value for COLORMAP component field.",
- return undef
- if (($p!~/^\d+$/) || ($p > 255));
- }
- }
- }
-
- if ($cmd eq 'INSERT') {
- #
- # apply any literals
- #
- foreach $i (keys(%$setcols)) {
- $t = $$data[$i];
- my $v = $$setcols{$i};
- push(@$t, (($v) x $maxary));
- }
- #
- # then apply the params
- #
- $k = 1;
- for ($j = 0; $j < scalar(@$paramcols); $j++) {
- $i = $$paramcols[$j];
- $t = $$data[$i];
- $ttype = $$types[$i];
- for ($k = 0; $k < $maxary; $k++) {
- #
- # merge input params and statement literals
- #
- $p = $$parms[$j];
- $p = (($is_parmary) ? $$p[$k] : $$p) if ($is_parmref);
-
- if (defined($p) &&
- (($ttype == SQL_CHAR) || ($ttype == SQL_VARCHAR)) &&
- (length($p) > $$precs[$i])) {
- #
- # need to push this error on the param status list (if one exists)
- #
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr =
- "Supplied value truncated at parameter $j.";
-
- $p = substr($p, 0, $$precs[$i]);
-
- $$parmsts[$k] =
- "Supplied value truncated at parameter $j."
- if ($parmsts && (ref $parmsts eq 'ARRAY'));
- $$parmsts{$k} =
- "Supplied value truncated at parameter $j."
- if ($parmsts && (ref $parmsts ne 'ARRAY'));
- }
- push(@$t, $p);
- }
- } # end foreach value
- return $k;
- }
-
- if ($cmd eq 'UPDATE') {
- #
- # check predicate to determine row numbers to update
- #
- if (! $predicate) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Parameter arrays not allowed for unqualified UPDATE.',
- return undef
- if ($is_parmary);
- #
- # apply any literals
- #
- foreach $i (keys(%$setcols)) {
- $t = $$data[$i];
- my $v = $$setcols{$i};
- $j = scalar(@$t);
- @$t = ($v) x $j;
- }
- #
- # then apply params
- #
- for ($j = 0; $j < scalar(@$paramcols); $j++) {
- $i = $$paramcols[$j];
- $t = $$data[$i];
- $k = scalar(@$t);
- $ttype = $$types[$i];
- $p = $$parms[$j];
- $p = $$p if ($is_parmref);
-
- if (defined($p) &&
- (($ttype == SQL_CHAR) || ($ttype == SQL_VARCHAR)) &&
- (length($p) > $$precs[$i])) {
- #
- # need to push this error on the param status list (if one exists)
- #
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr =
- "Supplied value truncated at parameter $j.";
-
- $p = substr($p, 0, $$precs[$i]);
-
- $$parmsts[$k] =
- "Supplied value truncated at parameter $j."
- if ($parmsts && (ref $parmsts eq 'ARRAY'));
- $$parmsts{$k} =
- "Supplied value truncated at parameter $j."
- if ($parmsts && (ref $parmsts ne 'ARRAY'));
- }
- @$t = ($p) x $k;
- }
- return 1;
- } # end if no predicate
- #
- # build ary of rownums based on predicate
- #
- $predval = $$predicate[2];
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Parameter arrays not allowed for literally qualified UPDATE.',
- return undef
- if (($predval ne '?') && ($is_parmary));
-
- my %rowmap = eval_predicate($$predicate[0], $$predicate[1],
- $predval, $types, $data, $parms, $is_parmary, $is_parmref,
- $maxary);
-
- return 0 unless scalar(%rowmap);
- #
- # apply any literals
- #
- my ($x, $y);
- foreach $i (keys(%$setcols)) {
- $t = $$data[$i];
- while (($x, $y) = each(%rowmap)) {
- $$t[$x] = $$setcols{$i};
- }
- }
- #
- # then apply params
- #
- for ($j = 0; $j < scalar(@$paramcols); $j++) {
- $i = $$paramcols[$j];
- $t = $$data[$i];
- $ttype = $$types[$i];
- while (($x, $y) = each(%rowmap)) {
- $p = $$parms[$j];
- $p = (($is_parmary) ? $$p[$y] : $$p) if ($is_parmref);
- if ((($ttype == SQL_CHAR) || ($ttype == SQL_VARCHAR) ||
- ($ttype == SQL_BINARY) || ($ttype == SQL_VARBINARY)) &&
- (length($p) > $$precs[$i])) {
- #
- # need to push this error on the param status list (if one exists)
- #
- $DBD::Chart::err = -1;
- $DBD::Chart::errstr =
- "Supplied value truncated at parameter $j.";
-
- $p = substr($p, 0, $$precs[$i]);
- }
- $$t[$x] = $p;
- }
- }
- return scalar(keys(%rowmap));
- }
-
- if ($cmd eq 'DELETE') {
- if (! $predicate) {
- #
- # apply any literals
- #
- $k = scalar(@{$$data[0]});
- foreach $t (@$data) {
- @$t = ();
- }
- return $k;
- } # end if no predicate
- #
- # build ary of rownums based on predicate
- #
- my %rowmap = eval_predicate($$predicate[0], $$predicate[1],
- $$predicate[2], $types, $data, $parms, $is_parmary,
- $is_parmref, $maxary);
-
- return 0 unless scalar(%rowmap);
-
- my @rownums = sort(keys(%rowmap));
- $j = scalar(@rownums);
- while ($k = pop(@rownums)) {
- for ($i = 0; $i < scalar(@$data); $i++) {
- $t = $$data[$i];
- splice(@$t, $k, 1);
- }
- }
- return $j;
- }
- #
- # must be SELECT, so render the chart
- #
- my $dtypes = $sth->{chart_charttypes};
- my $dcharts = $sth->{chart_sources};
- my $dprops = $sth->{chart_properties};
- my $dversions = $sth->{chart_version};
- my $dcols = $sth->{chart_columns};
- my $dnames = $sth->{chart_qnames};
- my $srcsth;
- my @dcolidxs = ();
- #
- # if COLORMAP, just fetch and return
- #
- if ($$dcharts[0] && ($$dcharts[0] eq 'COLORMAP')) {
- my $table = $DBD::Chart::charts{COLORMAP};
- my $col1 = $table->{data}->[0];
- if (defined($$props{NAME})) {
- #
- # selecting single color, setup for the fetch
- #
- if ($$props{NAME}=~/^\?(\d+)$/) {
- my $phnum = $1;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Insufficient parameters provided.',
- return undef
- if ($phnum > scalar(@$parms));
-
- $sth->{chart_colormap} = $$parms[$phnum];
- }
- else {
- $sth->{chart_colormap} = $$props{NAME};
- }
- my $color;
- foreach $color (@$col1) {
- last if ($color eq $sth->{chart_colormap});
- }
- return '0E0' if ($color ne $sth->{chart_colormap});
- $sth->{chart_1_color} = 1;
- return 1;
- }
- #
- # selecting all colors
- #
- delete $sth->{chart_1_color};
- $sth->{chart_colormap} = 0;
- return scalar @$col1;
- }
-
- for ($i = 0; $i <= $#$dcharts; $i++) {
- $name = $$dcharts[$i];
- next unless (($i > 0) || $name); # for composite images
- $srcsth = undef;
- if ($name!~/^\?(\d+)$/) {
- $chart = $DBD::Chart::charts{$name};
-
- $DBD::Chart::errstr = "Chart $name does not exist.",
- $DBD::Chart::err = -1,
- return undef
- unless $chart;
-
- $DBD::Chart::errstr =
- "Prepared version of $name differs from current version.",
- $DBD::Chart::err = -1,
- return undef
- if ($$chart{version} != $$dversions{$name});
-
- }
- else { # its a parameterized chartsource
- my $phn = $1;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Parameterized chartsource not provided.',
- return undef
- unless $$parms[$phn];
-
- $srcsth = $$parms[$phn];
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Parameterized chartsource value must be a prepared and executed DBI statement handle.',
- return undef
- if ((ref $srcsth ne 'DBI::st') && (ref $srcsth ne 'DBIx::Chart::st'));
-
- my $ctype = $$dtypes[$i];
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $ctype . ' chart requires at least ' .
- $mincols{$ctype} . ' columns.',
- return undef
- if ($srcsth->{NUM_OF_FIELDS} < $mincols{$ctype});
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'CANDLESTICK chart requires 2N + 1 columns.',
- return undef
- if (($ctype eq 'CANDLESTICK') && (! $$dprops[$i]->{STACK}) &&
- (($srcsth->{NUM_OF_FIELDS} - 1) & 1));
- #
- # collect and validate the column specification
- #
- my $cols = $$dcols[$i];
- my $colidxs = [ ];
- $dcolidxs[$i] = $colidxs;
- @$colidxs = (0..($srcsth->{NUM_OF_FIELDS} - 1)),
- next
- if ($$cols[0] eq '*');
-
- my ($d, $idx);
- $columns = get_ext_type_info($sth, $srcsth, 'NAME', ($i ? $i-1 : 0) );
- foreach $d (0..$#$columns) {
- $$columns[$d] = uc $$columns[$d] ;
- }
- foreach my $c (@$cols) {
- foreach $d (0..$#$columns) {
- $idx = $d,
- last if ($c eq $$columns[$d]);
- }
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Column ' . $c . ' not found in datasource.',
- return undef
- unless ($c eq $$columns[$idx]);
- push @$colidxs, $idx;
- }
- }
- }
- #
- # now we can safely process and render
- #
- my $img;
- my $xdomain;
- my $ydomain;
- my $zdomain;
- my @legends = ();
- #
- # need to determine domain type prior to adding points
- my $is_symbolic = undef;
- for ($i = 0; $i < scalar(@$dtypes); $i++) {
- $is_symbolic = 1, last
- if (($$dtypes[$i] eq 'BARCHART') ||
- ($$dtypes[$i] eq 'HISTOGRAM') ||
- ($$dtypes[$i] eq 'CANDLESTICK'));
- }
-
- for ($i = 0; $i < scalar(@$dtypes); $i++) {
-
- if ($$dtypes[$i] ne 'IMAGE') {
- if ($$dcharts[$i]=~/^\?(\d+)$/) {
- #
- # synthesize a chart structure from the stmt handle
- # NOTE: we should eventually support array binding here!!!
- #
- my $srcsth = $$parms[$1];
- $columns = get_ext_type_info($sth, $srcsth, 'NAME', ($i ? $i-1 : 0) );
- $types = get_ext_type_info($sth, $srcsth, 'TYPE', ($i ? $i-1 : 0));
- # $precs = get_ext_type_info($sth, $srcsth, 'PRECISION', $i-1);
- # $scales = get_ext_type_info($sth, $srcsth, 'SCALE', $i-1);
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'Datasource does not provide one of NAME or TYPE information.',
- $srcsth->finish,
- return undef
- unless ($types || $columns);
-
- $data = [];
- my $rowcnt = 0;
- my $row;
- my $colidxs = $dcolidxs[$i];
-
- push(@$data, [ ])
- foreach (@$colidxs);
-
- $row = $srcsth->fetchall_arrayref(undef, 10000);
- foreach my $r (@$row) {
- push(@{$$data[$_]}, $$r[$$colidxs[$_]])
- foreach (0..$#$colidxs);
- }
- }
- else {
- $chart = $DBD::Chart::charts{$$dcharts[$i]};
- #
- # get the record description
- #
- $columns = $$chart{columns};
- $types = $$chart{types};
- $precs = $$chart{precisions};
- $scales = $$chart{scales};
- $data = $$chart{data};
- }
- }
-
- $props = $$dprops[$i];
- #
- # validate and copy in any placeholder values
- #
- return undef unless validate_properties($props, $parms);
-
- if ($i == 0) {
- #
- # create plot object
- #
- $img = DBD::Chart::Plot->new($$props{WIDTH}, $$props{HEIGHT},
- get_colormap());
- return undef unless $img;
- #
- # set global properties
- #
- $img->setOptions( bgColor => $$props{BACKGROUND},
- textColor => $$props{TEXTCOLOR},
- gridColor => $$props{GRIDCOLOR},
- threed => $$props{THREE_D});
-
- $img->setOptions( title => $$props{TITLE})
- if $$props{TITLE};
-
- $img->setOptions( signature => $$props{SIGNATURE})
- if $$props{SIGNATURE};
-
- $img->setOptions(
- genMap => ($$props{MAPNAME}) ? $$props{MAPNAME} : 'plot',
- mapType => $sth->{chart_imagemap},
- mapURL => $$props{MAPURL},
- mapScript => $$props{MAPSCRIPT},
- mapType => ($$props{MAPTYPE}) ? $$props{MAPTYPE} : 'HTML',
- mapModifier => $sth->{chart_map_modifier},
- border => $$props{BORDER}
- )
- if $sth->{chart_imagemap};
-
- $img->setOptions( logo => $$props{LOGO}) if $$props{LOGO};
-
- $img->setOptions( 'xAxisLabel' => $$props{X_AXIS})
- if $$props{X_AXIS};
- $img->setOptions( 'yAxisLabel' => $$props{Y_AXIS})
- if $$props{Y_AXIS};
- $img->setOptions( 'zAxisLabel' => $$props{Z_AXIS})
- if $$props{Z_AXIS};
-
- $img->setOptions( 'xAxisVert' => ($$props{X_ORIENT} eq 'VERTICAL'))
- if $$props{X_ORIENT};
-
- $img->setOptions( 'horizGrid' => 1,
- 'vertGrid' => ($$dtypes[$i] ne 'BARCHART'))
- if $$props{SHOWGRID};
-
- $img->setOptions( 'xLog' => 1)
- if $$props{X_LOG};
-
- $img->setOptions( 'yLog' => 1)
- if $$props{Y_LOG};
-
- $img->setOptions( 'keepOrigin' => 1)
- if $$props{KEEPORIGIN};
-
- $img->setOptions( 'font' => $$props{FONT})
- if $$props{FONT};
- }
-
- next if ($$dtypes[$i] eq 'IMAGE'); # specific chart processing from here on
- #
- # establish color list
- #
- my @colors = ();
- my $clist = ($$props{COLOR}) ? $$props{COLOR} : \@dfltcolors;
- if ($$dtypes[$i] eq 'QUADTREE') {
- @colors = $$props{COLOR} ? @{$$props{COLOR}} : @quadcolors;
- }
- else {
- $t = ($$dtypes[$i] eq 'PIECHART') ? scalar @{$$data[0]} : scalar @$data;
- $t-- unless (($$dtypes[$i] eq 'BOXCHART') || # ($$dtypes[$i] eq 'HISTOGRAM') ||
- ($$dtypes[$i] eq 'PIECHART'));
- $t /= 2 if ($$dtypes[$i] eq 'CANDLESTICK');
- $t = 1 if ($$props{Z_AXIS});
- $t = scalar @{$$data[0]}
- if ((($$dtypes[$i] eq 'BARCHART') || ($$dtypes[$i] eq 'HISTOGRAM')) &&
- (scalar @$clist > 1) && (scalar @$data == 2));
- for ($k = 0, $j = 0; $k < $t; $k++) {
- push(@colors, $$clist[$j++]);
- $j = 0 if ($j >= scalar(@$clist));
- }
- }
-
- my $propstr = '';
- #
- # select domain type: numeric, symbolic, or temporal
- # and make sure every chart adheres to compatible types
- #
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Incompatible domain types for composite image.',
- return undef
- unless ((! $xdomain) ||
- ($numtype{$xdomain} && $numtype{$$types[0]}) ||
- ($timetype{$xdomain} && $timetype{$$types[0]} &&
- ($timetype{$xdomain} eq $timetype{$$types[0]})) ||
- ($symboltype{$xdomain} && $symboltype{$$types[0]}));
- $xdomain = $$types[0] unless $xdomain;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Incompatible range types for composite image.',
- return undef
- unless ((! $ydomain) || ($$dtypes[$i] eq 'BOXCHART') ||
- ($numtype{$ydomain} && $numtype{$$types[1]}) ||
- ($timetype{$ydomain} && $timetype{$$types[1]} &&
- ($timetype{$ydomain} eq $timetype{$$types[1]})));
- $ydomain = $$types[1]
- unless ($ydomain || ($$dtypes[$i] eq 'BOXCHART'));
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Incompatible Z axis types for composite image.',
- return undef
- unless ((! $zdomain) ||
- ($numtype{$zdomain} && $numtype{$$types[2]}) ||
- ($timetype{$zdomain} && $timetype{$$types[2]} &&
- ($timetype{$zdomain} eq $timetype{$$types[2]})) ||
- ($symboltype{$zdomain} && $symboltype{$$types[2]}));
-
- $zdomain = $$types[2] if ((! $zdomain) && $$props{Z_AXIS});
- $img->setOptions( 'symDomain' => 1)
- if (($$dtypes[$i] ne 'GANTT') && ($$dtypes[$i] ne 'QUADTREE') &&
- ($is_symbolic || $symboltype{$xdomain}));
- $img->setOptions( 'timeDomain' => $timetype{$xdomain})
- if (defined($xdomain) && $timetype{$xdomain});
- $img->setOptions( 'timeRange' => $timetype{$ydomain})
- if (defined($ydomain) && $timetype{$ydomain});
- #
- # we need to support temporal Z-axis!!!
- #
- # Piechart:
- # first data array is domain names, the 2nd is the
- # datasets. If more than 1 dataset is supplied, the
- # rest are ignored
- #
- if ($$dtypes[$i] eq 'PIECHART') {
- $propstr = 'pie ' . join(' ', @colors);
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints($$data[0], $$data[1], $propstr);
- next;
- }
- #
- # Quadtree:
- # 1st N-2 data arrays are categories, in a category hierarchy,
- # data array N-1 is the values assigned to the individual items,
- # data array N is the intensity values of individual items
- #
- if ($$dtypes[$i] eq 'QUADTREE') {
- $propstr = 'quadtree ' . join(' ', @colors);
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints(@$data, $propstr);
- next;
- }
- #
- # Gantt chart:
- # first data array is task names, 2nd is the start date,
- # 3rd is end date. Add'l optionals are assignee, pct. complete,
- # and any number of dependent tasks
- #
- if ($$dtypes[$i] eq 'GANTT') {
- $propstr = "gantt $colors[0]";
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints(@$data, $propstr);
- next;
- }
- #
- # need column names in defined order
- #
- my @colnames = ();
- if (! $srcsth) {
- $colnames[$$columns{$_}] = $_
- foreach (keys(%$columns));
- }
- else {
- @colnames = @$columns;
- }
- shift @colnames unless ($$dtypes[$i] eq 'BOXCHART');
-
- $propstr .= ' showvalues:' . (($$props{SHOWVALUES} == 1) ? 5 : $$props{SHOWVALUES}) . ' '
- if ($$props{SHOWVALUES});
- $propstr .= ' stack '
- if ($$props{STACK});
- #
- # default x-axis label orientation is vertical for candlesticks
- # and symbollic domains
- #
- $img->setOptions( 'xAxisVert' => ($$props{X_ORIENT} ne 'HORIZONTAL'))
- if ((! $numtype{$$types[0]}) || ($$dtypes[$i] eq 'CANDLESTICK'));
- #
- # force a legend if more than 1 range or plot
- # complicated algorithm here;
- # if multirange or composite {
- # if multirange {
- # push each column name onto legends array, prepended with
- # current query name if available
- # }
- # } else { must be a composite
- # push query name (default PLOTn) onto legends array
- # }
- #
- if (! $$props{Z_AXIS}) {
- if ((($$dtypes[$i] ne 'CANDLESTICK') && (scalar(@$data) > 2)) ||
- (($$dtypes[$i] eq 'BOXCHART') && (scalar(@$data) > 1)) ||
- (scalar(@$data) > 3)) {
- # its multirange
- my $incr = ($$dtypes[$i] ne 'CANDLESTICK') ? 1 : 2;
- # if stacked, we need an arrayref of legends
- my $legary = ($$props{STACK}) ? [ ] : \@legends;
- push(@legends, $legary) if ($$props{STACK});
- for (my $c = 0; $c <= $#colnames; $c += $incr) {
- #
- # if floating bar/histo, ignore last column name
- last if ((! $$props{ANCHORED}) && ($c == $#colnames) &&
- (($$dtypes[$i] eq 'BARCHART') ||
- ($$dtypes[$i] eq 'HISTOGRAM')));
- #
- # prepend query names if provided for composites
- push(@$legary, ($$dnames[$i] . '.' . $colnames[$c])),
- next
- if ($$dnames[$i]);
- push(@$legary, $colnames[$c]);
- }
- }
- elsif ($#$dtypes > 1) {
- #
- # single range, composite
- push(@legends, ($$dnames[$i] ? $$dnames[$i] : "PLOT$i"));
- }
- }
- #
- # establish icon list if any
- #
- my @icons = ();
- my $iconlist = $$props{ICON};
- if ($$props{ICON}) {
- for ($k = 1, $j = 0; $k <= $#$data; $k++) {
- push(@icons, $$iconlist[$j++]);
- $j = 0 if ($j > $#$iconlist);
- }
- $img->setOptions( icons => \@icons );
- }
-
- if (($$dtypes[$i] eq 'BARCHART') ||
- ($$dtypes[$i] eq 'HISTOGRAM')) {
- #
- # first data array is domain names, the rest are
- # datasets. If more than 1 dataset is supplied, then
- # bars are grouped
- #
- $propstr .= ($$dtypes[$i] eq 'HISTOGRAM') ? 'histo ' : 'bar ';
- if ($$props{Z_AXIS}) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints($$data[0], $$data[1], $$data[2],
- $propstr . $colors[0]),
- next;
- }
- #
- # if single domain and multiple colors, then push all colors into
- # the property string
- $propstr.= ' float' unless $$props{ANCHORED};
- if (($#$data == 1) && (! $$props{ICON})) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints($$data[0], $$data[1],
- $propstr . ' ' . join(' ', @colors)),
- next;
- }
- #
- # if stacked, send all the data at the same time
- #
- if ($$props{STACK}) {
- $propstr .= ' ' . ($$props{ICON} ? 'icon:' . join(' icon:', @icons) : join(' ', @colors));
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints(@$data, $propstr);
- next;
- }
-
- for ($i=1; $i <= $#$data; $i++) {
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints($$data[0], $$data[$i],
- $propstr . ($$props{ICON} ? 'icon:' . $icons[$i-1] : $colors[$i-1]));
- }
- next;
- }
- #
- # establish shape list, and merge with icon list if needed
- #
- my @shapes = ();
- my $shapelist = ($$props{SHAPE}) ? $$props{SHAPE} :
- [ 'fillcircle' ];
- $$props{SHOWPOINTS} = 1 if $$props{SHAPE};
- for ($k = 1, $j = 0, my $n = 0; $k <= $#$data; $k++) {
- push(@shapes, ($$shapelist[$j] eq 'icon') ? 'icon:' . $$iconlist[$n++] : $$shapelist[$j]);
- $n = 0 if ($n > $#$iconlist);
- $j++;
- $j = 0 if ($j > $#$shapelist);
- }
-
- if ($$dtypes[$i] eq 'CANDLESTICK') {
- #
- # first data array is domain symbols, the rest are
- # datasets, consisting of 2-tuples (y-min, y-max).
- # If more than 1 dataset is supplied, then sticks are grouped
- #
- if ($$props{STACK}) {
- $propstr .= ' candle ' . join(' ', @colors);
- $propstr .= ' ' . $shapes[0]
- if ($$props{SHOWPOINTS});
- $propstr .= ' width:' . ($$props{LINEWIDTH} ? $$props{LINEWIDTH} : 2);
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints(@$data, $propstr);
- next;
- }
- for (my $n = 0, $k = 1; $k <= $#$data; $k += 2, $n++) {
- $propstr .= ' candle ' . $colors[$n];
- $propstr .= ' ' . $shapes[$n]
- if ($$props{SHOWPOINTS});
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints($$data[0], $$data[$k], $$data[$k+1], $propstr);
- }
- next;
- }
-
- if ($$dtypes[$i] eq 'BOXCHART') {
- #
- # each data array is a distinct domain to be plotted
- #
- for (my $n = 0, $k = 0; $k <= $#$data; $k++, $n++) {
- $propstr .= ' box ' . $colors[$n];
- $propstr .= ' ' . $shapes[$n]
- if ($$props{SHOWPOINTS});
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints($$data[$k], $propstr);
- }
- next;
- }
- #
- # line, point, or area graph
- #
- $img->setOptions( lineWidth => ($$props{LINEWIDTH} ? $$props{LINEWIDTH} : 1));
- if (($$dtypes[$i] eq 'AREAGRAPH') && ($$props{STACK})) {
- $propstr .= ' fill ' . join(' ', @colors) ;
- $propstr .= ' ' . join(' ', @shapes)
- if ($$props{SHOWPOINTS} || $$props{SHAPE});
- $propstr .= ' float' unless $$props{ANCHORED};
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints(@$data, $propstr);
- next;
- }
- for ($k = 1; $k <= $#$data; $k++) {
- my $tprops = $propstr . ' ';
- $tprops .= ($$dtypes[$i] eq 'POINTGRAPH') ?
- 'noline ' . $colors[$k-1] . ' ' . $shapes[$k-1] :
- ($$dtypes[$i] eq 'LINEGRAPH') ?
- $colors[$k-1] :
- 'fill ' . $colors[$k-1] ;
- $tprops .= ' ' . $shapes[$k-1]
- if ($$props{SHOWPOINTS} || $$props{SHAPE});
- $tprops .= ' width:' . ($$props{LINEWIDTH} ? $$props{LINEWIDTH} : 1);
- $tprops .= ' float' unless $$props{ANCHORED};
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $img->setPoints($$data[0], $$data[$k], $tprops);
- }
- }
- #
- # if we have a legend, add it before plotting
- $img->setOptions( legend => \@legends)
- if ($#legends >= 0);
- #
- # all the image data loaded, now plot it
- #
- $sth->{chart_image} = $img->plot($dprops->[0]->{FORMAT});
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $sth->{chart_image};
-
- $sth->{chart_imagemap} =
- ($sth->{chart_imagemap}) ? $img->getMap() : undef;
-
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = $img->{errmsg},
- return undef
- unless $sth->{chart_image};
- #
- # update precision values
- $precs = $sth->{PRECISION};
- $$precs[0] = length($sth->{chart_image});
- $$precs[1] = length($sth->{chart_imagemap}) if $sth->{chart_imagemap};
- return 1;
- }
-
- sub convert_time {
- my ($value, $type) = @_;
- #
- # use Perl funcs to compute seconds from date
- return timegm(0, 0, 0, $3, $2 - 1, $1)
- if (($type == SQL_DATE) &&
- ($value=~/^(\d+)[\-\.\/](\d+)[\-\.\/](\d+)$/));
-
- return timegm(0, 0, 0, $3, $month{uc $2}, $1)
- if (($type == SQL_DATE) &&
- ($value=~/^(\d+)[\-\.\/](\w+)[\-\.\/](\d+)$/));
-
- return timegm($6, $5, $4, $3, $2 - 1, $1) + ($7 ? $7 : 0)
- if (($type == SQL_TIMESTAMP) &&
- ($value=~/^(\d+)[\-\.\/](\d+)[\-\.\/](\d+)\s+(\d+):(\d+):(\d+)(\.\d+)?$/));
-
- return timegm($6, $5, $4, $3, $month{uc $2}, $1) + ($7 ? $7 : 0)
- if (($type == SQL_TIMESTAMP) &&
- ($value=~/^(\d+)[\-\.\/](\w+)[\-\.\/](\d+)\s+(\d+):(\d+):(\d+)(\.\d+)?$/));
-
- return (($1 ? (($1 eq '-') ? -1 : 1) : 1) *
- (($3 ? ($3 * 3600) : 0) + ($5 ? ($5 * 60) : 0) + $6 + ($7 ? $7 : 0)))
- if ((($type == SQL_INTERVAL_HR2SEC) || ($type == SQL_TIME)) &&
- ($value=~/^([\-\+])?((\d+):)?((\d+):)?(\d+)(\.\d+)?$/));
-
- return undef; # for completeness, shouldn't get here
- }
-
- sub test_predicate {
- my ($rowmap, $pctype, $pc, $predop, $predval, $rownum) = @_;
- for (my $i = 0; $i <= $#$pc; $i++) {
- $$rowmap{$i} = -1, next
- if ((($pctype == SQL_CHAR) || ($pctype == SQL_VARCHAR)) &&
- (eval "\'$$pc[$i]\' $strpredops{$predop} \'$predval\'"));
-
- $$rowmap{$i} = -1, next
- if (($numtype{$pctype}) &&
- (eval "$$pc[$i] $numpredops{$predop} $predval"));
-
- if ($timetype{$pctype}) {
- my ($col, $operand) = (convert_time($$pc[$i], $pctype), convert_time($predval, $pctype));
- $$rowmap{$i} = -1
- if (eval "$col $numpredops{$predop} $operand");
- }
- }
- return 1;
- }
-
- sub eval_predicate {
- my ($predcol, $predop, $predval, $types, $data, $parms, $is_ary,
- $is_ref, $maxary) = @_;
- my %rowmap = ();
- my $pc = $$data[$predcol];
- my $pctype = $$types[$predcol];
- my ($k, $p);
-
- $predval=~s/^'(.+)'$/$1/, # trim any quotes
- test_predicate(\%rowmap, $pctype, $pc, $predop, $predval, -1),
- return %rowmap
- if ($predval ne '?');
- #
- # must be parameterized predicate
- #
- my $parmnum = $#$parms;
- for ($k = 0; $k < $maxary; $k++) {
- $p = $$parms[$parmnum];
- $p = (($is_ary) ? $$p[$k] : $$p) if ($is_ref);
- test_predicate(\%rowmap, $pctype, $pc, $predop, $p, $k);
- }
- return %rowmap;
- }
-
-
- sub fetch {
- my($sth) = @_;
-
- if ($sth->{chart_colormap}) {
- my $i = uc $sth->{chart_colormap};
- my $table = $DBD::Chart::charts{COLORMAP};
- my $ary = $table->{data};
- my ($col1, $col2, $col3, $col4) = ($$ary[0], $$ary[1], $$ary[2], $$ary[3]) ;
- if ($sth->{chart_1_color}) {
- my $color;
- foreach $color (@$col1) {
- last if ($i eq uc $color);
- }
- return '0E0' if ($i ne uc $color);
- $sth->{chart_colormap} = undef;
- }
-
- my @row = ($$col1[$i], $$col2[$i], $$col3[$i], $$col4[$i]);
- $sth->{chart_colormap}++;
- return $sth->_set_fbav(\@row);
- }
- my $buf = $sth->{chart_image};
- return 0 if (! $buf);
- my @row = ($buf);
- push(@row, $sth->{chart_imagemap})
- if ($sth->{NUM_OF_FIELDS} > 1);
- return $sth->_set_fbav(\@row);
- }
-
- sub finish {
- my($sth) = @_;
- }
-
- sub bind_param {
- my ($sth, $pNum, $val, $attr) = @_;
- #
- # data type for placeholders is taken from field definitions
- #
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr = 'Statement does not contain placeholders.',
- return undef
- unless $sth->{NUM_OF_PARAMS};
-
- my $params = $sth->{chart_params};
- $params = [ ],
- $sth->{chart_params} = $params
- unless defined($params);
-
- $$params[$pNum-1] = $val;
- 1;
- }
- *chart_bind_param_array = \&bind_param;
- *bind_param_array = \&bind_param;
-
- sub chart_bind_param_status {
- my ($sth, $stsary) = @_;
- $DBD::Chart::err = -1,
- $DBD::Chart::errstr =
- 'bind_param_status () requires arrayref or hashref parameter.',
- return undef
- if ((ref $stsary ne 'ARRAY') && (ref $stsary ne 'HASH'));
-
- $sth->{chart_paramsts} = $stsary;
- return 1;
- }
- *bind_param_status = \&chart_bind_param_status;
-
- sub bind_param_inout {
- my ($sth, $pNum, $val, $maxlen, $attr) = @_;
- #
- # what do I need maxlen for ???
- #
- return bind_param($sth, $pNum, $val, $attr);
- }
- #
- # get externally provided name/type/prec/scale info
- #
- sub get_ext_type_info {
- my ($sth, $srcsth, $item, $entry) = @_;
-
- my $t;
- #
- # if srcsth provides it, use it
- #
- return $srcsth->{$item}
- if eval { $t = $srcsth->{$item}; };
- #
- # if chart type map, and requested item
- # exists in the type map, return it
- #
- return undef
- unless (ref $sth->{chart_type_map} &&
- (ref $sth->{chart_type_map} eq 'ARRAY') &&
- $sth->{chart_type_map}->[$entry] &&
- ref $sth->{chart_type_map}->[$entry] &&
- (($entry == 0) &&
- ((ref $sth->{chart_type_map}->[$entry] eq 'HASH') &&
- $sth->{chart_type_map}->[$entry]->{$item}) ||
- ((ref $sth->{chart_type_map}->[$entry] eq 'ARRAY') &&
- $sth->{chart_type_map}->[$entry]->[0]->{$item})));
- #
- # if its single src form, collect the items into an arrayref
- #
- my $srcary = (($entry == 0) && (ref $sth->{chart_type_map}->[$entry] eq 'HASH')) ?
- $sth->{chart_type_map} : $sth->{chart_type_map}->[$entry];
- my @outary = ();
- push @outary, $_->{$item}
- foreach (@$srcary);
- return \@outary;
- }
-
- sub STORE {
- my ($sth, $attr, $val) = @_;
- return $sth->SUPER::STORE($attr, $val) unless ($attr=~/^chart_/) ;
- $sth->{$attr} = $val;
- return 1;
- }
-
- sub FETCH {
- my($sth, $attr) = @_;
- return $sth->{$attr} if ($attr =~ /^chart_/);
- return $sth->SUPER::FETCH($attr);
- }
-
- sub DESTROY { undef }
-
- 1;
- }
- __END__
-
- =head1 NAME
-
- DBD::Chart - DBI driver abstraction for Rendering Charts and Graphs
-
- =head1 SYNOPSIS
-
- $dbh = DBI->connect('dbi:Chart:')
- or die "Cannot connect: " . $DBI::errstr;
- #
- # create file if it deosn't exist, otherwise, just open
- #
- $dbh->do('CREATE TABLE mychart (name CHAR(10), ID INTEGER, value FLOAT)')
- or die $dbh->errstr;
-
- # add data to be plotted
- $sth = $dbh->prepare('INSERT INTO mychart VALUES (?, ?, ?)');
- $sth->bind_param(1, 'Values');
- $sth->bind_param(2, 45);
- $sth->bind_param(3, 12345.23);
- $sth->execute or die 'Cannot execute: ' . $sth->errstr;
-
- # and render it
- $sth = $dbh->prepare('SELECT BARCHART FROM mychart');
- $sth->execute or die 'Cannot execute: ' . $sth->errstr;
- @row = $sth->fetchrow_array;
- print $row[0];
-
- # delete the chart
- $sth = $dbh->prepare('DROP TABLE mychart')
- or die "Cannot prepare: " . $dbh->errstr;
- $sth->execute or die 'Cannot execute: ' . $sth->errstr;
-
- $dbh->disconnect;
-
- =head1 WARNING
-
- THIS IS BETA SOFTWARE.
-
- =head1 DESCRIPTION
-
- The DBD::Chart provides a DBI abstraction for rendering pie charts,
- bar charts, box&whisker charts (aka boxcharts), histograms,
- Gantt charts, and line, point, and area graphs.
-
- For detailed usage information, see the included L<dbdchart.html>
- webpage.
- See L<DBI(3)> for details on DBI.
-
- =head2 Prerequisites
-
- =over 4
-
- =item Perl 5.6.0 minimum
-
- =item DBI 1.14 minimum
-
- =item DBD::Chart::Plot 0.80 (included with this package)
-
- =item GD X.XX minimum
-
- =item GD::Text X.XX minimum
-
- =item Time::HiRes
-
- =item libpng
-
- =item zlib
-
- =item libgd
-
- =item jpeg-6b
-
- =back
-
-
- =head2 Installation
-
- For Windows users, use WinZip or similar to unpack the file, then copy
- Chart.pm to wherever your site-specific modules are kept (usually
- \Perl\site\lib\DBD for ActiveState Perl installations). Also create a
- 'Chart' directory in the DBD directory, and copy the Plot.pm module
- to the new directory.
- Note that you won't be able to execute the install test with this, but you need
- a copy of 'nmake' and all its libraries to run that anyway. I may
- whip up a PPM in the future.
-
- For Unix, extract it with
-
- gzip -cd DBD-Chart-0.80.tar.gz | tar xf -
-
- and then enter the following:
-
- cd DBD-Chart-0.80
- perl Makefile.PL
- make
-
- You can test the installation by running
-
- make test
-
- this will render a bunch of charts and an HTML page to view
- them with. Assuming the test completes successfully, you should
- use a web browser to view the file t/plottest.html and verify
- the images look reasonable.
-
- If tests succeed, proceed with installation via
-
- make install
-
- Note that you probably need root or administrator permissions.
- If you don't have them, read the ExtUtils::MakeMaker man page for details
- on installing in your own directories. L<ExtUtils::MakeMaker>.
-
- =head1 FOR MORE INFO
-
- Check out http://www.presicient.com/dbdchart with your
- favorite browser. It includes all the usage information.
-
- =head1 AUTHOR AND COPYRIGHT
-
- This module is Copyright (C) 2001, 2002 by Presicient Corporation
-
- Email: darnold@presicient.com
-
- You may distribute this module under the terms of the Artistic
- License, as specified in the Perl README file.
-
- =head1 SEE ALSO
-
- L<DBI(3)>
-
- For help on the use of DBD::Chart, see the DBI users mailing list:
-
- dbi-users-subscribe@perl.org
-
- For general information on DBI see
-
- http://dbi.perl.org
-
- =cut
-