File Coverage

blib/lib/JSprite.pm
Criterion Covered Total %
statement 19 1879 1.0
branch 0 790 0.0
condition 0 320 0.0
subroutine 7 54 12.9
pod 14 45 31.1
total 40 3088 1.3


line stmt bran cond sub pod time code
1             ##++
2             ## JSprite
3             ## Sprite v.3.2
4             ## Last modified: August 22, 1998
5             ##
6             ## Copyright (c) 1998, Jim Turner, from
7             ## Sprite.pm (c) 1995-1998, Shishir Gundavaram
8             ## All Rights Reserved
9             ##
10             ## E-Mail: shishir@ora.com
11             ## E-Mail: jim.turner@lmco.com
12             ##
13             ## Permission to use, copy, and distribute is hereby granted,
14             ## providing that the above copyright notice and this permission
15             ## appear in all copies and in supporting documentation.
16             ##
17             ## If you use Sprite for any cool (Web) applications, I would be
18             ## interested in hearing about them. So, drop me a line. Thanks!
19             ##--
20              
21             #############################################################################
22              
23             =head1 NAME
24              
25             JSprite - Modified version of Sprite to manipulate text delimited flat-files
26             as databases using SQL emulating Oracle. The remaining documentation
27             is based on Sprite.
28              
29             =head1 SYNOPSIS
30              
31             use JSprite;
32              
33             $rdb = new JSprite;
34              
35             $rdb->set_delimiter (-read => '::') ## OR: ('read', '::');
36             $rdb->set_delimiter (-write => '::') ## OR: ('write', '::');
37             $rdb->set_delimiter (-record => '\n') ## OR: ('record', '::');
38              
39             $rdb->set_os ('Win95');
40              
41             ## Valid arguments (case insensitive) include:
42             ##
43             ## Unix, Win95, Windows95, MSDOS, NT, WinNT, OS2, VMS,
44             ## MacOS or Macintosh. Default determined by $^O.
45              
46             #$rdb->set_lock_file ('c:\win95\tmp\Sprite.lck', 10);
47             $rdb->set_lock_file ('Sprite.lck', 10);
48              
49             $rdb->set_db_dir ('Mac OS:Perl 5:Data') || die "Can't access dir!\n";
50              
51             $data = $rdb->sql (<sql (<
52             .
53             . (SQL)
54             .
55             Query
56              
57             foreach $row (@$data) { ## OR: foreach $row (@data) {
58             @columns = @$row; ## NO null delimited string -- v3.2
59             }
60              
61             $rdb->xclose;
62             $rdb->close ($database); ## To save updated database
63              
64             =head1 DESCRIPTION
65              
66             Here is a simple database where the fields are delimited by double-colons:
67              
68             PLAYER=VARCHAR2(16)::YEARS=NUMBER::POINTS=NUMBER::REBOUNDS=NUMBER::ASSISTS=NUMBER::Championships=NUMBER
69             ...
70             Larry Bird::13::25::11::7::3
71             Michael Jordan::14::29::6::5::5
72             Magic Johnson::13::22::7::11::5
73             ...
74              
75             I The first line must contain the field names (case insensitive),
76             and the Oracle datatype and length. Currently, the only meaningful
77             datatypes are NUMBER and VARCHAR. All other types are treated
78             the same as VARCHAR (Perl Strings, for comparisens).
79              
80             =head1 Supported SQL Commands
81              
82             Here are a list of the SQL commands that are supported by JSprite:
83              
84             =over 5
85              
86             =item I
87              
88             select col1 [,col2] from table_name
89             where (cond1 OPERATOR value1)
90             [and|or (cond2 OPERATOR value2) ...]
91             order by col1 [,col2]
92              
93             The '*' operator can be used to select all columns.
94              
95             The I is simply the file that contains the data. If the file
96             is not in the current directory, the path must be specified. By
97             default, the actual file-name will end with the extension ".sdb".
98              
99             Valid column names can be used where [cond1..n] and [value1..n] are expected,
100             such as:
101              
102             I:
103              
104             select Player, Points from my_db
105             where (Rebounds > Assists)
106              
107             I Column names must not be Perl string or boolean operators, ie. (lt,
108             gt, eq, and, or, etc. and are case-insensitive.
109            
110             The following SQL operators can be used: =, <, >, <=, >=, <>,
111             is, as well as Perl's special operators: =~ and !~.
112             The =~ and !~ operators are used to
113             specify regular expressions, such as:
114              
115             I:
116              
117             select * from my_db
118             where (Name =~ /Bird$/i)
119              
120             Selects records where the Name column ends with "Bird" (case insensitive).
121             For more information, look at a manual on regexps.
122              
123             I A path to a database can contain only the following characters:
124              
125             \w, \x80-\xFF, -, /, \, ., :
126              
127             If you have directories with spaces or other 'invalid' characters, you
128             need to use the I method.
129              
130             =item I - updates records that match specified criteria.
131              
132             update table_name
133             set cond1 = (value1)[,cond2 = (value2) ...]
134             where (cond1 OPERATOR value1)
135             [and|or (cond2 OPERATOR value2) ...]
136              
137             I:
138              
139             update my_db
140             set Championships = (Championships + 1)
141             where (Player = 'Larry Bird')
142              
143             update my_db
144             set Championships = (Championships + 1),
145             Years = (12)
146             where (Player = 'Larry Bird')
147              
148             =item I - removes records that match specified criteria:
149              
150             delete from table_name
151             where (cond1 OPERATOR value1)
152             [and|or (cond2 OPERATOR value2) ...]
153              
154             I:
155              
156             delete from my_db
157             where (Player =~ /Johnson$/i) or
158             (Years > 12)
159              
160             =item I - simplified version of SQL-92 counterpart
161              
162             Creates a new table or sequence.
163              
164             create table table_name (
165             column-name datatype [, column-name2 datatype...]
166             [, primary key (column-name [, column-name..])
167              
168             create sequence sequence_name [increment by #] start with #
169              
170             A sequence is an Oracle-ish way of doing autonumbering. The sequence is stored
171             in a tiny ascii file (sequence-name.seq). You can also do autonumbering the
172             MySQL way with a field using the "AUTONUMBER" datatype and giving it a default
173             value of the starting sequence number.
174              
175             =item I - simplified version of SQL-92 counterpart
176              
177             Removes the specified column from the database. The other standard SQL
178             functions for alter table are also supported:
179              
180             alter table table_name drop (column-name [, column-name2...])
181              
182             alter table table_name add ([position] column-name datatype
183             [, [position2] column-name2 datatype2...]
184             [primary key (column-name [, column-name2...]) ])
185              
186             I:
187              
188             alter table my_db drop (Years)
189              
190             alter table my_db add (Legend VARCHAR(40) default "value", Mapname CHAR(5))
191              
192             alter table my_db add (1 Maptype VARCHAR(40))
193              
194             This example adds a new column as the 2nd column (0 for 1st column) of the
195             table. By default, new fields are added as the right-most (last) column of
196             the table. This is a JSprite Extension and is not supported by standard SQL.
197              
198             alter table my_db modify (Legend VARCHAR(40))
199              
200             alter table my_db modify (0 Legend default 1)
201              
202             The last example moves the "Legend" column to the 1st column in the table and
203             shifts the others over, and causes all subsequent records added to use a
204             default value of "1" for the "Legend" field, if no value is inserted for it.
205             This "Position" field (zero in the example) is a JSprite extension and is not
206             part of standard SQL.
207              
208             =item I - inserts a record into the database:
209              
210             insert into table_name
211             [(col1, col2, ... coln) ]
212             values
213             (val1, val2, ... valn)
214              
215             I:
216              
217             insert into my_db
218             (Player, Years, Points, Championships)
219             values
220             ('Kareem Abdul-Jabbar', 21, 26, 6)
221              
222             You don't have to specify all of the fields in the database! Sprite also
223             does not require you to specify the fields in the same order as that of
224             the database.
225              
226             I You should make it a habit to quote strings.
227              
228             =back
229              
230             =head1 METHODS
231              
232             Here are the available methods:
233              
234             =over 5
235              
236             =item I
237              
238             The set_delimiter function sets the read and write delimiter for the
239             database. The delimiter is not limited to one character; you can have
240             a string, and even a regexp (for reading only). In JSprite,
241             you can also set the record seperator (default is newline).
242              
243             I
244              
245             None
246              
247             =item I
248              
249             The set_os function can be used to notify Sprite as to the operating
250             system that you're using. Default is determined by $^O.
251              
252             I If you're using Sprite on Windows 95/NT or on OS2, make sure
253             to use backslashes -- and NOT forward slashes -- when specifying a path
254             for a database or to the I or I methods!
255              
256             I
257              
258             None
259              
260             =item I
261              
262             For any O/S that doesn't support flock (i.e Mac, Windows 95 and VMS), this
263             method allows you to set a lock file to use and the number of tries that
264             Sprite should try to obtain a 'fake' lock. However, this method is NOT
265             fully reliable, but is better than no lock at all.
266              
267             'Sprite.lck' (either in the directory specified by I or in
268             the current directory) is used as the default lock file if one
269             is not specified.
270              
271             I
272              
273             None
274              
275             =item I
276              
277             A path to a database can contain only the following characters:
278              
279             \w, \x80-\xFF, -, /, \, ., :
280              
281             If your path contains other characters besides the ones listed above,
282             you can use this method to set a default directory. Here's an example:
283              
284             $rdb->set_db_dir ("Mac OS:Perl 5:Data");
285              
286             $data = $rdb->sql ("select * from phone.db");
287              
288             Sprite will look for the file "Mac OS:Perl 5:Data:phone.db". Just to
289             note, the database filename cannot have any characters besides the one
290             listed above!
291              
292             I
293              
294             0 - Failure
295             1 - Success
296              
297             =item I
298              
299             JSprite permits the user to specify an extension that is part
300             of the actual file name, but not part of the corresponding
301             table name. The default is '.sdb'.
302              
303             $rdb->set_db_ext ('.sdb');
304              
305              
306             I
307              
308             None
309              
310             =item I
311              
312             The sql function is used to pass a SQL command to this module. All of the
313             SQL commands described above are supported. The I
314             returns an array containing the data, where the first element is the status.
315             All of the other other SQL commands simply return a status.
316              
317             I
318             1 - Success
319             0 - Error
320              
321             =item I
322              
323             The sql function is used to commit changes to the database.
324             Arguments: file-name (usually the table-name) - the file
325             name to write the table to. NOTE: The path and file
326             extension will be appended to it, ie:
327              
328             &rdb->commit('filename');
329              
330             I
331             1 - Success
332             0 - Error
333              
334             =item I
335              
336             The close function closes the file, and destroys the database object. You
337             can pass a filename to the function, in which case Sprite will save the
338             database to that file; the directory set by I is used as
339             the default.
340              
341             I
342              
343             None
344              
345             =back
346              
347             =head1 NOTES
348              
349             Sprite is not the solution to all your data manipulation needs. It's fine
350             for small databases (less than 1000 records), but anything over that, and
351             you'll have to sit there and twiddle your fingers while Sprite goes
352             chugging away ... and returns a few *seconds* or so later.
353              
354             The main advantage of Sprite is the ability to develop and test
355             prototype applications on personal machines (or other machines which do not
356             have an Oracle licence or some other "mainstream" database) before releasing
357             them on "production" machines which do have a "real" database. This can all
358             be done with minimal or no changes to your Perl code.
359              
360             Another advantage of Sprite is that you can use Perl's regular expressions
361             to search through your data. Yippee!
362              
363             JSprite provides the ability to emulate basic database tables
364             and SQL calls via flat-files. The primary use envisioned
365             for this is to permit website developers who can not afford
366             to purchase an Oracle licence to prototype and develop Perl
367             applications on their own equipment for later hosting at
368             larger customer sites where Oracle is used. :-)
369              
370             JSprite attempts to do things in as database-independent manner as possible,
371             but where differences occurr, JSprite most closely emmulates Oracle, for
372             example "sequences/autonumbering". JSprite uses tiny one-line text files
373             called "sequence files" (.seq). and Oracle's "seq_file_name.NEXTVAL"
374             function to insert into autonumbered fields.
375              
376             =head1 ADDITIONAL JSPRITE-SPECIFIC FEATURES
377              
378             JSprite supports Oracle sequences and functions. The
379             currently-supported Oracle functions are "SYSTIME", NEXTVAL, and "NULL".
380             Users can also "register" their own functions via the
381             "fn_register" method.
382              
383             =item I
384              
385             Method takes 2 arguments: Function name and optionally, a
386             package name (default is "main").
387              
388             $rdb->fn_register ('myfn','mypackage');
389            
390             -or-
391              
392             JSprite::fn_register ('myfn',__PACKAGE__);
393              
394             Then, you could say:
395              
396             insert into mytable values (myfn(?))
397            
398             and bind some value to "?", which is passed to "myfn", and the return-value
399             is inserted into the database. You could also say (without binding):
400              
401             insert into mytable values (myfn('mystring'))
402            
403             -or (if the function takes a number)-
404              
405             select field1, field2 from mytable where field3 = myfn(123)
406            
407             I
408              
409             None
410              
411             JSprite has added the SQL "create" function to
412             create new tables and sequences.
413              
414             I
415              
416             create table table1 (
417             field1 number,
418             field2 varchar(20),
419             field3 number(5,3) default 3.143)
420              
421             create sequence sequence-name [increment by 1] start with 0
422              
423             =head1 SEE ALSO
424              
425             DBD::Sprite, Sprite, Text::CSV, RDB
426              
427             =head1 ACKNOWLEDGEMENTS
428              
429             I would like to thank the following, especially Rod Whitby and Jim Esten,
430             for finding bugs and offering suggestions:
431              
432             Shishir Gundavaram (shishir@ora.com) (Original Sprite Author)
433             Rod Whitby (rwhitby@geocities.com)
434             Jim Esten (jesten@wdynamic.com)
435             Dave Moore (dmoore@videoactv.com)
436             Shane Hutchins (hutchins@ctron.com)
437             Josh Hochman (josh@bcdinc.com)
438             Barry Harrison (barryh@topnet.net)
439             Lisa Farley (lfarley@segue.com)
440             Loyd Gore (lgore@ascd.org)
441             Tanju Cataltepe (tanju@netlabs.net)
442             Haakon Norheim (hanorhei@online.no)
443              
444             =head1 COPYRIGHT INFORMATION
445            
446             JSprite Copyright (c) 1998-2001, Jim Turner
447             Sprite Copyright (c) 1995-1998, Shishir Gundavaram
448             All Rights Reserved
449              
450             Permission to use, copy, and distribute is hereby granted,
451             providing that the above copyright notice and this permission
452             appear in all copies and in supporting documentation.
453              
454             =cut
455              
456             ###############################################################################
457              
458             package JSprite;
459              
460 1     1   5 no warnings 'uninitialized';
  1         1  
  1         43  
461              
462             require 5.002;
463              
464 1     1   5 use vars qw($VERSION);
  1         1  
  1         34  
465              
466 1     1   5 use Cwd;
  1         1  
  1         61  
467 1     1   5 use Fcntl;
  1         1  
  1         275  
468 1     1   2347 use File::DosGlob 'glob';
  1         4987  
  1         19  
469             our ($XMLavailable, $results);
470 1     1   528 eval 'use XML::Simple; $XMLavailable = 1; 1';
  0            
  0            
471             eval {require 'OraSpriteFns.pl';};
472              
473             ##++
474             ## Global Variables. Declare lock constants manually, instead of
475             ## importing them from Fcntl.
476             ##
477 1     1   186 use vars qw ($VERSION $LOCK_SH $LOCK_EX);
  1         2  
  1         32623  
478             ##--
479              
480             $JSprite::VERSION = '6.1';
481             $JSprite::LOCK_SH = 1;
482             $JSprite::LOCK_EX = 2;
483              
484             my $NUMERICTYPES = '^(NUMBER|FLOAT|DOUBLE|INT|INTEGER|NUM|AUTONUMBER|AUTO|AUTO_INCREMENT|DECIMAL|TINYINT|BIGINT|DOUBLE)$'; #20000224
485             my $STRINGTYPES = '^(VARCHAR2|CHAR|VARCHAR|DATE|LONG|BLOB|MEMO|RAW|TEXT)$';
486             #my $BLOBTYPES = '^(LONG|BLOB|MEMO)$';
487             my $BLOBTYPES = '^(LONG.*|.*?LOB|MEMO|.FILE)$';
488             my $REFTYPES = '^(LONG.*|.FILE)$'; #SUPPORT FILE-REFERENCING FOR THESE BLOB-TYPES. (OTHERS ARE STORED INLINE). 20010125
489             my @perlconds = ();
490             my @perlmatches = ();
491             my $sprite_user = ''; #ADDED 20011026.
492             our ($errdetails);
493              
494             ##++
495             ## Public Methods and Constructor
496             ##--
497              
498             sub new
499             {
500 0     0 0   my $class = shift;
501 0           my $self;
502              
503 0           $self = {
504             commands => 'select|update|delete|alter|insert|create|drop|truncate|primary_key_info',
505             # column => '[A-Za-z0-9\~\x80-\xFF][\w\x80-\xFF]+', #CHGD. TO NEXT 20020214 TO ALLOW 1-LETTER FIELD NAMES!!!! (HOW DID THIS GO ON FOR SO LONG?)
506             column => '[A-Za-z0-9][\w\x80-\xFF]*',
507             _select => '[\w\x80-\xFF\*,\s\~]+',
508             path => '[\w\x80-\xFF\-\/\.\:\~\\\\]+',
509             table => '',
510             file => '',
511             table => '', #JWT: ADDED 20020515
512             ext => '', #JWT:ADD FILE EXTENSIONS.
513             directory => '',
514             timestamp => 0,
515             _read => ',',
516             _write => ',',
517             _record => "\n", #JWT:SUPPORT ANY RECORD-SEPARATOR!
518             fields => {},
519             fieldregex => '', #ADDED 20001218
520             use_fields => '',
521             key_fields => '',
522             order => [],
523             types => {},
524             lengths => {},
525             scales => {},
526             defaults => {},
527             records => [],
528             platform => 'Unix',
529             fake_lock => 0,
530             default_lock => 'Sprite.lck',
531             sprite_lock_file => '',
532             lock_handle => '',
533             default_try => 10,
534             sprite_lock_try => '',
535             lock_sleep => 1,
536             errors => {},
537             lasterror => 0, #JWT: ADDED FOR ERROR-CONTROL
538             lastmsg => '',
539             CaseTableNames => 0, #JWT: 19990991 TABLE-NAME CASE-SENSITIVITY?
540             LongTruncOk => 0, #JWT: 19991104: ERROR OR NOT IF TRUNCATION.
541             LongReadLen => 0, #JWT: 19991104: ERROR OR NOT IF TRUNCATION.
542             RaiseError => 0, #JWT: 20000114: ADDED DBI RAISEERROR HANDLING.
543             silent => 0,
544             dirty => 0, #JWT: 20000229: PREVENT NEEDLESS RECOMMITS.
545             StrictCharComp => 0, #JWT: 20010313: FORCES USER TO PAD STRING LITERALS W/SPACES IF COMPARING WITH "CHAR" TYPES.
546             sprite_forcereplace => 0, #JWT: 20010912: FORCE DELETE/REPLACE OF DATAFILE (FOR INTERNAL WEBFARM USE)!
547             sprite_Crypt => 0, #JWT: 20020109: Encrypt Sprite table files! FORMAT: [[encrypt=|decrypt=][Crypt]::CBC;][[IDEA[_PP]|DES]_PP];]keystr
548             sprite_reclimit => 0, #JWT: 20010123: PERMIT LIMITING # OF RECORDS FETCHED.
549             sprite_sizelimit => 0, #JWT: 20010123: SAME AS RECLIMIT, NEEDED BOR BACKWARD COMPAT.
550             sprite_actlimit => 0, #JWT: 20010123: SAME AS RECLIMIT, NEEDED BOR BACKWARD COMPAT.
551             dbuser => '', #JWT: 20011026: SAVE USER'S NAME.
552             dbname => '', #JWT: 20020515: SAVE DATABASE NAME.
553             CBC => 0, #JWT: 20020529: SAVE Crypt::CBC object, if encrypting!
554             sprite_xsl => '', #JWT: 20020611: OPTIONAL XSL TEMPLATE FILE.
555             sprite_CaseFieldNames => 0, #JWT: 20020618: FIELD-NAME CASE-SENSITIVITY?
556             sprite_lastsequence => '', #JWT: ADDED 20020905 TO SUPPORT DBIx::GeneratedKey!
557             sprite_nocase => 0, #JWT: ADDED 20040323 TO SUPPORT CASE-INSENSITIVE WHERE-CLAUSES LIKE LDAP.
558             #NOTE - ONLY CURRENTLY FOR "LIKE/NOT LIKE" (VALUE=1|'L)!
559             #MAY ADD OTHER VALUES LATER!
560             ASNAMES => {}, #ADDED 20040913 TO SUPPORT "AS" IN SELECTS.
561             };
562              
563 0           $self->{separator} = { Unix => '/', Mac => ':', #JWT: BUGFIX.
564             PC => '\\\\', VMS => '/' };
565             $self->{maxsizes} = {
566 0           'LONG RAW' => 2147483647,
567             'RAW' => 255,
568             'LONG' => 2147483647,
569             'CHAR' => 255,
570             'NUMBER' => 38,
571             'AUTONUMBER' => 38,
572             'DOUBLE' => 15,
573             'DATE' => 19,
574             'VARCHAR' => 2000,
575             'VARCHAR2' => 2000,
576             'BOOLEAN' => 1,
577             'BLOB' => 2147483647,
578             'MEMO' => 2147483647,
579             };
580              
581 0           bless $self, $class;
582              
583 0           for (my $i=0;$i
584             {
585 0           $self->{$_[$i]} = $_[$i+1];
586             }
587              
588 0           $self->initialize;
589 0           return $self;
590             }
591              
592             sub initialize
593             {
594 0     0 0   my $self = shift;
595              
596 0           $sprite_user = $self->{'dbuser'}; #ADDED 20011026.
597 0           $self->define_errors;
598 0 0         $self->set_os ($^O) if (defined $^O);
599 0 0         if ($self->{sprite_Crypt}) #ADDED: 20020109
600             {
601 0           my (@cryptinfo) = split(/\;/, $self->{sprite_Crypt});
602 0 0         unshift (@cryptinfo, 'IDEA') if ($#cryptinfo < 1);
603 0 0         unshift (@cryptinfo, 'Crypt::CBC') if ($#cryptinfo < 2);
604 0           $self->{sprite_Crypt} = 1;
605 0 0         $self->{sprite_Crypt} = 2 if ($cryptinfo[0] =~ s/^encrypt\=//i);
606 0 0         $self->{sprite_Crypt} = 3 if ($cryptinfo[0] =~ s/^decrypt\=//i);
607 0 0         $cryptinfo[0] = 'Crypt::' . $cryptinfo[0]
608             unless ($cryptinfo[0] =~ /\:\:/);
609 0           eval "require $cryptinfo[0]";
610 0 0         if ($@)
611             {
612 0           $errdetails = $@;
613 0           $self->display_error (-526);
614             }
615             else
616             {
617 0           eval {$self->{CBC} = Crypt::CBC->new($cryptinfo[2], $cryptinfo[1]); };
  0            
618 0 0         if ($@)
619             {
620 0           $errdetails = "Can't find/use module \"$cryptinfo[1].pm\"? ($@)!";
621 0           $self->display_error (-526);
622             }
623             }
624             }
625 0           return $self;
626             }
627              
628             sub set_delimiter
629             {
630 0     0 1   my ($self, $type, $delimiter) = @_;
631 0   0       $type ||= 'other';
632 0   0       $delimiter ||= $self->{_read} || $self->{_write};
      0        
633              
634 0           $type =~ s/^-//;
635 0           $type =~ tr/A-Z/a-z/;
636              
637 0 0         if ($type eq 'read') {
    0          
    0          
638 0           $self->{_read} = $delimiter;
639             } elsif ($type eq 'write') {
640 0           $self->{_write} = $delimiter;
641             } elsif ($type eq 'record') { #JWT:SUPPORT ANY RECORD-SEPARATOR!
642             ###$delimiter =~ s/^\r// if ($self->{platform} eq 'PC'); #20000403 (BINMODE HANDLES THIS!!!)
643 0           $self->{_record} = $delimiter;
644             } else {
645 0           $self->{_read} = $self->{_write} = $delimiter;
646             }
647              
648 0           return (1);
649             }
650              
651             sub set_os
652             {
653 0     0 1   my ($self, $platform) = @_;
654             #$platform = 'Unix', return unless ($platform); #20000403.
655 0 0         return $self->{platform} unless ($platform); #20000403
656              
657 0           $platform =~ s/\s//g;
658              
659             # if ($platform =~ /^(?:OS2|(?:Win)?NT|Win(?:dows)?95|(?:MS)?DOS)$/i) {
660             # $self->{platform} = ''; #20000403
661              
662 0 0         if ($platform =~ /(?:darwin|bsdos)/i) #20020218: ADDED FOR NEW MAC OS "OS X" WHICH USES "/"
    0          
    0          
    0          
663             {
664 0           $self->{platform} = 'Unix';
665             }
666             elsif ($platform =~ /(OS2|Win|DOS)/i)
667             { #20000403
668 0           $self->{platform} = 'PC';
669             }
670             elsif ($platform =~ /^Mac(?:OS|intosh)?$/i)
671             {
672 0           $self->{platform} = 'Mac';
673             }
674             elsif ($platform =~ /^VMS$/i)
675             {
676 0           $self->{platform} = 'VMS';
677             }
678             else
679             {
680 0           $self->{platform} = 'Unix';
681             }
682 0           return (1);
683             }
684              
685             sub set_db_dir
686             {
687 0     0 1   my ($self, $directory) = @_;
688 0 0         return (0) unless ($directory);
689              
690 0           stat ($directory);
691              
692             #if ( (-d _) && (-e _) && (-r _) && (-w _) ) { #20000103: REMD WRITABLE REQUIREMENT!
693 0 0 0       if ( (-d _) && (-e _) && (-r _) ) {
      0        
694 0           $self->{directory} = $directory;
695 0           return (1);
696             } else {
697 0           return (0);
698             }
699             }
700              
701             sub set_db_ext #JWT:ADD FILE EXTENSIONS.
702             {
703 0     0 1   my ($self, $ext) = @_;
704              
705 0 0         return (0) unless ($ext);
706              
707 0           stat ($ext);
708              
709 0           $self->{ext} = $ext;
710 0           return (1);
711             }
712              
713             sub get_path_info
714             {
715 0     0 0   my ($self, $file) = @_;
716 0           my ($separator, $path, $name, $full);
717 0           $separator = $self->{separator}->{ $self->{platform} };
718              
719 0           ($path, $name) = $file =~ m|(.*?)([^$separator]+)$|o;
720              
721 0 0         $name =~ tr/A-Z/a-z/ unless ($self->{CaseTableNames}); #JWT:TABLE-NAMES ARE NOW CASE-INSENSITIVE!
722 0 0         if ($path) {
723 0           $full = $file;
724             } else {
725             #$path = $self->{directory} || fastcwd;
726 0           $path = $self->{directory};
727 0           $path .= $separator;
728 0           $full = $path . $name;
729             }
730 0 0         return wantarray ? ($path, $name) : $full;
731             }
732              
733             sub set_lock_file
734             {
735 0     0 1   my ($self, $file, $lock_try) = @_;
736              
737 0 0 0       if (!$file || !$lock_try) {
738 0           return (0);
739             } else {
740 0           $self->{sprite_lock_file} = $file;
741 0           $self->{sprite_lock_try} = $lock_try;
742            
743 0           return (1);
744             }
745             }
746              
747             sub lock
748             {
749 0     0 0   my $self = shift;
750 0           my $count;
751              
752 0   0       $self->{sprite_lock_file} ||= $self->{default_lock};
753 0           $self->{sprite_lock_file} = $self->get_path_info ($self->{sprite_lock_file});
754 0   0       $self->{sprite_lock_try} ||= $self->{default_try};
755              
756 0           local *FILE;
757              
758 0           $count = 0;
759              
760 0           while (++$count <= $self->{sprite_lock_try}) {
761 0 0         if (sysopen (FILE, $self->{sprite_lock_file},
762             O_WRONLY|O_EXCL|O_CREAT, 0644)) {
763              
764 0           $self->{fake_lock} = 1;
765 0           $self->{lock_handle} = *FILE;
766              
767 0           last;
768             } else {
769 0           select (undef, undef, undef, $self->{lock_sleep});
770             }
771             }
772              
773 0           return $self->{fake_lock};
774             }
775              
776             sub unlock
777             {
778 0     0 0   my $self = shift;
779              
780 0 0         if ($self->{fake_lock}) {
781              
782 0 0         close ($self->{lock_handle}) || return (0);
783 0 0         unlink ($self->{sprite_lock_file}) || return (0);
784            
785 0           $self->{fake_lock} = 0;
786 0           $self->{lock_handle} = '';
787              
788             }
789              
790 0           return (1);
791             }
792              
793             sub sql
794             {
795 0     0 1   my ($self, $query) = @_;
796 0           my ($command, $status);
797              
798 0 0         return wantarray ? () : -514 unless ($query);
    0          
799              
800 0           $sprite_user = $self->{'dbuser'}; #ADDED 20011026.
801 0           $self->{lasterror} = 0;
802 0           $self->{lastmsg} = '';
803             #$query =~ s/\n/ /gs; #REMOVED 20011107
804 0           $query =~ s/^\s*(.*?)\s*$/$1/s;
805 0           $command = '';
806              
807 0 0         if ($query =~ /^($self->{commands})/io)
808             {
809 0           $command = $1;
810 0           $command =~ tr/A-Z/a-z/; #ADDED 19991202!
811 0           $status = $self->$command ($query);
812 0 0         if (ref ($status) eq 'ARRAY')
813             { #SELECT RETURNED OK (LIST OF RECORDS).
814             #unshift (@$status, 1);
815              
816 0 0         return wantarray ? @$status : $status;
817             }
818             else
819             {
820 0 0         if ($status < 0)
821             { #SQL RETURNED AN ERROR!
822 0           $self->display_error ($status);
823             #return ($status);
824 0 0         return wantarray ? () : $status;
825             }
826             else
827             { #SQL RETURNED OK.
828 0 0         return wantarray ? ($status) : $status;
829             }
830             }
831             }
832             else
833             {
834 0 0         return wantarray ? () : -514;
835             }
836             }
837              
838             sub display_error
839             {
840 0     0 0   my ($self, $error) = @_;
841              
842 0   0       my $other = $@ || $! || 'None';
843              
844 0 0         print STDERR <{silent});
845              
846             Oops! Sprite encountered the following error when processing your request:
847              
848             ($error) $self->{errors}->{$error} ($errdetails)
849              
850             Here's some more information to help you:
851              
852             file: $self->{file}
853             $other
854              
855             Error_Message
856              
857             #JWT: ADDED FOR ERROR-CONTROL.
858              
859 0           $self->{lasterror} = $error;
860 0           $self->{lastmsg} = "$error:" . $self->{errors}->{$error};
861 0 0         $self->{lastmsg} .= '('.$errdetails.')' if ($errdetails); #20000114
862              
863 0           $errdetails = ''; #20000114
864 0 0         die $self->{lastmsg} if ($self->{RaiseError}); #20000114.
865              
866 0           return (1);
867             }
868              
869             sub commit
870             {
871 0     0 1   my ($self, $file) = @_;
872 0           my ($status, $full_path);
873              
874 0           $status = 1;
875 0 0         return $status unless ($self->{dirty});
876              
877 0 0         if ($file)
878             {
879 0           $full_path = $self->get_path_info ($file);
880 0 0         $full_path .= $self->{ext} if ($self->{ext}); #JWT:ADD FILE EXTENSIONS.
881             }
882             else #ADDED 20010911 TO ASSIST IN HANDLING AUTOCOMMIT!
883             {
884 0           $full_path = $self->{file};
885             }
886 0           $status = $self->write_file ($full_path);
887 0 0         $self->display_error ($status) if ($status <= 0);
888              
889 0 0         return undef if ($status <= 0); #ADDED 20000103
890              
891 0           my $blobglob = $full_path;
892 0           $blobglob =~ s/$self->{ext}$/\_\*\_$$\.tmp/;
893 0           my @tempblobs;
894 0           eval qq|\@tempblobs = <$blobglob>|;
895 0           my ($blobfile, $tempfile);
896 0           my $bloberror = 0;
897 0           while (@tempblobs)
898             {
899 0           $tempfile = shift(@tempblobs);
900 0           $blobfile = $tempfile;
901 0           $blobfile =~ s/\_$$\.tmp/\.ldt/;
902 0 0 0       unlink $blobfile if ($self->{sprite_forcereplace} && -w $blobfile && -e $tempfile);
      0        
903 0 0         $bloberror = $?.':'.$@ if ($?);
904 0 0         rename ($tempfile, $blobfile) or ($bloberror = "Could not rename $tempfile to $blobfile (".$!.')');
905 0 0         last if ($bloberror);
906             }
907 0 0         if ($bloberror)
908             {
909 0           $errdetails = $bloberror;
910 0           $self->display_error (-528);
911 0           return undef;
912             }
913             else
914             {
915             $blobglob = $self->{directory}.$self->{separator}->{ $self->{platform} }
916 0           .$self->{table}."_*_$$.del";
917 0           @tempblobs = ();
918 0           eval qq|\@tempblobs = <$blobglob>|;
919 0           while (@tempblobs)
920             {
921 0           $tempfile = shift(@tempblobs);
922 0           unlink $tempfile;
923             }
924 0           $self->{dirty} = 0;
925             }
926 0           return $status;
927             }
928              
929             sub xclose
930             {
931 0     0 0   my ($self, $file) = @_;
932            
933 0           my $status = $self->commit($file);
934 0           undef $self;
935              
936 0           return $status;
937             }
938              
939             ##++
940             ## Private Methods
941             ##--
942              
943             sub define_errors
944             {
945 0     0 0   my $self = shift;
946 0           my $errors;
947              
948 0           $errors = {};
949              
950 0           $errors->{'-501'} = 'Could not open specified database.';
951 0           $errors->{'-502'} = 'Specified column(s) not found.';
952 0           $errors->{'-503'} = 'Incorrect format in [select] statement.';
953 0           $errors->{'-504'} = 'Incorrect format in [update] statement.';
954 0           $errors->{'-505'} = 'Incorrect format in [delete] statement.';
955 0           $errors->{'-506'} = 'Incorrect format in [add/drop column] statement.';
956 0           $errors->{'-507'} = 'Incorrect format in [alter table] statement.';
957 0           $errors->{'-508'} = 'Incorrect format in [insert] command.';
958 0           $errors->{'-509'} = 'The no. of columns does not match no. of values.';
959 0           $errors->{'-510'} = 'A severe error! Check your query carefully.';
960 0           $errors->{'-511'} = 'Cannot write the database to output file.';
961 0           $errors->{'-512'} = 'Unmatched quote in expression.';
962 0           $errors->{'-513'} = 'Need to open the database first!';
963 0           $errors->{'-514'} = 'Please specify a valid query.';
964 0           $errors->{'-515'} = 'Cannot get lock on database file.';
965 0           $errors->{'-516'} = 'Cannot delete temp. lock file.';
966 0           $errors->{'-517'} = "Built-in function failed ($@).";
967 0           $errors->{'-518'} = "Unique Key Constraint violated."; #JWT.
968 0           $errors->{'-519'} = "Field would have to be truncated."; #JWT.
969 0           $errors->{'-520'} = "Can not create existing table (drop first!)."; #20000225 JWT.
970 0           $errors->{'-521'} = "Can not change datatype on non-empty table."; #20000323 JWT.
971 0           $errors->{'-522'} = "Can not decrease field-size on non-empty table."; #20000323 JWT.
972 0           $errors->{'-523'} = "Special table \"DUAL\" is READONLY!"; #20000323 JWT.
973 0           $errors->{'-524'} = "Can't store non-NULL value into AUTOSEQUENCE!"; #20011029 JWT.
974 0           $errors->{'-525'} = "Can't update AUTOSEQUENCE field!"; #20011029 JWT.
975 0           $errors->{'-526'} = "Can't find encryption modules"; #20011029 JWT.
976 0           $errors->{'-527'} = "Database illedgable - wrong encryption key/method?"; #20011029 JWT.
977 0           $errors->{'-528'} = "Could not read/write BLOB file!"; #20011029 JWT.
978 0           $errors->{'-529'} = "Conversion between BLOB and nonBLOB types not (yet) supported!"; #20011029 JWT.
979 0           $errors->{'-530'} = 'Incorrect format in [create] command.'; #ADDED 20020222
980 0           $errors->{'-531'} = 'Encryption of XML databases not supported.'; #ADDED 20020516.
981 0           $errors->{'-532'} = 'XML requested, but XML::Simple module not available!'; #ADDED 20020516.
982 0           $errors->{'-533'} = 'Incorrect format in [truncate] statement.';
983 0           $self->{errors} = $errors;
984              
985 0           return (1);
986             }
987              
988             sub parse_expression
989             {
990 0     0 0   my ($self, $query, $colmlist) = @_;
991 0 0         return unless ($query);
992 0           my ($column, @strings, %numopmap, %stropmap, $numops, $strops, $special);
993 0   0       $colmlist ||= join('|',@{$self->{order}});
  0            
994 0           my ($psuedocols) = "CURRVAL|NEXTVAL";
995              
996 0 0         unless ($colmlist =~ /\S/o)
997             {
998 0 0         $self->{file} =~ tr/A-Z/a-z/ unless ($self->{CaseTableNames}); #JWT:TABLE-NAMES ARE NOW CASE-INSENSITIVE!
999             #$thefid = $self->{file};
1000 0           $colmlist = &load_columninfo($self, '|');
1001 0 0         return $colmlist if ($colmlist =~ /^\-?\d+$/o);
1002             }
1003 0           $column = $self->{column};
1004 0           @strings = ();
1005              
1006 0           %numopmap = ( '=' => 'eq', '==' => 'eq', '>=' => 'ge', '<=' => 'le',
1007             '>' => 'gt', '<' => 'lt', '!=' => 'ne', '<>' => 'ne');
1008 0           %stropmap = ( 'eq' => '==', 'ge' => '>=', 'le' => '<=', 'gt' => '>',
1009             'lt' => '<', 'ne' => '!=' , '=' => '==');
1010              
1011 0           $numops = join '|', keys %numopmap;
1012 0           $strops = join '|', keys %stropmap;
1013              
1014 0           $special = "$strops|and|or";
1015             #NOTE!: NEVER USE ANY VALUE OF $special AS A COLUMN NAME IN YOUR TABLES!!
1016              
1017             ##++
1018             ## The expression: "([^"\\]*(\\.[^"\\]*)*)" was provided by
1019             ## Jeffrey Friedl. Thanks Jeffrey!
1020             ##--
1021              
1022 0           $query =~ s/\\\\/\x02\^2jSpR1tE\x02/gso; #PROTECT "\\" #XCHGD. TO 2 LINES DOWN 20020111
1023             #$query =~ s/\\\'|\'\'/\x02\^3jSpR1tE\x02/gs; #CHGD. TO NEXT 20040305 2 FIX PERL BUG? UNPROTECT WON'T WORK IF STR="m'str\x02..\x02more'"?!?!?!
1024 0           $query =~ s/\\\'|\'\'/\^3jSpR1tE/gso; #20000201 #PROTECT "", \", '', AND \'.
1025              
1026 0           my ($i, $j, $j2, $k);
1027 0 0         my $caseopt = ($self->{sprite_nocase} ? 'i' : ''); #ADDED 20040325.
1028              
1029 0           while (1)
1030             {
1031 0           $i = 0;
1032 0           $i = ($query =~ s|\b($colmlist)\s+not\s+like\s+|$1 !^ |is);
1033 0 0         $i = ($query =~ s|\b($colmlist)\s+like\s+|$1 =^ |is) unless ($i);
1034 0 0         if ($i)
1035             {
1036             #if ($query =~ /([\=\!]\^\s*)(["'])(.*?)\2/s) #CHGD. TO NEXT 20040325.
1037 0 0         if ($query =~ s/([\=\!]\^\s*)(["'])(.*?)\2/$1$2$3$2$caseopt/s) #20001010
1038             {
1039 0           $j = "$1$2"; #EVERYTHING BEFORE THE QUOTE (DELIMITER), INCLUSIVE
1040 0           $i = $3; #THE STUFF BETWEEN THE QUOTES.
1041 0           my $iquoted = $i; #ADDED 20000816 TO FIX "LIKE 'X.%'" (X\.%)!
1042 0           $iquoted =~ s/([\\\|\(\)\[\{\^\$\*\+\?\.])/\\$1/gs;
1043 0           my ($k) = "\^$iquoted\$";
1044 0           $k =~ s/^\^%//so;
1045 0           $k =~ s/%\$$//s;
1046 0           $j2 = $j;
1047             #$j2 =~ s/^\^/~/; #CHANGE SPECIAL OPERATORS (=^ AND !^) BACK TO (=~ AND !~).
1048 0           $j2 =~ s/^(.)\^/$1~/s; #20001010 CHANGE SPECIAL OPERATORS (=^ AND !^) BACK TO (=~ AND !~).
1049 0           $k =~ s/_/./gso;
1050 0           $query =~ s/\Q$j$i\E/$j2$k/s;
1051             }
1052             }
1053             else
1054             {
1055 0           last;
1056             }
1057             }
1058            
1059             #$query =~ s/([!=][~\^])\s*(m)?([^\w;\s])([^\3\\]*(?:\\.[^\3\\]*)*)\3(i)?/
1060             #THIS REGEX LOOKS FOR USER-DEFINED FUNCTIONS FOLLOWING "=~ OR !~ (LIKE),
1061             #FINDS THE MATCHING CLOSE PARIN(IF ANY), AND SURROUNDS THE FUNCTION AND
1062             #IT'S ARGS WITH AN "&", A DELIMITER LATER USED TO EVAL IT.
1063            
1064 0           1 while ($query =~ s|([!=][~\^])\s*([a-zA-Z_]+)(.*)$|
1065 0           my ($one, $two, $three) = ($1, $2, $3);
1066 0           my ($parincnt) = 0;
1067 0           my (@lx) = split('', $three);
1068 0           my ($i);
1069            
1070 0           for ($i=0;$i<=length($three);$i++)
1071             {
1072 0 0         ++$parincnt if ($lx[$i] eq '(');
1073 0 0         last unless ($parincnt);
1074 0 0         --$parincnt if ($lx[$i] eq ')');
1075             }
1076 0           "$one ".'&'."$two".substr($three,0,$i).'&'.
1077             substr($three,$i);
1078             |es);
1079              
1080             #THIS REGEX HANDLES ALL OTHER LIKE AND PERL "=~" AND "!~" OPERATORS.
1081              
1082 0           @perlconds = ();
1083 0           $query =~ s%\b($colmlist)\s*([!=][~\^])\s*(m)?(.)([^\4]*?)\4(i)?% #20011017: CHGD TO NEXT.
1084 0           my ($m, $i, $delim, $four, $one, $fldname) = ($3, $6, $4, $5, $2, $1);
1085 0           my ($catchmatch) = 0;
1086 0   0       $m ||= ''; $i ||= '';
  0   0        
1087 0 0         $m = 'm' unless ($delim eq '/');
1088 0           my ($three) = $delim;
1089 0           $four =~ s/\\\(/\x02\^5jSpR1tE\x02/gso;
1090 0           $four =~ s/\\\)/\x02\^6jSpR1tE\x02/gso;
1091 0 0         if ($four =~ /\(.*\)/)
1092             {
1093             #$four =~ s/\(//g;
1094             #$four =~ s/\)//g;
1095 0           $catchmatch = 1;
1096             }
1097 0           $four =~ s/\x02\^5jSpR1tE\x02/\(/gso;
1098 0           $four =~ s/\x02\^6jSpR1tE\x02/\)/gso;
1099 0           push (@strings, "$m$delim$four$three$i");
1100 0 0         push (@perlconds, "\$_->{$fldname} $one *$#strings; push (\@perlmatches, \$1) if (defined \$1); push (\@perlmatches, \$2) if (defined \$2);") if ($catchmatch);
1101 0           "$fldname $one *$#strings";
1102             %geis;
1103             #$query =~ s|(['"])([^\1\\]*(?:\\.[^\1\\]*)*)\1|
1104 0           $query =~ s|(["'])(.*?)\1|
1105 0           push (@strings, "$1$2$1"); "*$#strings";
  0            
1106             |ges;
1107              
1108 0           $query =~ s/\x02\^3jSpR1tE\x02/\'/gso; #RESTORE PROTECTED SINGLE QUOTES HERE.
1109 0           $query =~ s/\^3jSpR1tE/\'/gso; #ADDED 20040913 RESTORE PROTECTED SINGLE QUOTES HERE.
1110             #$query =~ s/\x02\^2jSpR1tE\x02/\\/gs; #RESTORE PROTECTED SLATS HERE. #CHGD. TO NEXT 20020111
1111 0           $query =~ s/\x02\^2jSpR1tE\x02/\\\\/gso; #RESTORE PROTECTED SLATS HERE.
1112 0           for $i (0..$#strings)
1113             {
1114             #$strings[$i] =~ s/\x02\^3jSpR1tE\x02/\\\'/gs; #CHGD. TO NEXT IF-STMT. 20040503.
1115             #### NOTE: STRING MUST *NOT* CONTAIN BOTH SINGLE-QUOTES AND GRAVS AND "^"S!!!!!
1116 0 0         if ($strings[$i] =~ /^m\'/o) #TEST MODIFIED 20050429 TO FIX BUG - IF STRING IS LIKE, THEN CHANGE m'str' to m`str` and restore ' UNESCAPED!
1117             { #ALSO HAD 2 REMOVE "\X02" BRACKETS ON RESERVED STR. (PERL BUG?)
1118 0           $strings[$i] =~ s/\^3jSpR1tE/\'/gso; #RESTORE PROTECTED SINGLE QUOTES HERE.
1119 0 0         if ($string !~ /\`/o) #NO GRAVS IN STRING, SAVE TO BRACKET W/GRAVS.
1120             {
1121 0           $strings[$i] =~ s/^m\'/m\`/o;
1122 0           $strings[$i] =~ s/\'${caseopt}$/\`$caseopt/; #JWT:MODIFIED 20150123 TO INCLUDE $caseopt TO FIX BUG W/sprite_nocase
1123             }
1124             else #GRAVS TAKEN TOO, TRY "^" FOR BRACKET CHAR. IF BOTH GRAVS & "^" TAKEN, THEN PUNT!
1125             {
1126 0           $strings[$i] =~ s/^m\'/m\^/o;
1127 0           $strings[$i] =~ s/\'${caseopt}$/\^$caseopt/; #JWT:MODIFIED 20150123 TO INCLUDE $caseopt TO FIX BUG W/sprite_nocase
1128             }
1129             }
1130             else
1131             {
1132 0           $strings[$i] =~ s/\^3jSpR1tE/\\\'/gso; #RESTORE PROTECTED SINGLE QUOTES HERE.
1133             }
1134 0           $strings[$i] =~ s/\x02\^2jSpR1tE\x02/\\\\/gso; #RESTORE PROTECTED SLATS HERE.
1135             }
1136              
1137 0 0         if ($query =~ /^($column)$/)
1138             {
1139 0           $i = $1;
1140             #$query = '&' . $i unless ($i =~ $colmlist); #CHGD. TO NEXT (20011019)
1141 0 0         $query = '&' . $i unless ($i =~ m/($colmlist)/i);
1142             }
1143              
1144 0           $query =~ s#\b($colmlist)\s*($numops)\s*\*#$1 $numopmap{$2} \*#gis;
1145 0           $query =~ s#\b($colmlist)\s*($numops)\s*\'#$1 $numopmap{$2} \'\'#gis;
1146 0           $query =~ s#\b($colmlist)\s*($numops)\s*($colmlist)#$1 $numopmap{$2} $3#gis;
1147             #$query =~ s#\b($colmlist)\s*($numops)\s*($column(?:\(.*?\))?)#$1 $numopmap{$2} $3#gi;
1148 0           $query =~ s%\b($column\s*(?:\(.*?\))?)\s+is\s+null%$1 eq ''%igs;
1149 0           $query =~ s%\b($column\s*(?:\(.*?\))?)\s+is\s+not\s+null%$1 ne ''%igs;
1150             #$query =~ s%\b($colmlist)\s*(?:\(.*?\))?)\s*($numops)\s*CURRVAL%$1 $2 &pscolfn($self,$3)%gi;
1151 0           $query =~ s%($column)\s*($numops)\s*($column\.(?:$psuedocols))%"$1 $2 ".&pscolfn($self,$3)%egs;
  0            
1152             #$query =~ s%\b($column\s*(?:\(.*?\))?)\s*($numops)\s*($column\s*(?:\(.*?\))?)% #CHGD. TO NEXT 20020108 TO FIX BUG WHEN WHERE-CLAUSE TESTED EQUALITY WITH NEGATIVE CONSTANTS.
1153 0           $query =~ s%\b($column\s*(?:\(.*?\))?)\s*($numops)\s*((?:[\+\-]?[0..9+-\.Ee]+|$column)\s*(?:\(.*?\))?)%
1154 0           my ($one,$two,$three) = ($1,$2,$3);
1155 0           $one =~ s/\s+$//;
1156 0           my $ONE = $one;
1157 0 0         $ONE =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
1158 0 0 0       if ($one =~ /NUM\s*\(/ || ${$self->{types}}{$ONE} =~ /$NUMERICTYPES/io)
  0            
1159             {
1160 0           $two =~ s/^($strops)$/$stropmap{$two}/s;
1161 0           "$one $two $three";
1162             }
1163             else
1164             {
1165 0           "$one $numopmap{$two} $three";
1166             }
1167             %egs;
1168              
1169             # (JWT 8/8/1998) $query =~ s|\b($colmlist)\s+($strops)\s+(\d+)|$1 $stropmap{$2} $3|gi;
1170 0           $query =~ s|\b($colmlist)\s*($strops)\s*(\d+)|$1 $stropmap{$2} $3|gis;
1171              
1172 0           my $ineqop = '!=';
1173 0           $query =~ s!\b($colmlist)\s*($strops)\s*(\*\d+)!
1174 0           my ($one,$two,$three) = ($1,$2,$3);
1175 0           $one =~ s/\s+$//;
1176 0           my $ONE = $one;
1177 0 0         $ONE =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
1178 0           my $res;
1179 0 0 0       if ($one =~ /NUM\s*\(/ || ${$self->{types}}{$ONE} =~ /$NUMERICTYPES/ios)
  0 0 0        
1180             {
1181 0           my ($opno) = undef; #NEXT 18 LINES ADDED 20010313 TO CAUSE STRING COMPARISENS W/NUMERIC FIELDS TO RETURN ZERO, SINCE PERL NON-NUMERIC STRINGS RETURN ZERO.
1182 0 0         if ($three =~ /^\*\d+/s)
1183             {
1184 0           $opno = substr($three,1);
1185 0           $opno = $strings[$opno];
1186 0           $opno =~ s/^\'//s;
1187 0           $opno =~ s/\'$//s;
1188             }
1189             else
1190             {
1191 0           $opno = $three;
1192             }
1193 0 0         unless ($opno =~ /^[\+\-\d\.][\d\.Ex\+\-\_]*$/s) #ARGUMENT IS A VALID NUMBER.
1194             {
1195             # $res = '0';
1196             # $res = '1' if ($two eq $ineqop);
1197 0           $res = "$one $two '0'";
1198             }
1199             else
1200             {
1201 0 0         $two =~ s/^($strops)$/$stropmap{$two}/s unless ($opno eq "0");
1202 0           $res = "$one $two $three";
1203             }
1204             }
1205 0           elsif ($self->{StrictCharComp} == 0 && ${$self->{types}}{$ONE} eq 'CHAR')
1206             {
1207 0           my ($opno) = undef; #NEXT 18 LINES ADDED 20010313 TO CAUSE STRING COMPARISENS W/NUMERIC FIELDS TO RETURN ZERO, SINCE PERL NON-NUMERIC STRINGS RETURN ZERO.
1208 0 0         if ($three =~ /^\*\d+/)
1209             {
1210 0           $opno = substr($three,1);
1211 0           my $opstr = $strings[$opno];
1212 0           $opstr =~ s/^\'//s;
1213 0           $opstr =~ s/\'$//s;
1214             $strings[$opno] = "'" . sprintf(
1215 0           '%-'.${$self->{lengths}}{$ONE}.'s',
  0            
1216             $opstr) . "'";
1217             }
1218 0           $res = "$one $two $three";
1219             }
1220             else
1221             {
1222 0           $res = "$one $two $three";
1223             }
1224 0           $res;
1225             !egis;
1226              
1227             #NOTE!: NEVER USE ANY VALUE OF $special AS A COLUMN NAME IN YOUR TABLES!!
1228             #20000224 ADDED "\b" AFTER "$special)" 5 LINES BELOW!
1229 0           $query =~ s!\b(($colmlist))\b!
1230 0           my $match = $1;
1231 0 0         $match =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
1232 0 0         ($match =~ /\b(?:$special)\b/ios) ? "\L$match\E" :
1233             "\$_->{$match}"
1234             !geis;
1235 0           $query =~ s/ (and|or|not) / \L$1\E /igs; #ADDED 20001011 TO FIX BUG THAT DIDN'T ALLOW UPPER-CASE BOOLOPS! 20001215: SPACES ADDED TO PREVENT "$_->{MandY}" MANGLE!
1236 0           $query =~ s|[;`]||gso;
1237 0           $query =~ s#\|\|#or#gso;
1238 0           $query =~ s#&&#and#gso;
1239              
1240 0           $query =~ s|(\d+)\s*($strops)\s*(\d+)|$1 $stropmap{$2} $3|gios; #ADDED 20010313 TO MAKE "1=0" CONDITION EVAL WO/ERROR.
1241 0           $query =~ s|\*(\d+)|$strings[$1]|gs;
1242 0           for (my $i=0;$i<=$#perlconds;$i++)
1243             {
1244 0           $perlconds[$i] =~ s|\*(\d+)|$strings[$1]|gs;
1245             }
1246              
1247             #THIS REGEX EVALS USER-FUNCTION CALLS FOLLOWING "=~" OR "!~".
1248 0           $query =~ s@([!=][~\^])\s*m\&([a-zA-Z_]+[^&]*)\&@
1249 0           my ($one, $two) = ($1, $2);
1250            
1251             #$one =~ s/\^/\~/s; #MOVED INSIDE "UNLESS" BELOW 20040323.
1252 0           my ($res) = eval($two);
1253 0           $res =~ s/^\%//so;
1254 0           $res =~ s/\%$//so;
1255 0           my ($rtn, $isalike);
1256 0           foreach my $i ('/',"'",'"','|')
1257             {
1258 0 0         unless ($res =~ m%$i%)
1259             {
1260 0 0         $isalike = 1 if ($one =~ s/\^/\~/so);
1261 0           $rtn = "$one m".$i.$res.$i;
1262 0 0 0       $rtn .= 'i' if ($self->{sprite_nocase} && $isalike);
1263 0           last;
1264             }
1265             }
1266 0           $rtn;
1267             @egs;
1268 0           return $query;
1269             }
1270              
1271             sub check_columns
1272             {
1273 0     0 0   my ($self, $column_string) = @_;
1274 0           my ($status, @columns, $column);
1275              
1276 0           $status = 1;
1277 0 0         unless ($self->{sprite_CaseFieldNames})
1278             {
1279 0 0         $column =~ tr/a-z/A-Z/ if (defined $column); #JWT
1280 0           $column_string =~ tr/a-z/A-Z/; #JWT
1281             }
1282 0           $self->{use_fields} = $column_string; #JWT
1283 0           @columns = split (/\,/o, $column_string);
1284              
1285 0           foreach $column (@columns) {
1286             #$status = 0 unless ($self->{fields}->{$column}); #20000114
1287 0 0         unless ($self->{fields}->{$column})
1288             {
1289 0           $errdetails = $column;
1290 0           $status = 0;
1291             }
1292             }
1293              
1294 0           return $status;
1295             }
1296              
1297             sub parse_columns
1298             {
1299 0     0 0   my ($self, $command, $column_string, $condition, $values,
1300             $ordercols, $descorder, $fields, $distinct) = @_;
1301 0           my ($i, $j, $k, $rowcnt, $status, @columns, $single, $loop, $code, $column);
1302 0           my (%colorder, $rawvalue);
1303 0           my (@result_index); #ADDED 20020709 TO SUPPORT SORTING ON COLUMNS NOT IN RESULT-SET.
1304              
1305 0           my ($psuedocols) = "CURRVAL|NEXTVAL"; #ADDED 20011019.
1306 0           local $results = undef;
1307 0           my (@keyfields) = split(',', $self->{key_fields}); #JWT: PREVENT DUP. KEYS.
1308 0           my (%valuenames); #ADDED 20001218 TO ALLOW FIELD-NAMES AS RIGHT-VALUES.
1309 0           foreach $i (keys %$values)
1310             {
1311             # $valuenames{$i} = $values->{$i}; #MOVED TO BOTTOM OF LOOP 20020522 TO FIX SINGLE-QUOTE-IN-VALUE (-517) BUG!
1312 0           $values->{$i} =~ s/^\'(.*)\'$/my ($stuff) = $1;
  0            
1313 0           $stuff =~ s|\'|\\\'|gso;
1314 0           $stuff =~ s|\\\'\\\'|\\\'|gso;
1315 0           "'" . $stuff . "'"/es;
1316 0           $values->{$i} =~ s/^\'$//so; #HANDLE NULL VALUES.
1317             #$values->{$i} =~ s/\n//gs; #REMOVE LFS ADDED BY NETSCAPE TEXTAREAS! #REMOVED 20011107 - ALLOW \n IN DATA!
1318             #$values->{$i} =~ s/\r /\r/gs; #20000108: FIX LFS PREV. CONVERTED TO SPACES! #REMOVED 20011107 (SHOULDN'T NEED ANYMORE).
1319 0 0         $values->{$i} = "''" unless ($values->{$i} =~ /\S/o);
1320 0           $valuenames{$i} = $values->{$i};
1321             }
1322 0     0     local $SIG{'__WARN__'} = sub { $status = -510; $errdetails = "$_[0] at ".__LINE__ };
  0            
  0            
1323 0           local $^W = 0;
1324 0           local ($_);
1325 0           $status = 1;
1326 0           $results = [];
1327 0           @columns = split (/,/o, $column_string);
1328              
1329 0 0         if ($command eq 'update') #ADDED NEXT 11 LINES 20011029 TO PROTECT AUTOSEQUENCE FIELDS FROM UPDATES.
1330             {
1331 0           foreach my $i (@columns)
1332             {
1333 0 0         if (${$self->{types}}{$i} =~ /AUTO/o)
  0            
1334             {
1335 0           $errdetails = $i;
1336 0           return (-525);
1337             }
1338             }
1339             }
1340             #$single = ($#columns) ? $columns[$[] : $column_string;
1341 0 0         $single = ($#columns) ? $columns[$#columns] : $column_string;
1342 0           $rowcnt = 0;
1343              
1344 0           my (@these_results);
1345 0           my ($skipreformat) = 0;
1346 0           my ($colskipreformat) = 0;
1347 0           my (@types);
1348             my (@coltypes);
1349 0           @coltypes = ();
1350 0           for (my $i=0;$i<=$#columns;$i++)
1351             {
1352 0           push (@coltypes, (${$self->{types}}{$columns[$i]} =~ /$REFTYPES/o));
  0            
1353             }
1354 0 0         if ($fields)
1355             {
1356 0           @types = ();
1357 0           for (my $i=0;$i<=$#{$fields};$i++)
  0            
1358             {
1359             #$_ = (${$self->{types}}{$columns[$i]} =~ /$REFTYPES/o);
1360 0   0       push (@types, ((${$self->{types}}{$columns[$i]} =~ /$REFTYPES/o)||0));
1361             }
1362             }
1363             else
1364             {
1365 0           push (@$results, [ @$_{@columns} ]);
1366 0           for (my $i=0;$i<=@{$_{@columns}};$i++)
  0            
1367             {
1368             #$_ = (${$self->{types}}{$i} =~ /$REFTYPES/o);
1369 0   0       push (@types, ((${$self->{types}}{$i} =~ /$REFTYPES/o)||0));
1370             }
1371             }
1372 0           my $blobfid;
1373             my $jj;
1374 0   0       $self->{sprite_reclimit} ||= $self->{sprite_sizelimit}; #ADDED 20020530 FOR SQL-PERL PLUS!
1375 0           for ($loop=0; $loop < scalar @{ $self->{records} }; $loop++)
  0            
1376             {
1377 0 0         next unless (defined $self->{records}->[$loop]); #JWT: DON'T RETURN BLANK DELETED RECORDS.
1378 0           $_ = $self->{records}->[$loop];
1379 0           $@ = '';
1380             #####print "<<<<<<< JSPRITE EVAL CONDITION=$condition=\n";
1381 0 0 0       if ( !$condition || (eval $condition) )
    0          
1382             {
1383 0 0         if ($command eq 'select')
    0          
    0          
    0          
1384             {
1385 0 0 0       last if ($self->{sprite_reclimit} && $loop >= $self->{sprite_reclimit}); #ADDED 20020123 TO SPEED UP INFO-ONLY. FETCHES.
1386 0 0         if ($fields)
1387             {
1388 0           @these_results = ();
1389 0           for (my $i=0;$i<=$#{$fields};$i++)
  0            
1390             {
1391 0           $fields->[$i] =~ s/($self->{column}\.(?:$psuedocols))\b/&pscolfn($self,$1)/eg; #ADDED 20011019
  0            
1392 0           $rawvalue = eval $fields->[$i];
1393 0 0 0       if ($types[$i] && $rawvalue =~ /^\d+$/o) #A LONG (REFERENCED) TYPE
1394             {
1395             $blobfid = $self->{directory}
1396             .$self->{separator}->{ $self->{platform} }
1397 0           .$self->{table}."_${rawvalue}_$$.tmp";
1398 0 0         if (open(FILE, "<$blobfid"))
1399             {
1400 0           binmode FILE;
1401 0           $rawvalue = '';
1402 0           my $rawline;
1403 0           while ($rawline = )
1404             {
1405 0           $rawvalue .= $rawline;
1406             }
1407 0           close FILE;
1408             }
1409             else
1410             {
1411             $blobfid = $self->{directory}
1412             .$self->{separator}->{ $self->{platform} }
1413 0           .$self->{table}."_${rawvalue}.ldt";
1414 0 0         if (open(FILE, "<$blobfid"))
1415             {
1416 0           binmode FILE;
1417 0           $rawvalue = '';
1418 0           my $rawline;
1419 0           while ($rawline = )
1420             {
1421 0           $rawvalue .= $rawline;
1422             }
1423 0           close FILE;
1424             }
1425             else
1426             {
1427 0           $errdetails = "$blobfid ($?)";
1428 0           return (-528);
1429             }
1430             }
1431             }
1432 0           push (@these_results, $rawvalue);
1433             }
1434 0           push (@$results, [ @these_results ]);
1435 0           push (@result_index, $loop); #ADDED 20020709 TO SUPPORT SORTING ON COLUMNS NOT IN RESULT-SET.
1436             }
1437             else #I THINK THIS IS DEAD CODE!!!
1438             {
1439             #print "
-pc: SHOULD BE DEAD-CODE, PLEASE EMAIL JIM TURNER THE QUERY WHICH GOT HERE!
\n";
1440             #foreach my $i (@columns) {print "
$i: =$_{$i}=\n";};
1441 0           push (@$results, [ @$_{@columns} ]);
1442             }
1443             }
1444             elsif ($command eq 'update')
1445             {
1446 0           @perlmatches = ();
1447 0           for (my $i=0;$i<=$#perlconds;$i++)
1448             {
1449 0           eval $perlconds[$i];
1450             }
1451 0           $code = '';
1452 0           my ($matchcnt) = 0;
1453 0           my (@valuelist) = keys(%$values);
1454             #my ($dontchkcols) = '('.join('|',@valuelist).')';
1455 0           my ($dontchkcols) = '('.join('|',@valuelist);
1456 0           for (my $i=0;$i<=$#columns;$i++)
1457             {
1458 0 0         $dontchkcols .= '|'.$columns[$i] if ($coltypes[$i]);
1459             }
1460 0           $dontchkcols .= ')';
1461 0           foreach $i (@valuelist)
1462             {
1463 0           for ($j=0;$j<=$#keyfields;$j++)
1464             {
1465 0 0         if ($i eq $keyfields[$j])
1466             {
1467 0           K: for ($k=0;$k < scalar @{ $self->{records} }; $k++)
  0            
1468             {
1469 0           $rawvalue = $values->{$i};
1470 0           $rawvalue =~ s/^\'(.*)\'\s*$/$1/s;
1471 0 0         if ($self->{records}->[$k]->{$i} eq $rawvalue)
1472             {
1473 0           foreach $jj (@keyfields)
1474             {
1475 0 0         unless ($jj =~ /$dontchkcols/)
1476             {
1477             next K
1478             unless ($self->{records}->[$k]->{$jj}
1479 0 0         eq $_->{$jj});
1480             }
1481             }
1482 0           goto MATCHED1;
1483             }
1484             }
1485 0           goto NOMATCHED1;
1486 0           MATCHED1: ++$matchcnt;
1487             }
1488             }
1489             }
1490 0 0 0       return (-518) if ($matchcnt && $matchcnt > $#valuelist); #ALL KEY FIELDS WERE DUPLICATES!
1491             NOMATCHED1:
1492 0           $self->{dirty} = 1;
1493 0           foreach $jj (@columns) #JWT 19991104: FORCE TRUNCATION TO FIT!
1494             {
1495 0           $colskipreformat = $skipreformat;
1496             #$rawvalue = $values->{$jj}; #CHGD TO NEXT 20011018.
1497 0           $rawvalue = $valuenames{$jj};
1498             #NEXT LINE ADDED 20011018 TO HANDLE PERL REGEX SUBSTITUTIONS.
1499 0 0         $colskipreformat = 0 if ($rawvalue =~ s/\$(\d)/$perlmatches[$1-1]/g);
1500 0 0         if ($valuenames{$jj} =~ /^[_a-zA-Z]/o) #NEXT 5 LINES ADDED 20000516 SO FUNCTIONS WILL WORK IN UPDATES!
1501             {
1502 0 0         if ($self->{sprite_CaseFieldNames})
1503             {
1504 0 0         unless ($self->{fields}->{"$valuenames{$jj}"}) #ADDED TEST 20001218 TO ALLOW FIELD-NAMES AS RIGHT-VALUES.
1505             {
1506             #$rawvalue = &chkcolumnparms($self, $valuenames{$jj}); #CHGD. TO NEXT 20011018.
1507 0           $rawvalue = &chkcolumnparms($self, $rawvalue);
1508 0           $rawvalue = eval $rawvalue; #FUNCTION EVAL 3
1509 0 0         return (-517) if ($@);
1510             }
1511             else
1512             {
1513 0           $rawvalue = $_->{$valuenames{$jj}};
1514             }
1515             }
1516             else
1517             {
1518 0 0         unless ($self->{fields}->{"\U$valuenames{$jj}\E"}) #ADDED TEST 20001218 TO ALLOW FIELD-NAMES AS RIGHT-VALUES.
1519             {
1520             #$rawvalue = &chkcolumnparms($self, $valuenames{$jj}); #CHGD. TO NEXT 20011018.
1521 0           $rawvalue = &chkcolumnparms($self, $rawvalue);
1522 0           $rawvalue = eval $rawvalue; #FUNCTION EVAL 3
1523 0 0         return (-517) if ($@);
1524             }
1525             else
1526             {
1527 0           $rawvalue = $_->{$valuenames{$jj}};
1528             }
1529             }
1530 0           $colskipreformat = 0;
1531             }
1532             else
1533             {
1534 0 0         $rawvalue =~ s/^\'(.*)\'\s*$/$1/s if ($valuenames{$jj} =~ /^\'/o);
1535             }
1536             #if (${$self->{types}}{$jj} =~ /$NUMERICTYPES/) #CHGD TO NEXT LINE 20010313.
1537              
1538 0 0         unless ($colskipreformat) #ADDED 20011018 TO OPTIMIZE.
1539             {
1540 0 0 0       if (length($rawvalue) > 0 && ${$self->{types}}{$jj} =~ /$NUMERICTYPES/)
  0            
1541             {
1542 0           $k = sprintf(('%.'.${$self->{scales}}{$jj}.'f'),
  0            
1543             $rawvalue);
1544             }
1545             else
1546             {
1547 0           $k = $rawvalue;
1548             }
1549             #$rawvalue = substr($k,0,${$self->{lengths}}{$jj});
1550 0 0         $rawvalue = (${$self->{types}}{$jj} =~ /$BLOBTYPES/) ? $k : substr($k,0,${$self->{lengths}}{$jj});
  0            
  0            
1551 0 0 0       unless ($self->{LongTruncOk} || $rawvalue eq $k ||
      0        
1552 0           (${$self->{types}}{$jj} eq 'FLOAT'))
1553             {
1554 0           $errdetails = "$jj to ${$self->{lengths}}{$jj} chars";
  0            
1555 0           return (-519); #20000921: ADDED (MANY PLACES) LENGTH TO ERRDETAILS "(fieldname to ## chars)"
1556             }
1557 0 0 0       if ((${$self->{types}}{$jj} eq 'FLOAT')
  0            
1558             && (int($rawvalue) != int($k)))
1559             {
1560 0           $errdetails = "$jj to ${$self->{lengths}}{$jj} chars";
  0            
1561 0           return (-519);
1562             }
1563             #if (${$self->{types}}{$jj} eq 'CHAR') #CHGD. TO NEXT 20030812.
1564 0 0 0       if (${$self->{types}}{$jj} eq 'CHAR' && length($rawvalue) > 0)
  0            
1565             {
1566             $values->{$jj} = "'" . sprintf(
1567 0           '%-'.${$self->{lengths}}{$jj}.'s',
  0            
1568             $rawvalue) . "'";
1569             }
1570             #elsif (${$self->{types}}{$jj} !~ /$NUMERICTYPES/) #CHGD. TO NEXT 20010313.
1571             #CHGD. TO NEXT 20160111: elsif (!length($rawvalue) || ${$self->{types}}{$jj} !~ /$NUMERICTYPES/)
1572             #REASON: STOP TRAILING ZEROES IN DECIMALS FROM BEING TRUNCATED (WE ALREADY FORMATTED AT LINE 1541 ABOVE!)
1573             else
1574             {
1575 0           $values->{$jj} = "'" . $rawvalue . "'";
1576             }
1577             #xNEXT 4 REMOVED 20160111: else
1578             #x {
1579             #x $values->{$jj} = $rawvalue;
1580             #x }
1581             }
1582             }
1583             #map { $code .= qq|\$_->{'$_'} = $values->{$_};| } @columns; #NEXT 2 CHGD TO NEXT 34 20020125 TO SUPPORT BLOB REFERENCING.
1584             #eval $code;
1585 0           for (my $i=0;$i<=$#columns;$i++)
1586             {
1587 0 0         if ($coltypes[$i]) #BLOB REF.
1588             {
1589 0           $code = qq|\$rawvalue = $values->{$columns[$i]};|;
1590 0           eval $code;
1591             $blobfid = $self->{directory}.$self->{separator}->{ $self->{platform} }
1592 0           .$self->{table}.'_'.$_->{$columns[$i]}."_$$.tmp";
1593 0 0         if (open(FILE, ">$blobfid"))
1594             {
1595 0           binmode FILE;
1596 0 0 0       if ($self->{CBC} && $self->{sprite_Crypt} <= 2) #ADDED: 20020109
1597             {
1598 0           print FILE $self->{CBC}->encrypt($rawvalue);
1599             }
1600             else
1601             {
1602 0           print FILE $rawvalue;
1603             }
1604 0           close FILE;
1605             }
1606             else
1607             {
1608 0           $errdetails = "$blobfid: ($?)";
1609 0           return (-528);
1610             }
1611             }
1612             else
1613             {
1614 0           $code = qq|\$_->{'$columns[$i]'} = $values->{$columns[$i]};|;
1615 0           eval $code;
1616             }
1617             }
1618              
1619 0 0         return (-517) if ($@);
1620             }
1621             elsif ($command eq 'add')
1622             {
1623 0           $_->{$single} = ''; #ORACLE DOES NOT SET EXISTING RECORDS TO DEFAULT VALUE!
1624             }
1625             elsif ($command eq 'drop')
1626             {
1627 0           delete $_->{$single};
1628             }
1629 0           ++$rowcnt;
1630 0           $skipreformat = 1;
1631             }
1632             elsif ($@) #ADDED 20010313 TO CATCH SYNTAX ERRORS.
1633             {
1634 0           $errdetails = "Condition failed ($@) in condition=$condition!";
1635 0 0         return -503 if ($command eq 'select');
1636 0 0         return -505 if ($command eq 'delete');
1637 0           return -504;
1638             }
1639             }
1640 0 0         if ($status <= 0)
    0          
1641             {
1642 0           return $status;
1643             }
1644             elsif ( $command ne 'select' )
1645             {
1646 0           return $rowcnt;
1647             }
1648             else
1649             {
1650 0           my $theresanull = 0; #ADDED 20030930 TO HANDLE SINGLE NULL ELEMENT TO FIX _set_fbav ERROR!
1651 0           my $rowcntdigits = length(scalar(@$results)); #ADDED 20050514 TO ENSURE SORTING WORKS CORRECTLY.
1652 0           my ($ii, $t);
1653 0 0         if ($distinct) #THIS IF ADDED 20010521 TO MAKE "DISTINCT" WORK.
1654             {
1655 0           my (%disthash);
1656 0           for (my $i=0;$i<=$#$results;$i++)
1657             {
1658 0           ++$disthash{join("\x02\^2jSpR1tE\x02",@{$results->[$i]})};
  0            
1659             }
1660 0           @$results = ();
1661             #foreach my $i (sort keys(%disthash)) #CHGD. TO NEXT 20050514 - UNNECESSARY TO SORT.
1662 0           foreach my $i (keys(%disthash))
1663             {
1664 0 0         if ($i eq '') #(20030930) SINGLE NULL ELEMENT MUST GO ON *END* OF ARRAY!
1665             {
1666 0           $theresanull = 1;
1667 0           next;
1668             }
1669             # push (@$results, [split(/\x02\^2jSpR1tE\x02/, $i)]); #CHGD. TO NEXT 20031001 TO FIX _set_fbav ERROR!
1670 0           push (@$results, [split(/\x02\^2jSpR1tE\x02/o, $i, -1)]);
1671             }
1672             }
1673 0 0         if (@$ordercols) #COMPLETELY OVERHAULED 20020708 TO SUPPORT MULTIPLE ASCENDING/DESCENDING DECISIONS & SORTING ON COLUMNS NOT IN RESULT-SET!
1674             {
1675 0           @$ordercols = reverse(@$ordercols);
1676 0           @$descorder = reverse(@$descorder);
1677 0           $rowcnt = 0;
1678             #my ($mysep) = "\x02\^2jSpR1tE\x02";
1679             #$mysep = "\xFF" if ($descorder);
1680             #my @mysep = ("^", "V");
1681 0           my @mysep = ("\x00", "\xff");
1682 0           my @SA = ();
1683 0           my @SSA = ();
1684 0           my @SI = ();
1685 0           my @l;
1686 0           for (0..$#columns)
1687             {
1688 0           $colorder{$columns[$_]} = $_;
1689             }
1690 0           for (my $i=0;$i<=$#$results;$i++)
1691             {
1692 0           $t = sprintf('%'.$rowcntdigits.'.'.$rowcntdigits.'d', $i); #ADDED 20050514 TO ENSURE SORTING WORKS CORRECTLY.
1693 0           push (@SI, $t);
1694 0           push (@SSA, $t);
1695             }
1696 0           my $jcnt = 0;
1697 0 0         my $do = ($descorder->[0] =~ /de/io) ? 1 : 0;
1698 0           my $fieldval;
1699 0           foreach my $j (@$ordercols)
1700             {
1701 0 0         $j =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
1702             #$k = $colorder{$j} || -1; #CHGD. TO NEXT 20050514 TO FIX BUG THAT PREVENTED SORTING FROM WORKING W/SELECT DISTINCT.
1703 0 0         $k = defined($colorder{$j}) ? $colorder{$j} : -1;
1704 0           for (my $i=0;$i<=$#$results;$i++)
1705             {
1706             $fieldval = ($k >= 0) ?
1707 0           ${$results}[$SI[$i]]->[$k]
1708 0 0         : $self->{records}->[$result_index[$SI[$i]]]->{$j};
1709 0 0 0       if (${$self->{types}}{$j} eq 'FLOAT' || ${$self->{types}}{$j} eq 'DOUBLE')
  0 0 0        
  0            
1710             {
1711 0           push (@SA, (sprintf('%'.${$self->{lengths}}{$j}.${$self->{scales}}{$j}.'e',$fieldval) . $mysep[$do] . $SSA[$i]));
  0            
  0            
1712             }
1713 0           elsif (length ($fieldval) > 0 && ${$self->{types}}{$j} =~ /$NUMERICTYPES/)
1714             {
1715 0           push (@SA, (sprintf('%'.${$self->{lengths}}{$j}.${$self->{scales}}{$j}.'f',$fieldval) . $mysep[$do] . $SSA[$i]));
  0            
  0            
1716             }
1717             else
1718             {
1719 0           push (@SA, ($fieldval . $mysep[$do] . $SSA[$i]));
1720             }
1721             }
1722 0           @SI = ();
1723 0           @SSA = ();
1724 0           @SI = sort {$a cmp $b} @SA;
  0            
1725 0 0         @SI = reverse(@SI) if ($do);
1726 0           @SA = ();
1727 0           my $ii = $#SI;
1728 0           $l = length($ii);
1729 0 0         if ($jcnt < $#$ordercols)
1730             {
1731 0 0         $do = ($descorder->[++$jcnt] =~ /de/io) ? 1 : 0;
1732 0           for (my $i=0;$i<=$#SI;$i++)
1733             {
1734 0 0         $SI[$i] = $1 if ($SI[$i] =~ /(\d+)$/o);
1735             #push (@SSA, sprintf("%${l}d",$ii--) . $mysep[$do] . $SI[$i]); #CHGD. TO NEXT 20050514 TO ENSURE SORTING WORKS CORRECTLY.
1736 0 0         push (@SSA, sprintf("%${l}d",($do ? $ii-- : $i)) . $mysep[$do] . sprintf('%'.$rowcntdigits.'.'.$rowcntdigits.'d',$SI[$i]));
1737             }
1738             }
1739             }
1740 0           @SA = @$results;
1741 0           @$results = ();
1742 0           for (my $i=0;$i<=$#SI;$i++)
1743             {
1744 0 0         $SI[$i] = $1 if ($SI[$i] =~ /(\d+)$/o);
1745 0           push (@$results, $SA[$SI[$i]]);
1746             }
1747             }
1748 0 0         if ($theresanull) #ADDED 20030930 TO HANDLE SINGLE NULL ELEMENT TO FIX _set_fbav ERROR!
1749             {
1750 0           unshift (@$results, ['']);
1751             }
1752             #$rowcnt = $#$results + 1;
1753             #NEXT 2 ADDED 20160111 TO SUPPORT "limit #" ON QUERIES:
1754 0           $#{$results} = $self->{sprite_actlimit} - 1
1755 0 0 0       if ($self->{sprite_actlimit} > 0 && $#{$results} >= $self->{sprite_actlimit});
  0            
1756 0           $rowcnt = scalar(@{$results});
  0            
1757             }
1758 0           unshift (@$results, $rowcnt);
1759 0           return $results;
1760             }
1761              
1762             sub check_for_reload
1763             {
1764 0     0 0   my ($self, $file) = @_;
1765 0           my ($table, $path, $status);
1766              
1767 0 0         return unless ($file);
1768              
1769 0 0         if ($file =~ /^DUAL$/io) #ADDED 20000306 TO HANDLE ORACLE'S "DUAL" TABLE!
1770             {
1771 0           undef %{ $self->{types} };
  0            
1772 0           undef %{ $self->{lengths} };
  0            
1773 0           $self->{use_fields} = 'DUMMY';
1774 0           $self->{key_fields} = 'DUMMY'; #20000223 - FIX LOSS OF KEY ASTERISK ON ROLLBACK!
1775 0           ${$self->{types}}{DUMMY} = 'VARCHAR2';
  0            
1776 0           ${$self->{lengths}}{DUMMY} = 1;
  0            
1777 0           ${$self->{scales}}{DUMMY} = 1;
  0            
1778 0           $self->{order} = [ 'DUMMY' ];
1779 0           $self->{fields}->{DUMMY} = 1;
1780 0           undef @{ $self->{records} };
  0            
1781 0           $self->{records}->[0] = {'DUMMY' => 'X'};
1782 0           $self->{table} = 'DUAL';
1783 0           return (1);
1784             }
1785              
1786 0           ($path, $table) = $self->get_path_info ($file);
1787 0           $file = $path . $table; # if ($table eq $file);
1788 0 0         $file .= $self->{ext} if ($self->{ext}); #JWT:ADD FILE EXTENSIONS.
1789 0           $self->{table} = $table;
1790 0           $status = 1;
1791              
1792 0           my (@stats) = stat ($file);
1793 0 0 0       if ( ($self->{table} ne $table) || ($self->{file} ne $file
      0        
1794             || $self->{timestamp} != $stats[9]) )
1795             {
1796 0 0 0       if ( (-e _) && (-s _) && (-r _) )
      0        
1797             {
1798              
1799 0           $self->{table} = $table;
1800 0           $self->{file} = $file;
1801 0           $status = $self->load_database ($file);
1802 0           $self->{timestamp} = $stats[9];
1803             }
1804             else
1805             {
1806 0           $errdetails = $file; #20000114
1807 0           $status = 0;
1808             }
1809             }
1810              
1811 0 0         $errdetails = $file if ($status == 0); #20000114
1812 0           return $status;
1813             }
1814              
1815             sub rollback
1816             {
1817 0     0 0   my ($self) = @_;
1818 0           my ($table, $path, $status);
1819              
1820 0           my (@stats) = stat ($self->{file});
1821            
1822 0 0 0       if ( (-e _) && (-T _) && (-s _) && (-r _) )
      0        
      0        
1823             {
1824 0           $status = $self->load_database ($self->{file});
1825 0           $self->{timestamp} = $stats[9];
1826             }
1827             else
1828             {
1829 0           $status = 0;
1830             }
1831 0           my $blobglob = $self->{file};
1832 0           $blobglob =~ s/$self->{ext}$/\_\*\_$$\.tmp/;
1833 0           my $bloberror = 0;
1834 0           unlink $blobglob;
1835 0 0         $bloberror = $?.':'.$@ if ($?);
1836             #if ($bloberror) #CHGD. TO NEXT 20020222 TO PREVENT EXTRA FALSE ERROR MSG.
1837 0 0 0       if ($blobglob && $bloberror)
1838             {
1839 0           $errdetails = $bloberror;
1840 0           $self->display_error (-528);
1841 0           return undef;
1842             }
1843             else
1844             {
1845             $blobglob = $self->{directory}.$self->{separator}->{ $self->{platform} }
1846 0           .$self->{table}."_*_$$.del";
1847 0           my @tempblobs = ();
1848 0           eval qq|\@tempblobs = <$blobglob>|;
1849 0           my ($blobfile, $tempfile);
1850 0           while (@tempblobs)
1851             {
1852 0           $tempfile = shift(@tempblobs);
1853 0           $blobfile = $tempfile;
1854 0           $blobfile =~ s/\_$$\.del/\.ldt/;
1855 0           rename ($tempfile, $blobfile);
1856             }
1857 0           $self->{dirty} = 0;
1858             }
1859 0           return $status;
1860             }
1861              
1862             sub select
1863             {
1864 0     0 1   my ($self, $query) = @_;
1865 0           my ($i, @l, $regex, $path, $columns, $table, $extra, $condition,
1866             $values_or_error, $descorder, @descorder);
1867 0           my (@ordercols) = ();
1868 0           $regex = $self->{_select};
1869 0           $path = $self->{path};
1870             #$fieldregex = $self->{fieldregex};
1871 0           my ($psuedocols) = "CURRVAL|NEXTVAL";
1872              
1873 0           my $distinct; #NEXT 2 ADDED 20010521 TO ADD "DISTINCT" CAPABILITY!
1874 0 0         $distinct = 1 if ($query =~ /^select\s+distinct/o);
1875 0           $query =~ s/^select\s+distinct(\s+\w|\s*\(|\s+\*)/select $1/is;
1876              
1877              
1878 0 0         if ($query =~ /^select\s+
1879             (.+)\s+
1880             from\s+
1881             (\w+)(.*)$/ioxs)
1882             {
1883 0           my ($column_stuff, $table, $extra) = ($1, $2, $3);
1884 0           my (@fields) = ();
1885 0           my ($fnname, $found_parin, $parincnt, $t);
1886 0           my @column_stuff;
1887              
1888             #ORACLE COMPATABILITY!
1889              
1890 0 0 0       if ($column_stuff =~ /^table_name\s*$/io && $table =~ /^(user|all)_tables$/io) #JWT: FETCH TABLE NAMES!
1891             {
1892 0           my $full_path = $self->{directory};
1893             $full_path .= $self->{separator}->{ $self->{platform} }
1894 0 0 0       unless ($full_path !~ /\S/o
1895             || $full_path =~ m#$self->{separator}->{ $self->{platform} }$#);
1896 0           my ($cmd);
1897 0           $cmd = $full_path . '*' . $self->{ext};
1898 0           my ($code);
1899 0 0         if ($^O =~ /Win/i) #NEEDED TO MAKE PERL2EXE'S "-GUI" VERSION WORK!
1900             {
1901 0           @l = glob $cmd;
1902             }
1903             else
1904             {
1905 0           @l = ();
1906 0           $code = "while (my \$i = <$cmd>)\n";
1907 0           $code .= <<'END_CODE';
1908             {
1909             chomp ($i);
1910             push (@l, $i);
1911             }
1912             END_CODE
1913 0           eval $code;
1914             }
1915 0           $self->{use_fields} = 'TABLE_NAME'; #ADDED 20000224 FOR DBI!
1916 0           $values_or_error = [];
1917 0           for ($i=0;$i<=$#l;$i++) {
1918             #chomp($l[$i]); #NO LONGER NEEDED 20000228
1919 0 0         if ($^O =~ /Win/i) #COND. ADDED 20010321 TO HANDLE WINDOZE FILENAMES (CAN BE UPPER & OR LOWER)!
1920             {
1921 0           $l[$i] =~ s/${full_path}(.*?)$self->{ext}/$1/i;
1922 0           $l[$i] =~ s/$self->{ext}$//i; #ADDED 20000418 - FORCE THIS!
1923             }
1924             else
1925             {
1926 0           $l[$i] =~ s/${full_path}(.*?)$self->{ext}/$1/;
1927 0           $l[$i] =~ s/$self->{ext}$//; #ADDED 20000418 - FORCE THIS!
1928             }
1929 0           push (@$values_or_error,[$l[$i]]);
1930             }
1931 0           unshift (@$values_or_error, ($#l+1));
1932 0           return $values_or_error;
1933             }
1934              
1935             #SPLIT UP THE FIELDS BEING REQUESTED.
1936              
1937 0           $self->{ASNAMES} = {}; #ADDED NEXT 4 LINES 20040913 TO SUPPORT "AS".
1938 0           while ($column_stuff =~ s/($self->{column})\s+(?:AS|as)\s+($self->{column})/$1/)
1939             {
1940 0           $self->{ASNAMES}->{$1} = $2;
1941             };
1942 0           $column_stuff =~ s/\s+$//o;
1943 0           while (1)
1944             {
1945 0           $found_parin = 0;
1946 0           $column_stuff =~ s/^\s+//o;
1947 0           $fnname = '';
1948             # $fnname = $1 if ($column_stuff =~ s/^(\w+)//); #CHGD TO NEXT 20020211!
1949 0 0         $fnname = $1 if ($column_stuff =~ s/^($self->{column}(?:\.(?:$psuedocols))?)//);
1950 0           $column_stuff =~ s/^ +//o;
1951 0 0         last unless ($fnname);
1952 0           @column_stuff = split(//o,$column_stuff);
1953 0 0 0       if ($#column_stuff <= 0 || $column_stuff[0] eq ',')
1954             {
1955 0           push (@fields, $fnname);
1956 0           $column_stuff =~ s/^\,//o;
1957 0           next;
1958             }
1959              
1960             #FOR FUNCTIONS W/ARGS, WE MUST FIND THE CLOSING ")"!
1961              
1962 0           for ($i=0;$i<=length($column_stuff);$i++)
1963             {
1964 0 0         if ($column_stuff[$i] eq '(')
1965             {
1966 0           ++$parincnt;
1967 0           $found_parin = 1;
1968             }
1969 0 0 0       last if (!$parincnt && $found_parin);
1970 0 0         --$parincnt if ($column_stuff[$i] eq ')');
1971             }
1972 0           push (@fields, ($fnname . substr($column_stuff,0,$i)));
1973 0           $t = substr($column_stuff,$i);
1974 0           $t =~ s/^\s*\,//o;
1975 0 0         last unless ($t);
1976 0           $column_stuff = $t;
1977             }
1978              
1979             #$thefid = $table;
1980             #$self->check_for_reload ($table) || return (-501); #CHGD. TO NEXT 20020110 TO BETTER CATCH ERRORS.
1981 0   0       my $cfr = $self->check_for_reload($table) || -501;
1982 0 0         return $cfr if ($cfr < 0);
1983 0           $columns = '';
1984 0           my (@strings);
1985 0           my ($column_list) = '('.join ('|', @{ $self->{order} }).')';
  0            
1986              
1987             #DETERMINE WHICH WORDS ARE VALID COLUMN NAMES AND CONVERT THEM INTO
1988             #THE VARIABLE FOR LATER EVAL IN PARSE_EXPRESSION! OTHER WORDS ARE
1989             #TREATED AS FUNCTION NAMES AND ARE EVALLED AS THEY ARE.
1990              
1991 0           for (my $i=0;$i<=$#fields;$i++)
1992             {
1993 0           @strings = ();
1994              
1995             #FIRST, WE MUST PROTECT COLUMN NAMES APPEARING IN LITERAL STRINGS!
1996              
1997 0           $fields[$i] =~ s|(\'[^\']+\')|
1998 0           push (@strings, $1);
1999 0           "\x02\^2jSpR1tE\x02$#strings\x02\^2jSpR1tE\x02"
2000             |eg;
2001              
2002             #NOW CONVERT THE REMAINING COLUMN NAMES TO "$$_{COLUMN_NAME}"!
2003              
2004             #$fields[$i] =~ s/($column_list)/ #ADDED WORD-BOUNDARIES 20011129 TO FIX BUG WHERE ONE COLUMN NAME CONTAINED ANOTHER ONE, IE. "EMPL" AND "EMPLID".
2005 0 0         if ($self->{sprite_CaseFieldNames})
2006             {
2007 0           $fields[$i] =~ s/\b($column_list)\b/
2008 0           my ($column_name) = $1;
2009 0           $columns .= $column_name . ',';
2010 0           "\$\$\_\{$column_name\}"/ieg;
2011             }
2012             else
2013             {
2014 0           $fields[$i] =~ s/\b($column_list)\b/
2015 0           my ($column_name) = $1;
2016 0           $columns .= $column_name . ',';
2017 0           "\$\$\_\{\U$column_name\E\}"/ieg;
2018             }
2019 0           $fields[$i] =~ s/\x02\^2jSpR1tE\x02(\d+)\x02\^2jSpR1tE\x02/$strings[$1]/g; #UNPROTECT LITERALS!
2020             }
2021 0           chop ($columns);
2022              
2023             #PROCESS ANY WHERE AND ORDER-BY CLAUSES.
2024              
2025             #if ($extra =~ s/([\s|\)]+)order\s+by\s*(.*)/$1/i) #20011129
2026 0 0         if ($extra =~ s/([\s|\)]+)order\s+by\s*(.*)/$1/is)
2027             {
2028 0           my $orderclause = $2;
2029 0           @ordercols = split(/,/o, $orderclause);
2030             #$descorder = ($ordercols[$#ordercols] =~ s/(\w+\W+)desc$/$1/i);
2031             #$descorder = ($ordercols[$#ordercols] =~ s/(\w+\W+)desc$/$1/is); #20011129
2032 0           for (my $i=0;$i<=$#ordercols;$i++)
2033             {
2034 0           $descorder = 'asc';
2035 0 0         $descorder = $2 if ($ordercols[$i] =~ s/(\w+)\W+(asc|desc|ascending|descending)$/$1/is); #20020708
2036 0           push (@descorder, $descorder); #20020708
2037             }
2038             #$orderclause =~ s/,\s+/,/g;
2039 0           for $i (0..$#ordercols)
2040             {
2041 0           $ordercols[$i] =~ s/\s//go;
2042 0           $ordercols[$i] =~ s/[\(\)]+//go;
2043             }
2044             }
2045             #if ($extra =~ /^\s+where\s*(.+)$/i) #20011129
2046 0 0         if ($extra =~ /^\s+where\s*(.+)$/iso)
2047             {
2048 0           $condition = $self->parse_expression ($1);
2049             }
2050 0 0         if ($column_stuff =~ /\*/o)
2051             {
2052 0           @fields = @{ $self->{order} };
  0            
2053 0           $columns = join (',', @fields);
2054 0 0         if ($self->{sprite_CaseFieldNames})
2055             {
2056 0           for (my $i=0;$i<=$#fields;$i++)
2057             {
2058 0           $fields[$i] =~ s/([^\,]+)/\$\$\_\{$1\}/g;
2059             }
2060             }
2061             else
2062             {
2063 0           for (my $i=0;$i<=$#fields;$i++)
2064             {
2065             #$fields[$i] =~ s/([^\,]+)/\$\$\_\{\U$1\E\}/g; #CHGD. TO NEXT 20030208 TO FIX WIERD BUG THAT $#?%ED UP NAMES SOMETIMES!
2066 0           $fields[$i] =~ s/([^\,]+)/\$\$\_\{$1\}/g;
2067 0           $fields[$i] =~ tr/a-z/A-Z/;
2068             }
2069             }
2070             }
2071 0 0         $columns =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
2072 0 0         $self->check_columns ($columns) || return (-502);
2073             #$self->{use_fields} = join (',', @{ $self->{order} }[0..$#fields] )
2074 0 0         if ($#fields >= 0)
2075             {
2076 0           my (@fieldnames) = @fields;
2077 0           for (my $i=0;$i<=$#fields;$i++)
2078             {
2079 0           $fieldnames[$i] =~ s/\(.*$//o;
2080 0           $fieldnames[$i] =~ s/\$\_//o;
2081 0           $fieldnames[$i] =~ s/[^\w\,]//go;
2082             }
2083 0           $self->{use_fields} = join(',', @fieldnames);
2084             }
2085 0           $values_or_error = $self->parse_columns ('select', $columns,
2086             $condition, '', \@ordercols, \@descorder, \@fields, $distinct); #JWT
2087 0           return $values_or_error;
2088             }
2089             else #INVALID SELECT STATEMENT!
2090             {
2091 0           $errdetails = $query;
2092 0           return (-503);
2093             }
2094             }
2095              
2096             sub update
2097             {
2098 0     0 1   my ($self, $query) = @_;
2099 0           my ($i, $path, $regex, $table, $extra, $condition, $all_columns,
2100             $columns, $status);
2101 0           my ($psuedocols) = "CURRVAL|NEXTVAL";
2102              
2103             ##++
2104             ## Hack to allow parenthesis to be escaped!
2105             ##--
2106              
2107 0           $query =~ s/\\([()])/sprintf ("%%\0%d: ", ord ($1))/ges;
  0            
2108 0           $path = $self->{path};
2109 0           $regex = $self->{column};
2110              
2111 0 0         if ($query =~ /^update\s+($path)\s+set\s+(.+)$/ios) {
2112 0           ($table, $extra) = ($1, $2);
2113 0 0         return (-523) if ($table =~ /^DUAL$/io);
2114              
2115             #ADDED IF-STMT 20010418 TO CATCH
2116             #PARENTHESIZED SET-CLAUSES (ILLEGAL IN ORACLE & CAUSE WIERD PARSING ERRORS!)
2117 0 0         if ($extra =~ /^\(.+\)\s*where/so)
2118             {
2119 0           $errdetails = 'parenthesis around SET clause?';
2120 0           return (-504);
2121             }
2122             #$thefid = $table;
2123             #$self->check_for_reload ($table) || return (-501); #CHGD. TO NEXT 20020110 TO BETTER CATCH ERRORS.
2124 0   0       my $cfr = $self->check_for_reload($table) || -501;
2125 0 0         return $cfr if ($cfr < 0);
2126              
2127 0 0         return (-511) unless (-w $self->{file}); #ADDED 19991207!
2128              
2129 0           $all_columns = {};
2130 0           $columns = '';
2131              
2132 0           $extra =~ s/\\\\/\x02\^2jSpR1tE\x02/gso; #PROTECT "\\"
2133             #$extra =~ s/\\\'|\'\'/\x02\^3jSpR1tE\x02/gs; #PROTECT '', AND \'. #CHANGED 20000303 TO NEXT 2.
2134             #$extra =~ s/\'\'/\x02\^3jSpR1tE\x02\x02\^3jSpR1tE\x02/gs; #CHGD. TO NEXT 20040121
2135             #$extra =~ s/\'\'/\x02\^3jSpR1tE\x02\x02\^8jSpR1tE\x02/gs; #PROTECT '', AND \'.
2136 0           $extra =~ s/\'\'/\x02\^8jSpR1tE\x02/gso; #PROTECT ''.
2137 0           $extra =~ s/\\\'/\x02\^3jSpR1tE\x02/gso; #PROTECT \'.
2138             #$extra =~ s/\\\"|\"\"/\x02\^4jSpR1tE\x02/gs; #REMOVED 20000303.
2139              
2140             #$extra =~ s/^[\s\(]+(.*)$/$1/; #STRIP OFF SURROUNDING SPACES AND PARINS.
2141             #$extra =~ s/[\s\)]+$/$1/;
2142             #$extra =~ s/^[\s\(]+//; #STRIP OFF SURROUNDING SPACES AND PARINS.
2143             #$extra =~ s/[\s\)]+$//;
2144 0           $extra =~ s/^\s+//so; #STRIP OFF SURROUNDING SPACES.
2145 0           $extra =~ s/\s+$//so;
2146             #NOW TEMPORARILY PROTECT COMMAS WITHIN (), IE. FN(ARG1,ARG2).
2147 0           my $column = $self->{column};
2148 0           $extra =~ s/($column\s*\=\s*)\'(.*?)\'(,|$)/
2149 0           my ($one,$two,$three) = ($1,$2,$3);
2150 0           $two =~ s|\,|\x02\^5jSpR1tE\x02|go;
2151 0           $two =~ s|\(|\x02\^6jSpR1tE\x02|go;
2152 0           $two =~ s|\)|\x02\^7jSpR1tE\x02|go;
2153 0           $one."'".$two."'".$three;
2154             /egs;
2155              
2156 0           1 while ($extra =~ s/\(([^\(\)]*)\)/
2157 0           my ($args) = $1;
2158 0           $args =~ s|\,|\x02\^5jSpR1tE\x02|go;
2159 0           "\x02\^6jSpR1tE\x02$args\x02\^7jSpR1tE\x02";
2160             /egs);
2161             ###$extra =~ s/\'(.*?)\'/my ($j)=$1; #PROTECT COMMAS IN QUOTES.
2162             ### $j=~s|,|\x02\^5jSpR1tE\x02|g;
2163             ### "'$j'"/eg;
2164 0           my @expns = split(',',$extra);
2165 0           for ($i=0;$i<=$#expns;$i++) #PROTECT "WHERE" IN QUOTED VALUES.
2166             {
2167 0           $expns[$i] =~ s/\x02\^5jSpR1tE\x02/,/gso;
2168 0           $expns[$i] =~ s/\x02\^6jSpR1tE\x02/\(/gso;
2169 0           $expns[$i] =~ s/\x02\^7jSpR1tE\x02/\)/gso;
2170 0           $expns[$i] =~ s/\=\s*'([^']*?)where([^']*?)'/\='$1\x02\^5jSpR1tE\x02$2'/gis;
2171 0           $expns[$i] =~ s/\'(.*?)\'/my ($j)=$1;
  0            
2172 0           $j=~s|where|\x02\^5jSpR1tE\x02|go;
2173 0           "'$j'"/egs;
2174             }
2175 0           $extra = $expns[$#expns]; #EXTRACT WHERE-CLAUSE, IF ANY.
2176 0           $extra =~ s/\x02\^8jSpR1tE\x02/\'\'/gso; #ADDED 20040121.
2177 0 0         $condition = ($extra =~ s/(.*)where(.+)$/where$1/is) ? $2 : '';
2178 0           $condition =~ s/\s+//so;
2179             ####$condition =~ s/^\((.*)\)$/$1/g; #REMOVED 20010313 SO "WHERE ((COND) OP (COND) OP (COND)) WOULD WORK FOR DBIX-RECORDSET. (SELECT APPEARS TO WORK WITHOUT THIS).
2180             #$expns[$#expns] =~ s/where(.+)$//i;
2181 0           $expns[$#expns] =~ s/\s*where(.+)$//iso; #20000108 REP. PREV. LINE 2FIX BUG IF LAST COLUMN CONTAINS SINGLE QUOTES.
2182             ##########$expns[$#expns] =~ s/\s*\)\s*$//i; #20010416: ADDED TO FIX BUG WHERE LAST ")" BEFORE "WHERE" NOT STRIPPED!
2183             ##########ABOVE NOT A BUG -- MUST NOT USE PARINS AROUND UPDATE CLAUSE, IE.
2184             ##########"update table set (a = b, c = d) where e = f" is INVALID (IN ORACLE ALSO!!!!!!!!
2185 0           $column = $self->{column};
2186 0           $condition = $self->parse_expression ($condition);
2187 0           $columns = ''; #ADDED 20010228. (THESE CHGS FIXED INCORRECT ORDER BUG FOR "TYPE", "NAME", ETC. LISTS IN UPDATES).
2188 0           for ($i=0;$i<=$#expns;$i++) #EXTRACT FIELD NAMES AND
2189             #VALUES FROM EACH EXPRESSION.
2190             {
2191 0           $expns[$i] =~ s!\s*($column)\s*=\s*(.+)$!
2192 0           my ($var) = $1;
2193 0           my ($val) = $2;
2194              
2195 0 0         $var =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
2196 0           $columns .= $var . ','; #ADDED 20010228.
2197 0           $val =~ s|%\0(\d+): |pack("C",$1)|ge;
  0            
2198 0           $all_columns->{$var} = $val;
2199 0           $all_columns->{$var} =~ s/\x02\^2jSpR1tE\x02/\\\\/g;
2200 0           $all_columns->{$var} =~ s/\x02\^8jSpR1tE\x02/\'\'/g; #ADDED 20040121.
2201 0           $all_columns->{$var} =~ s/\x02\^3jSpR1tE\x02/\'/g; #20000108 REPL. PREV. LINE - NO NEED TO DOUBLE QUOTES (WE ESCAPE THEM) - THIS AIN'T ORACLE.
2202             #$all_columns->{$var} =~ s/\x02\^4jSpR1tE\x02/\"\"/g; #REMOVED 20000303.
2203             !es;
2204             }
2205             #$columns = join (',', keys %$all_columns); #NEXT 2 CHGD TO 3RD LINE 20010228.
2206             #$columns =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames}); #JWT
2207 0           chop($columns); #ADDED 20010228.
2208             #$condition = ($extra =~ /^\s*where\s+(.+)$/is) ? $1 : '';
2209              
2210             #$self->check_for_reload ($table) || return (-501);
2211 0 0         $self->check_columns ($columns) || return (-502);
2212             #### MOVED UP ABOVE FOR-LOOP SO "NEXTVAL" GETS EVALUATED IN RIGHT ORDER!
2213             ####$condition = $self->parse_expression ($condition);
2214 0           $status = $self->parse_columns ('update', $columns,
2215             $condition,
2216             $all_columns);
2217 0           return ($status);
2218             } else {
2219 0           $errdetails = $query;
2220 0           return (-504);
2221             }
2222             }
2223              
2224             sub delete
2225             {
2226 0     0 1   my ($self, $query) = @_;
2227 0           my ($path, $table, $condition, $status, $wherepart);
2228              
2229 0           $path = $self->{path};
2230              
2231 0 0         if ($query =~ /^delete\s+from\s+($path)(?:\s+where\s+(.+))?$/ios) {
2232 0           $table = $1;
2233 0           $wherepart = $2;
2234             #$thefid = $table;
2235             #$self->check_for_reload ($table) || return (-501); #CHGD. TO NEXT 20020110 TO BETTER CATCH ERRORS.
2236 0   0       my $cfr = $self->check_for_reload($table) || -501;
2237 0 0         return $cfr if ($cfr < 0);
2238              
2239 0 0         return (-511) unless (-w $self->{file}); #ADDED 19991207!
2240 0 0         if ($wherepart =~ /\S/o)
2241             {
2242 0           $condition = $self->parse_expression ($wherepart);
2243             }
2244             else
2245             {
2246 0           $condition = 1;
2247             }
2248             #$self->check_for_reload ($table) || return (-501);
2249              
2250 0           $status = $self->delete_rows ($condition);
2251              
2252 0           return $status;
2253             } else {
2254 0           $errdetails = $query;
2255 0           return (-505);
2256             }
2257             }
2258              
2259             sub drop
2260             {
2261 0     0 0   my ($self, $query) = @_;
2262 0           my ($path, $table, $condition, $status, $wherepart);
2263              
2264 0           $path = $self->{path};
2265              
2266 0           $_ = undef;
2267 0 0         if ($query =~ /^drop\s+table\s+($path)\s*$/ios)
2268             {
2269 0           $table = $1;
2270             #$self->check_for_reload ($table) || return (-501); #CHGD. TO NEXT 20020110 TO BETTER CATCH ERRORS.
2271 0   0       my $cfr = $self->check_for_reload($table) || -501;
2272 0 0         return $cfr if ($cfr < 0);
2273              
2274 0           @{$self->{records}} = (); #ADDED 20021025 TO REMOVE DANGLING DATA (CAUSED TESTS TO FAIL AT 9)!
  0            
2275 0           @{$self->{order}} = ();
  0            
2276 0           %{$self->{types}} = ();
  0            
2277 0           %{$self->{lengths}} = ();
  0            
2278 0           %{$self->{scales}} = ();
  0            
2279 0           %{$self->{defaults}} = ();
  0            
2280 0           $self->{key_fields} = '';
2281              
2282             #SOME DAY, I SHOULD ADD CODE TO DELETE DANGLING BLOB FILES!!!!!!!
2283              
2284             # return (unlink $self->{file} || -501); #NEXT 2 CHGD. TO FOLLOWING 20020606.
2285             # return
2286 0 0         return (unlink $self->{file}) ? '0E0' : -501;
2287             }
2288 0           $errdetails = $query;
2289 0           return (-501);
2290             }
2291              
2292             sub truncate
2293             {
2294 0     0 0   my ($self, $query) = @_;
2295 0 0         return $self->delete($query)
2296             if ($query =~ s/^\s*truncate\s+table\s+/delete from /ios);
2297 0           $errdetails = $query;
2298 0           return (-533);
2299             }
2300              
2301             sub primary_key_info
2302             {
2303 0     0 0   my ($self, $query) = @_;
2304 0           my $table = $query;
2305 0           $table =~ s/^.*\s+(\w+)$/$1/;
2306 0   0       my $cfr = $self->check_for_reload($table) || -501;
2307 0 0         return $cfr if ($cfr < 0);
2308 0           undef %{ $self->{types} };
  0            
2309 0           undef %{ $self->{lengths} };
  0            
2310 0           $self->{use_fields} = 'CAT,SCHEMA,TABLE_NAME,PRIMARY_KEY';
2311 0           $self->{order} = [ 'CAT', 'SCHEMA', 'TABLE_NAME', 'PRIMARY_KEY' ];
2312 0           $self->{fields}->{CAT} = 1;
2313 0           $self->{fields}->{SCHEMA} = 1;
2314 0           $self->{fields}->{TABLE_NAME} = 1;
2315 0           $self->{fields}->{PRIMARY_KEY} = 1;
2316 0           undef @{ $self->{records} };
  0            
2317 0           my (@keyfields) = split(',', $self->{key_fields}); #JWT: PREVENT DUP. KEYS.
2318 0           ${$self->{types}}{CAT} = 'VARCHAR2';
  0            
2319 0           ${$self->{types}}{SCHEMA} = 'VARCHAR2';
  0            
2320 0           ${$self->{types}}{TABLE_NAME} = 'VARCHAR2';
  0            
2321 0           ${$self->{types}}{PRIMARY_KEY} = 'VARCHAR2';
  0            
2322 0           ${$self->{lengths}}{CAT} = 50;
  0            
2323 0           ${$self->{lengths}}{SCHEMA} = 50;
  0            
2324 0           ${$self->{lengths}}{TABLE_NAME} = 50;
  0            
2325 0           ${$self->{lengths}}{PRIMARY_KEY} = 50;
  0            
2326 0           ${$self->{defaults}}{CAT} = undef;
  0            
2327 0           ${$self->{defaults}}{SCHEMA} = undef;
  0            
2328 0           ${$self->{defaults}}{TABLE_NAME} = undef;
  0            
2329 0           ${$self->{defaults}}{PRIMARY_KEY} = undef;
  0            
2330 0           ${$self->{scales}}{PRIMARY_KEY} = 50;
  0            
2331 0           ${$self->{scales}}{PRIMARY_KEY} = 50;
  0            
2332 0           ${$self->{scales}}{PRIMARY_KEY} = 50;
  0            
2333 0           ${$self->{scales}}{PRIMARY_KEY} = 50;
  0            
2334 0           my $results;
2335 0           my $keycnt = scalar(@keyfields);
2336 0           while (@keyfields)
2337             {
2338 0           push (@{$results}, [0, 0, $table, shift(@keyfields)]);
  0            
2339             }
2340 0           unshift (@$results, $keycnt);
2341 0           return $results;
2342             }
2343              
2344             sub delete_rows
2345             {
2346 0     0 0   my ($self, $condition) = @_;
2347 0           my ($status, $loop);
2348 0     0     local $SIG{'__WARN__'} = sub { $status = -510; $errdetails = "$_[0] at ".__LINE__ };
  0            
  0            
2349 0           local $^W = 0;
2350              
2351             #$status = 1;
2352 0           $status = 0;
2353              
2354 0           my @blobcols;
2355 0           foreach my $i (keys %{$self->{types}})
  0            
2356             {
2357 0 0         push (@blobcols, $i) if (${$self->{types}}{$i} =~ /$REFTYPES/o)
  0            
2358             }
2359 0           my ($blobfid, $delfid, $rawvalue);
2360            
2361 0           $loop = 0;
2362 0           while (1)
2363             {
2364             #last if ($loop > scalar @{ $self->{records} });
2365             #last if (!scalar(@{$self->{records}}) || $loop > scalar @{ $self->{records} }); #JWT: 19991222
2366 0 0 0       last if (!scalar(@{$self->{records}}) || $loop >= scalar @{ $self->{records} }); #JWT: 20000609 FIX INFINITE LOOP!
  0            
  0            
2367              
2368 0           $_ = $self->{records}->[$loop];
2369            
2370 0 0         if (eval $condition)
2371             {
2372 0           foreach my $i (@blobcols)
2373             {
2374 0           $rawvalue = $self->{records}->[$loop]->{$i};
2375             $blobfid = $self->{directory}
2376             .$self->{separator}->{ $self->{platform} }
2377 0           .$self->{table}."_${rawvalue}.ldt";
2378             $delfid = $self->{directory}
2379             .$self->{separator}->{ $self->{platform} }
2380 0           .$self->{table}."_${rawvalue}_$$.del";
2381 0           rename ($blobfid, $delfid);
2382             }
2383             #$self->{records}->[$loop] = undef;
2384 0           splice(@{ $self->{records} }, $loop, 1);
  0            
2385 0           ++$status; #LET'S COUNT THE # RECORDS DELETED!
2386             }
2387             else
2388             {
2389 0           ++$loop;
2390             }
2391             }
2392              
2393 0 0         $self->{dirty} = 1 if ($status > 0);
2394 0           return $status;
2395             }
2396              
2397             sub create
2398             {
2399 0     0 1   my ($self, $query) = @_;
2400              
2401 0           my ($i, @keyfields, @values);
2402             ### create table table1 (field1 number, field2 varchar(20), field3 number(5,3))
2403 0           local (*FILE, $^W);
2404 0           local ($/) = $self->{_record}; #JWT:SUPPORT ANY RECORD-SEPARATOR!
2405              
2406 0           $^W = 0;
2407 0 0         if ($query =~ /^create\s+table\s+($self->{path})\s*\((.+)\)\s*$/is)
    0          
2408             {
2409 0           my ($table, $extra) = ($1, $2);
2410              
2411 0 0         $query =~ tr/a-z/A-Z/s unless ($self->{sprite_CaseFieldNames}); #ADDED 20000225;
2412             #$extra =~ tr/a-z/A-Z/; #ADDED 20000225;
2413 0           $extra =~ s/^\s*//so;
2414 0           $extra =~ s/\s*$//so;
2415 0           $extra =~ s/\((.*?)\)/
2416 0           my ($precision) = $1;
2417 0           $precision =~ s|\,|\x02\^2jSpR1tE\x02|g; #PROTECT COMMAS IN ().
2418 0           "($precision)"/egs;
2419 0           $extra =~ s/([\'\"])([^\1]*?)\1/
2420 0           my ($quote) = $1;
2421 0           my ($str) = $2;
2422 0           $str =~ s|\,|\x02\^2jSpR1tE\x02|g; #PROTECT COMMAS IN QUOTES.
2423 0           "$quote$str$quote"/egs;
2424              
2425 0           my (@fieldlist) = split(/,/o ,$extra);
2426 0           my $fieldname;
2427 0           for ($i=0;$i<=$#fieldlist;$i++)
2428             {
2429 0           $fieldlist[$i] =~ s/^\s+//gso;
2430 0           $fieldlist[$i] =~ s/\s+$//gso;
2431 0 0         if ($fieldlist[$i] =~ s/^PRIMARY\s+KEY\s*\(([^\)]+)\)$//i)
2432             {
2433 0           my $keyfields = $1;
2434 0           $keyfields =~ s/\s+//go;
2435 0 0         $keyfields =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
2436 0           @keyfields = split(/\x02\^2jSpR1tE\x02/o ,$keyfields);
2437             }
2438             }
2439              
2440             #ALTERED THIS ROUTINE 20021024 TO DO CREATES VIA WRITE_FILE (AS IT SHOULD!)
2441             #SO THAT NEW XML TABLES GET CREATED IN XML!!!!
2442              
2443 0           @{$self->{order}} = ();
  0            
2444 0           %{$self->{types}} = ();
  0            
2445 0           %{$self->{lengths}} = ();
  0            
2446 0           %{$self->{scales}} = ();
  0            
2447 0           %{$self->{defaults}} = ();
  0            
2448 0           while (@fieldlist)
2449             {
2450 0           $i = shift(@fieldlist);
2451             #$i =~ s/^\s*\(\s*//;
2452 0 0         last unless ($i =~ /\S/o);
2453 0           $i =~ s/\s+DEFAULT\s+(?:([\'\"])([^\1]*?)\1|([\+\-]?[\d\.]+)|(NULL))$/
2454 0   0       my ($value) = $4 || $3 || $2 || $1;
2455 0 0         $value = '' if ($4);
2456 0           push (@values, $value);
2457 0           "=<3>"/ieg;
2458 0           $i =~ s/\s+/=/o;
2459 0 0         $i =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
2460 0           $fieldname = $i;
2461 0           $fieldname =~ s/=.*//o;
2462             #NEXT LINE ADDED 20030901 TO ALLOW PRIMARY-KEY ATTRIBUTE ON SAME LINE AS FIELD.
2463 0 0         push (@keyfields, $fieldname) if ($i =~ s/\s*PRIMARY\s+KEY\s*//i);
2464 0           my ($tp,$len,$scale);
2465 0           $i =~ s/\w+\=//o;
2466 0           $i =~ s/\s+//go;
2467 0 0         if ($i =~ /(\w+)(?:\((\d+))?(?:\x02\^2jSpR1tE\x02(\d+))?/o)
2468             {
2469 0           $tp = $1;
2470 0           $len = $2;
2471 0           $scale = $3;
2472             }
2473             else
2474             {
2475 0           $tp = 'VARCHAR2';
2476             }
2477 0 0         unless ($len)
2478             {
2479 0           $len = 40;
2480 0 0         $len = 10 if ($tp =~ /NUM|INT|FLOAT|DOUBLE/o);
2481             #$len = 5000 if ($tp =~ /LONG|BLOB|MEMO/); #CHGD TO NEXT 20020110.
2482 0 0 0       $len = $self->{LongReadLen} || 0 if ($tp =~ /$BLOBTYPES/);
2483             }
2484 0 0         unless ($scale)
2485             {
2486 0           $scale = $len;
2487 0 0         if ($tp eq 'FLOAT')
    0          
2488             {
2489 0           $scale -= 3;
2490             }
2491             elsif ($tp =~ /$NUMERICTYPES/)
2492             {
2493 0           $scale = 0;
2494             }
2495             }
2496 0           my ($value) = '';
2497 0 0         if ($i =~ /\<3\>/)
2498             {
2499 0           $value = shift(@values);
2500 0           my ($rawvalue);
2501             #if ($tp =~ /$NUMERICTYPES/) #CHGD TO NEXT LINE 20010313.
2502 0 0 0       if (length($value) > 0 && $tp =~ /$NUMERICTYPES/)
2503             {
2504 0           $rawvalue = sprintf(('%.'.$scale.'f'),
2505             $value);
2506             }
2507             else
2508             {
2509 0           $rawvalue = $value;
2510             }
2511             #$value = substr($rawvalue,0,$len); #CHGD. TO NEXT 20020110.
2512 0 0         $value = ($tp =~ /$BLOBTYPES/) ? $rawvalue : substr($rawvalue,0,$len);
2513 0 0 0       unless ($self->{LongTruncOk} || $value eq $rawvalue ||
      0        
2514             ($tp eq 'FLOAT'))
2515             {
2516 0           $errdetails = "$fieldname to $len chars";
2517 0           return (-519);
2518             }
2519 0 0 0       if (($tp eq 'FLOAT')
2520             && (int($value) != int($rawvalue)))
2521             {
2522 0           $errdetails = "$fieldname to $len chars";
2523 0           return (-519);
2524             }
2525             # if ($tp eq 'CHAR') #CHGD. TO NEXT 20030812.
2526 0 0 0       if ($tp eq 'CHAR' && length($rawvalue) > 0)
2527             {
2528 0           $rawvalue = sprintf('%-'.$len.'s',$value);
2529             }
2530             else
2531             {
2532 0           $rawvalue = $value;
2533             }
2534             # if ($tp eq 'CHAR') #REDUNDANT CODE REMOVED 20030812
2535             # {
2536             # $value = sprintf('%-'.$len.'s',$rawvalue);
2537             # }
2538             # else
2539             # {
2540             # $value = $rawvalue;
2541             # }
2542             }
2543 0           push (@{$self->{order}}, $fieldname);
  0            
2544 0           ${$self->{types}}{$fieldname} = $tp;
  0            
2545 0           ${$self->{lengths}}{$fieldname} = $len;
  0            
2546 0           ${$self->{scales}}{$fieldname} = $scale;
  0            
2547 0           ${$self->{defaults}}{$fieldname} = $value;
  0            
2548             }
2549 0           $self->{key_fields} = join(',',@keyfields);
2550 0           $self->{dirty} = 1;
2551 0           @{$self->{records}} = (); #ADDED 20021025 TO REMOVE DANGLING DATA (CAUSED TESTS TO FAIL AT 9)!
  0            
2552              
2553 0           $self->commit($table); #ALWAYS AUTOCOMMIT NEW TABLES (ORACLE DOES)!
2554 0   0       my $cfr = $self->check_for_reload($table) || -501;
2555 0 0         return $cfr if ($cfr < 0);
2556             }
2557             elsif ($query =~ /^create\s+sequence\s+($self->{path})(?:\s+inc(?:rement)?\s+by\s+(\d+))?(?:\s+start\s+with\s+(\d+))?/is)
2558             {
2559 0           my ($seqfid, $incval, $startval) = ($1, $2, $3);
2560              
2561 0 0         $incval = 1 unless ($incval);
2562 0 0         $startval = 0 unless ($startval);
2563              
2564 0           my ($new_file) = $self->get_path_info($seqfid) . '.seq';
2565             #### $new_file =~ tr/A-Z/a-z/ unless ($self->{CaseTableNames}); #JWT:TABLE-NAMES ARE NOW CASE-INSENSITIVE!
2566 0 0 0       unlink ($new_file) if ($self->{sprite_forcereplace} && -e $new_file); #ADDED 20010912.
2567 0 0         if (open (FILE, ">$new_file"))
2568             {
2569 0           print FILE "$startval,$incval\n";
2570 0           close (FILE);
2571             }
2572             else
2573             {
2574 0           $errdetails = "$@/$? (file:$new_file)";
2575 0           return -511;
2576             }
2577             }
2578             else #ADDED 20020222 TO CHECK WHETHER TABLE CREATED!
2579             {
2580 0           $errdetails = $query;
2581 0           return -530;
2582             }
2583             }
2584              
2585             sub alter
2586             {
2587 0     0 1   my ($self, $query) = @_;
2588 0           my ($i, $path, $regex, $table, $extra, $type, $column, $count, $status, $fd);
2589 0           my ($posn);
2590 0           my (@keyfields) = split(',', $self->{key_fields}); #JWT: PREVENT DUP. KEYS.
2591              
2592 0           $path = $self->{path};
2593 0           $regex = $self->{column};
2594              
2595 0 0         if ($query =~ /^alter\s+table\s+($path)\s+(.+)$/ios)
2596             {
2597 0           ($table, $extra) = ($1, $2);
2598 0 0         if ($extra =~ /^(add|modify|drop)\s*(.+)$/ios)
2599             {
2600 0           my ($type, $columnstuff) = ($1, $2);
2601 0           $columnstuff =~ s/^\s*\(//s;
2602 0           $columnstuff =~ s/\)\s*$//s;
2603             ###alter table table2 add (newcol1 varchar(5), newcol2 varchar(10))
2604 0           $columnstuff =~ s/\((.*?)\)/
2605 0           my ($precision) = $1;
2606 0           $precision =~ s|\,|\x02\^2jSpR1tE\x02|g; #PROTECT COMMAS IN ().
2607 0           "($precision)"/egs;
2608 0           $columnstuff =~ s/([\'\"])([^\1]*?)\1/
2609 0           my ($quote) = $1;
2610 0           my ($str) = $2;
2611 0           $str =~ s|\,|\x02\^2jSpR1tE\x02|gs; #PROTECT COMMAS IN QUOTES.
2612 0           "$quote$str$quote"/egs;
2613              
2614             #$thefid = $table;
2615             #$self->check_for_reload ($table) || return (-501); #CHGD. TO NEXT 20020110 TO BETTER CATCH ERRORS.
2616 0   0       my $cfr = $self->check_for_reload($table) || -501;
2617 0 0         return $cfr if ($cfr < 0);
2618              
2619 0           my (@values) = ();
2620 0           my (@fieldlist) = split(/,/,$columnstuff);
2621 0           my ($olddf, $oldln, $tp, $x);
2622 0           while (@fieldlist)
2623             {
2624 0           $i = shift(@fieldlist);
2625 0           $i =~ s/^\s+//go;
2626 0           $i =~ s/\s+$//go;
2627 0 0         last unless ($i =~ /\S/o);
2628 0           $i =~ s/\x02\^2jSpR1tE\x02/\,/go;
2629 0           $i =~ s/\s+DEFAULT\s+(?:([\'\"])([^\1]*?)\1|([\+\-]?[\d\.]+)|(NULL))$/
2630 0   0       my ($value) = $4 || $3 || $2 || $1;
2631 0 0         $value = "\x02\^4jSpR1tE\x02" if ($4);
2632 0           push (@values, $value);
2633 0           "=\x02\^3jSpR1tE\x02"/ieg;
2634 0           $posn = undef;
2635 0 0         $posn = $1 if ($i =~ s/^(\d+)\s*//o);
2636 0           $i =~ s/\s+/=/o;
2637 0           $fd = $i;
2638 0           $fd =~ s/=.*//o;
2639 0 0         $fd =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
2640 0           for (my $j=0;$j<=$#keyfields;$j++)
2641             {
2642 0 0         $i =~ s/=/=*/o if ($fd eq $keyfields[$j]);
2643             }
2644 0           $x = undef;
2645 0           $tp = undef;
2646 0           $i =~ /\w+\=[\*]?(\w*)\s*(.*)/o;
2647 0           ($tp, $x) = ($1, $2);
2648 0           $oldln = 0;
2649 0           $tp =~ tr/a-z/A-Z/;
2650 0 0         if ($type =~ /modify/io)
2651             {
2652 0 0         unless ($tp =~ /[a-zA-Z]/)
2653             {
2654 0           $tp = $self->{types}->{$fd};
2655             }
2656 0 0         unless ($tp eq $self->{types}->{$fd})
2657             {
2658 0 0         if ($#{$self->{records}} >= 0)
  0            
2659             {
2660 0           $errdetails = ($#{$self->{records}}+1) . ' records!';
  0            
2661 0           return -521;
2662             }
2663             }
2664 0           $olddf = undef;
2665 0 0         $olddf = $self->{defaults}->{$fd} if (defined $self->{defaults}->{$fd});
2666 0 0         unless ($tp eq $self->{types}->{$fd})
2667             {
2668 0           $self->{lengths}->{$fd} = 0;
2669 0           $self->{scales}->{$fd} = 0;
2670             }
2671 0           $oldln = $self->{lengths}->{$fd};
2672             }
2673 0           $self->{defaults}->{$fd} = undef;
2674 0 0         $self->{lengths}->{$fd} = $1 if ($x =~ s/(\d+)//o);
2675 0 0         unless ($self->{lengths}->{$fd})
2676             {
2677 0           $self->{lengths}->{$fd} = 40;
2678 0 0         $self->{lengths}->{$fd} = 10 if ($tp =~ /NUM|INT|FLOAT|DOUBLE/o);
2679             #$self->{lengths}->{$fd} = 5000 if ($tp =~ /$BLOBTYPES/); #CHGD. 20020110
2680 0 0 0       $self->{lengths}->{$fd} = $self->{LongReadLen} || 0 if ($tp =~ /$BLOBTYPES/);
2681             }
2682 0 0 0       if ($self->{lengths}->{$fd} < $oldln && $tp !~ /$BLOBTYPES/)
2683             {
2684 0           $errdetails = $fd;
2685 0           return -522;
2686             }
2687 0           $x =~ s/\x02\^3jSpR1tE\x02/
2688 0           $self->{defaults}->{$fd} = shift(@values);
2689             #$self->{defaults}->{$fd} =~ s|\x02\^2jSpR1tE\x02|\,|g;
2690 0           $self->{defaults}->{$fd}/eg;
2691 0           $self->{fields}->{$fd} = 1;
2692 0 0 0       if ($self->{types}->{$fd} =~ /$REFTYPES/o || $tp =~ /$REFTYPES/o)
2693             {
2694 0           $errdetails = "$fd: ".$self->{types}->{$fd}." <=> $tp";
2695 0           return -529;
2696             }
2697 0           $self->{types}->{$fd} = $tp;
2698             $self->{defaults}->{$fd} = $olddf
2699 0 0 0       if ((defined $olddf) && !(defined $self->{defaults}->{$fd}));
2700 0 0         $self->{defaults}->{$fd} = undef if ($self->{defaults}->{$fd} eq "\x02\^4jSpR1tE\x02");
2701 0 0         if ($x =~ s/\,\s*(\d+)//o)
    0          
2702             {
2703 0           $self->{scales}->{$fd} = $1;
2704             }
2705             elsif ($self->{types}->{$fd} eq 'FLOAT')
2706             {
2707 0           $self->{scales}->{$fd} = $self->{lengths}->{$fd} - 3;
2708             }
2709 0 0         if (defined $self->{defaults}->{$fd})
2710             {
2711 0           my ($val);
2712             #if (${$self->{types}}{$fd} =~ /$NUMERICTYPES/) #CHGD TO NEXT LINE 20010313.
2713 0 0 0       if (length($self->{defaults}->{$fd}) > 0 && ${$self->{types}}{$fd} =~ /$NUMERICTYPES/)
  0            
2714             {
2715 0           $val = sprintf(('%.'.${$self->{scales}}{$fd}.'f'),
2716 0           $self->{defaults}->{$fd});
2717             }
2718             else
2719             {
2720 0           $val = $self->{defaults}->{$fd};
2721             }
2722             #$self->{defaults}->{$fd} = substr($val,0, #CHGD. TO NEXT 2 20020110
2723             # ${$self->{lengths}}{$fd});
2724 0 0         $self->{defaults}->{$fd} = (${$self->{types}}{$fd} =~ /$BLOBTYPES/) ? $val : substr($val,0,${$self->{lengths}}{$fd});
  0            
  0            
2725 0 0 0       unless ($self->{LongTruncOk} || ${$self->{types}}{$fd} =~ /$BLOBTYPES/
  0   0        
      0        
2726             || $self->{defaults}->{$fd} eq $val
2727 0           || ${$self->{types}}{$fd} eq 'FLOAT')
2728             {
2729 0           $errdetails = "$fd to ${$self->{lengths}}{$fd} chars";
  0            
2730 0           return (-519);
2731             }
2732 0 0 0       if (${$self->{types}}{$fd} eq 'FLOAT' &&
  0            
2733             int($self->{defaults}->{$fd}) != int($val))
2734             {
2735 0           $errdetails = "$fd to ${$self->{lengths}}{$fd} chars";
  0            
2736 0           return (-519);
2737             }
2738             #if (${$self->{types}}{$fd} eq 'CHAR') #CHGD TO NEXT 20030812.
2739 0 0 0       if (${$self->{types}}{$fd} eq 'CHAR' && length($self->{defaults}->{$fd}) > 0)
  0            
2740             {
2741 0           $val = sprintf('%-'.${$self->{lengths}}{$fd}.'s',
2742 0           $self->{defaults}->{$fd});
2743 0           $self->{defaults}->{$fd} = $val;
2744             }
2745              
2746             #THIS CODE SETS ALL EMPTY VALUES FOR THIS FIELD TO THE
2747             #DEFAULT VALUE. ORACLE DOES NOT DO THIS!
2748             #for ($j=0;$j < scalar @{ $self->{records} }; $j++)
2749             #{
2750             # $self->{records}->[$j]->{$fd} = $self->{defaults}->{$fd}
2751             # unless (length($self->{records}->[$j]->{$fd}));
2752             #}
2753             }
2754 0 0         if ($type =~ /add/io)
    0          
    0          
2755             {
2756 0 0         if (defined $posn)
2757             {
2758 0           my (@myorder) = (@{ $self->{order} }[0..($posn-1)],
2759             $fd,
2760 0           @{ $self->{order} }[$posn..$#{ $self->{order} }]);
  0            
  0            
2761 0           @{ $self->{order} } = @myorder;
  0            
2762             }
2763             else
2764             {
2765 0           push (@{ $self->{order} }, $fd);
  0            
2766             }
2767             }
2768             elsif ($type =~ /modify/io)
2769             {
2770 0 0         if (defined $posn)
2771             {
2772 0           for (my $j=0;$j<=$#{ $self->{order} };$j++)
  0            
2773             {
2774 0 0         if (${ $self->{order} }[$j] eq $fd)
  0            
2775             {
2776 0           splice (@{ $self->{order} }, $j, 1);
  0            
2777 0           my (@myorder) = (@{ $self->{order} }[0..($posn-1)],
2778             $fd,
2779 0           @{ $self->{order} }[$posn..$#{ $self->{order} }]);
  0            
  0            
2780 0           @{ $self->{order} } = @myorder;
  0            
2781 0           last;
2782             }
2783             }
2784             }
2785             }
2786             elsif ($type =~ /drop/io)
2787             {
2788 0 0         $self->check_columns ($fd) || return (-502);
2789 0           $count = -1;
2790 0           foreach (@{ $self->{order} })
  0            
2791             {
2792 0           ++$count;
2793 0 0         last if ($_ eq $fd);
2794             }
2795 0           splice (@{ $self->{order} }, $count, 1);
  0            
2796 0           delete $self->{fields}->{$fd};
2797 0           delete $self->{types}->{$fd};
2798 0           delete $self->{lengths}->{$fd};
2799 0           delete $self->{scales}->{$fd};
2800             }
2801             }
2802              
2803 0           $status = $self->parse_columns ("\L$type\E", $column);
2804 0           $self->{dirty} = 1;
2805 0           $self->commit($table); #ALWAYS AUTOCOMMIT TABLE ALTERATIONS!
2806 0           return $status;
2807             }
2808             else
2809             {
2810 0           $errdetails = $extra;
2811 0           return (-506);
2812             }
2813             }
2814             else
2815             {
2816 0           $errdetails = $query;
2817 0           return (-507);
2818             }
2819             }
2820              
2821             sub insert
2822             {
2823 0     0 1   my ($self, $query) = @_;
2824 0           my ($i, $path, $table, $columns, $values, $status);
2825 0           $path = $self->{path};
2826 0 0         if ($query =~ /^insert\s+into\s+ # Keyword
2827             ($path)\s* # Table
2828             (?:\((.+?)\)\s*)? # Keys
2829             values\s* # 'values'
2830             \((.+)\)$/ixos)
2831             { #JWT: MAKE COLUMN LIST OPTIONAL!
2832              
2833 0           ($table, $columns, $values) = ($1, $2, $3);
2834 0 0         return (-523) if ($table =~ /^DUAL$/io);
2835             #$thefid = $table;
2836             #$self->check_for_reload ($table) || return (-501); #CHGD. TO NEXT 20020110 TO BETTER CATCH ERRORS.
2837 0   0       my $cfr = $self->check_for_reload($table) || -501;
2838 0 0         return $cfr if ($cfr < 0);
2839              
2840 0   0       $columns ||= '';
2841 0           $columns =~ s/\s//gso;
2842 0 0         $columns = join(',', @{ $self->{order} }) unless ($columns =~ /\S/o); #JWT
  0            
2843             #$self->check_for_reload ($table) || return (-501);
2844 0 0         return (-511) unless (-w $self->{file});
2845 0 0         unless ($columns =~ /\S/o)
2846             {
2847             #$thefid = $self->{file};
2848 0           $columns = &load_columninfo($self, ',');
2849 0 0         return $columns if ($columns =~ /^\-?\d+$/o);
2850             }
2851              
2852 0           $values =~ s/\\\\/\x02\^2jSpR1tE\x02/gso; #PROTECT "\\" #XCHGD. TO 4 LINES DOWN 20020111
2853             #$values =~ s/\\\'/\x02\^3jSpR1tE\x02/gs; #CHGD. TO NEXT 20060720.
2854 0           $values =~ s/\\\'/\x02\^3jSpR1tE\x02/gso; #PROTECT ESCAPED QUOTES.
2855 0           $values =~ s/\\\"/\x02\^5jSpR1tE\x02/gso;
2856              
2857 0           1 while ($values =~ s/\(([^\)]*?)\)/
2858 0           my ($j)=$1;
2859 0           $j=~s|\,|\x02\^4jSpR1tE\x02|gso; #PROTECT "," IN PARENTHESIS (FUNCTION-CALL ARG-LISTS).
2860 0           "($j\x02\^6jSpR1tE\x02"
2861             /egs);
2862             # $values =~ s/\'([^\']*?)\'/ #CHGD. TO NEXT 20060720.
2863 0           $values =~ s/([\'\"])([^\1]*?)\1/
2864 0           my ($j)=$2;
2865 0           $j=~s|\,|\x02\^4jSpR1tE\x02|gso; #PROTECT "," IN QUOTES.
2866 0           "'$j'"
2867             /egs;
2868 0           $values =~ s/\x02\^6jSpR1tE\x02/\)/gso;
2869              
2870 0           my $x;
2871 0           my @values = split(/\,\s*/o ,$values);
2872 0           $values = '';
2873 0           for $i (0..$#values)
2874             {
2875 0           $values[$i] =~ s/^\s+//so; #STRIP LEADING & TRAILING SPACES.
2876 0           $values[$i] =~ s/\s+$//so;
2877 0           $values[$i] =~ s/\x02\^5jSpR1tE\x02/\\\"/gso; #RESTORE PROTECTED SINGLE QUOTES HERE.
2878 0           $values[$i] =~ s/\x02\^3jSpR1tE\x02/\\\'/gso; #RESTORE PROTECTED SINGLE QUOTES HERE.
2879 0           $values[$i] =~ s/\x02\^2jSpR1tE\x02/\\\\/gso; #RESTORE PROTECTED SLATS HERE.
2880 0           $values[$i] =~ s/\x02\^4jSpR1tE\x02/\,/gos; #RESTORE PROTECTED COMMAS HERE.
2881 0 0         if ($values[$i] =~ /^[_a-zA-Z]/so)
2882             {
2883 0 0 0       if ($values[$i] =~ /\s*(\w+).NEXTVAL\s*$/o
2884             || $values[$i] =~ /\s*(\w+).CURRVAL\s*$/o)
2885             {
2886 0           my ($seq_file) = $self->get_path_info($1) . '.seq';
2887             #### REMOVED 20010814 - ALREAD DONE IN GET_PATH_INFO!!!! ####$seq_file =~ tr/A-Z/a-z/ unless ($self->{CaseTableNames}); #JWT:TABLE-NAMES ARE NOW CASE-INSENSITIVE!
2888             #open (FILE, "<$seq_file") || return (-511);
2889 0 0         unless (open (FILE, "<$seq_file"))
2890             {
2891 0           $errdetails = "$@/$? (file:$seq_file)";
2892 0           return (-511);
2893             }
2894 0           $x = ;
2895             #chomp($x);
2896 0           $x =~ s/\s+$//; #20000113 CHOMP WON'T WORK HERE IF RECORD DELIMITER SET TO OTHER THAN \n!
2897 0           my ($incval, $startval) = split(/,/o ,$x);
2898 0           close (FILE);
2899 0           $_ = $values[$i];
2900 0 0         if (/\s*(\w+).NEXTVAL\s*$/o)
2901             {
2902             #open (FILE, ">$seq_file") || return (-511);
2903 0 0 0       unlink ($seq_file) if ($self->{sprite_forcereplace} && -e $seq_file); #ADDED 20010912.
2904 0 0         unless (open (FILE, ">$seq_file"))
2905             {
2906 0           $errdetails = "$@/$? (file:$seq_file)";
2907 0           return (-511);
2908             }
2909 0   0       $incval += ($startval || 1);
2910 0           print FILE "$incval,$startval\n";
2911 0           close (FILE);
2912             }
2913 0           $values[$i] = $incval;
2914 0           $self->{sprite_lastsequence} = $incval; #ADDED 20020905 TO SUPPORT DBIx::GeneratedKey!
2915             }
2916             else
2917             {
2918             #eval {$values[$i] = &{$values[$i]} };
2919 0           $values[$i] = eval &chkcolumnparms($self, $values[$i]); #FUNCTION EVAL 2
2920 0 0         return (-517) if ($@);
2921             }
2922             }
2923             };
2924 0           chop($values);
2925 0 0         $self->check_columns ($columns) || return (-502);
2926              
2927 0           $status = $self->insert_data ($columns, @values);
2928            
2929 0           return $status;
2930             } else {
2931 0           $errdetails = $query;
2932 0           return (-508);
2933             }
2934             }
2935              
2936             sub insert_data
2937             {
2938 0     0 0   my ($self, $column_string, @values) = @_;
2939 0           my (@columns, $hash, $loop, $column, $j, $k, $autoColumnIncluded);
2940 0 0         $column_string =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
2941 0           @columns = split (/,/, $column_string);
2942 0           foreach my $i (@{ $self->{order} }) #NEXT LOOP ADDED 20040913 TO SUPPORT AUTONUMBERING W/O SPECIFYING COLUMN ON INSERT (LIKE MYSQL)?
  0            
2943             {
2944 0 0         if (${$self->{types}}{$i} =~ /AUTO/io)
  0            
2945             {
2946 0           $autoColumnIncluded = 0;
2947 0           foreach my $j (@columns)
2948             {
2949 0 0         if ($j eq $i)
2950             {
2951 0           $autoColumnIncluded = 1;
2952 0           last;
2953             }
2954             }
2955 0 0         unless ($autoColumnIncluded)
2956             {
2957 0           push (@columns, $i);
2958 0           push (@values, '');
2959             }
2960             }
2961             }
2962 0           $column_string = join(',', @columns);
2963             #JWT: @values = $self->quotewords (',', 0, $value_string);
2964             # if ($#columns > $#values) #ADDED 20011029 TO DO AUTOSEQUENCING!
2965             # {
2966             # $column_string .= ',' unless ($column_string =~ /\,\s*$/);
2967             # for (my $i=0;$i<=$#columns;$i++)
2968             # {
2969             # if (${$self->{types}}{$columns[$i]} =~ /AUTO/)
2970             # {
2971             # $column_string =~ s/$columns[$i]\,//;
2972             # $column_string .= $columns[$i] . ',';
2973             # push (@values, "''");
2974             # }
2975             # }
2976             # $column_string =~ s/\,\s*$//;
2977             # @columns = split (/\,/, $column_string);
2978             # }
2979 0 0         if ($#columns == $#values) {
2980            
2981 0           my (@keyfields) = split(',', $self->{key_fields}); #JWT: PREVENT DUP. KEYS.
2982 0           my ($matchcnt) = 0;
2983            
2984 0           $hash = {};
2985              
2986 0           foreach $column (@{ $self->{order} })
  0            
2987             {
2988 0 0         $column =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames}); #JWT
2989             $hash->{$column} = $self->{defaults}->{$column}
2990 0 0 0       if (defined($self->{defaults}->{$column}) && length($self->{defaults}->{$column}));
2991             }
2992              
2993 0           for ($loop=0; $loop <= $#columns; $loop++)
2994             {
2995 0           $column = $columns[$loop];
2996 0 0         $column =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
2997            
2998 0           my ($v);
2999 0 0         if ($self->{fields}->{$column})
3000             {
3001 0           $values[$loop] =~ s/^\'(.*)\'$/my ($stuff) = $1;
  0            
3002             #$stuff =~ s|\'|\\\'|gs;
3003 0           $stuff =~ s|\'\'|\'|gso;
3004 0           $stuff/es;
3005 0           $values[$loop] =~ s|^\'$||so; #HANDLE NULL VALUES!!!.
3006 0 0 0       if (${$self->{types}}{$column} =~ /AUTO/o) #NEXT 12 ADDED 20011029 TO DO ODBC&MYSQL-LIKE AUTOSEQUENCING.
  0 0          
3007             {
3008 0 0         if (length($values[$loop]))
3009             {
3010 0           $errdetails = "value($values[$loop]) into column($column)";
3011 0           return (-524);
3012             }
3013             else
3014             {
3015 0           $v = ++$self->{defaults}->{$column};
3016 0           $self->{sprite_lastsequence} = $v; #ADDED 20020905 TO SUPPORT DBIx::GeneratedKey!
3017             }
3018             }
3019             elsif (length($values[$loop]) || !length($self->{defaults}->{$column}))
3020             {
3021 0           $v = $values[$loop];
3022             }
3023             else
3024             {
3025 0           $v = $self->{defaults}->{$column};
3026             }
3027             #if (${$self->{types}}{$column} =~ /$NUMERICTYPES/) #CHGD TO NEXT LINE 20010313.
3028 0 0 0       if (length($v) > 0 && ${$self->{types}}{$column} =~ /$NUMERICTYPES/)
  0 0          
3029             {
3030 0           $hash->{$column} = sprintf(('%.'.${$self->{scales}}{$column}.'f'), $v);
  0            
3031             }
3032 0           elsif (${$self->{types}}{$column} =~ /$REFTYPES/o) #ADDED 20020124 TO SUPPORT REFERENCED TYPES.
3033             {
3034 0           my $randblobid = int(rand(99999));
3035 0           my $randblobfid;
3036 0           do {
3037 0           $randblobid = int(rand(99999));
3038             $randblobfid = $self->{directory}
3039             .$self->{separator}->{ $self->{platform} }
3040 0           .$self->{table}."_${randblobid}_$$.tmp";
3041             } while (-e $randblobfid);
3042 0 0         if (open(FILE, ">$randblobfid"))
3043             {
3044 0           binmode FILE;
3045 0 0 0       if ($self->{CBC} && $self->{sprite_Crypt} <= 2) #ADDED: 20020109
3046             {
3047 0           print FILE $self->{CBC}->encrypt($v);
3048             }
3049             else
3050             {
3051 0           print FILE $v;
3052             }
3053 0           close FILE;
3054 0           $hash->{$column} = $randblobid;
3055             }
3056             else
3057             {
3058 0           $errdetails = "$randblobfid: ($?)";
3059 0           return (-528);
3060             }
3061             }
3062             else
3063             {
3064 0           $hash->{$column} = $v;
3065             }
3066             #$v = substr($hash->{$column},0,${$self->{lengths}}{$column}); #CHGD TO NEXT (20020110)
3067 0 0         $v = (${$self->{types}}{$column} =~ /$BLOBTYPES/) ? $hash->{$column} : substr($hash->{$column},0,${$self->{lengths}}{$column});
  0            
  0            
3068 0 0 0       unless ($self->{LongTruncOk} || $v eq $hash->{$column} ||
      0        
3069 0           (${$self->{types}}{$column} eq 'FLOAT'))
3070             {
3071 0           $errdetails = "$column to ${$self->{lengths}}{$column} chars";
  0            
3072 0           return (-519);
3073             }
3074 0 0 0       if ((${$self->{types}}{$column} eq 'FLOAT')
  0 0 0        
3075             && (int($v) != int($hash->{$column})))
3076             {
3077 0           $errdetails = "$column to ${$self->{lengths}}{$column} chars";
  0            
3078 0           return (-519);
3079             }
3080             #elsif (${$self->{types}}{$column} eq 'CHAR') #CHGD. TO NEXT 20030812.
3081 0           elsif (${$self->{types}}{$column} eq 'CHAR' && length($v) > 0)
3082             {
3083 0           $hash->{$column} = sprintf('%-'.${$self->{lengths}}{$column}.'s',$v);
  0            
3084             }
3085             else
3086             {
3087 0           $hash->{$column} = $v;
3088             }
3089             }
3090             }
3091              
3092             #20000201 - FIX UNIQUE-KEY TEST FOR LARGE DATASETS.
3093              
3094 0           recloop: for ($k=0;$k < scalar @{ $self->{records} }; $k++) #CHECK EACH RECORD.
  0            
3095             {
3096 0           $matchcnt = 0;
3097 0           valueloop: foreach $column (keys %$hash) #CHECK EACH NEW VALUE AGAINST IT'S RESPECTIVE COLUMN.
3098             {
3099 0           keyloop: for ($j=0;$j<=$#keyfields;$j++)
3100             {
3101 0 0         if ($column eq $keyfields[$j])
3102             {
3103 0 0         if ($self->{records}->[$k]->{$column} eq $hash->{$column})
3104             {
3105 0           ++$matchcnt;
3106 0 0 0       return (-518) if ($matchcnt && $matchcnt > $#keyfields); #ALL KEY FIELDS WERE DUPLICATES!
3107             }
3108             }
3109             }
3110             }
3111             #return (-518) if ($matchcnt && $matchcnt > $#keyfields); #ALL KEY FIELDS WERE DUPLICATES!
3112             }
3113              
3114              
3115 0           push @{ $self->{records} }, $hash;
  0            
3116            
3117 0           $self->{dirty} = 1;
3118 0           return (1);
3119             } else {
3120 0           $errdetails = "$#columns != $#values"; #20000114
3121 0           return (-509);
3122             }
3123             }
3124              
3125             sub write_file
3126             {
3127 0     0 0   my ($self, $new_file) = @_;
3128 0           my ($i, $j, $status, $loop, $record, $column, $value, $fields, $record_string);
3129 0           my (@keyfields) = split(',', $self->{key_fields}); #JWT: PREVENT DUP. KEYS.
3130             return ($self->display_error (-531) * -531)
3131 0 0 0       if (($self->{_write} =~ /^xml/io) && $self->{CBC} && $self->{sprite_Crypt} <= 2);
      0        
3132              
3133 0           local (*FILE, $^W);
3134 0           local ($/);
3135 0 0 0       if ($self->{CBC} && $self->{sprite_Crypt} <= 2) #ADDED: 20020109
    0          
3136             {
3137 0           $/ = "\x03^0jSp".$self->{_record}; #(EOR) JWT:SUPPORT ANY RECORD-SEPARATOR!
3138             }
3139             elsif ($self->{_write} !~ /^xml/io)
3140             {
3141 0           $/ = $self->{_record}; #JWT:SUPPORT ANY RECORD-SEPARATOR!
3142             }
3143              
3144 0           $^W = 0;
3145              
3146             #$status = (scalar @{ $self->{records} }) ? 1 : -513;
3147 0           $status = 1; #JWT 19991222
3148              
3149 0 0         return 1 if $#{$self->{order}} < 0; #ADDED 20000225 PREVENT BLANKING OUT TABLES, IE IF USER CREATES SEQUENCE W/SAME NAME AS TABLE, THEN COMMITS!
  0            
3150            
3151             #########$new_file =~ tr/A-Z/a-z/ unless ($self->{CaseTableNames}); #JWT:TABLE-NAMES ARE NOW CASE-INSENSITIVE!
3152 0 0 0       unlink ($new_file) if ($status >= 1 && $self->{sprite_forcereplace} && -e $new_file); #ADDED 20010912.
      0        
3153 0 0 0       if ( ($status >= 1) && (open (FILE, ">$new_file")) ) {
3154 0           binmode FILE; #20000404
3155              
3156             #if (($^O eq 'MSWin32') or ($^O =~ /cygwin/i)) #CHGD. TO NEXT 20020221
3157 0 0         if ($self->{platform} eq 'PC')
3158             {
3159 0 0         $self->lock || $self->display_error (-515);
3160             }
3161             else #GOOD, MUST BE A NON-M$ SYSTEM :-)
3162             {
3163 0 0         eval { flock (FILE, $JSprite::LOCK_EX) || die };
  0            
3164              
3165 0 0         if ($@)
3166             {
3167 0 0 0       $self->lock || $self->display_error (-515) if ($@);
3168             }
3169             }
3170              
3171 0           $fields = '';
3172              
3173 0           my $reccnt = scalar @{ $self->{records} };
  0            
3174 0 0         if ($self->{_write} =~ /^xml/io)
3175             {
3176 0           require MIME::Base64;
3177 0           $fields = <
3178            
3179             END_XML
3180 0 0         $fields .= <{sprite_xsl});
3181             {sprite_xsl}"?>
3182             END_XML
3183 0           $fields .= <
3184            
3185            
3186             END_XML
3187 0           $fields .= ' '."\n";
  0            
3188 0           my ($iskey, $haveadefault, $havemaxsize, $typeinfo);
3189 0           for $i (0..$#{$self->{order}})
  0            
3190             {
3191 0           $iskey = 'NO';
3192 0           for ($j=0;$j<=$#keyfields;$j++) #JWT: MARK KEY FIELDS.
3193             {
3194 0 0         if (${$self->{order}}[$i] eq $keyfields[$j])
  0            
3195             {
3196 0           $iskey = 'PRIMARY';
3197 0           last;
3198             }
3199             }
3200 0           $haveadefault = ${$self->{defaults}}{${$self->{order}}[$i]};
  0            
  0            
3201 0           $havemaxsize = (${$self->{types}}{${$self->{order}}[$i]} =~ /$BLOBTYPES/)
  0            
3202             ? ($self->{LongReadLen} || '0')
3203             : ($self->{maxsizes}->{${$self->{types}}{${$self->{order}}[$i]}}
3204 0 0 0       || ${$self->{lengths}}{${$self->{order}}[$i]} || '0');
      0        
3205 0           $fields .= <
3206            
3207 0           ${$self->{order}}[$i]
3208 0           ${$self->{types}}{${$self->{order}}[$i]}
  0            
3209             $havemaxsize
3210 0           ${$self->{lengths}}{${$self->{order}}[$i]}
  0            
3211 0           ${$self->{scales}}{${$self->{order}}[$i]}
  0            
3212             NULL
3213             $iskey
3214             $haveadefault
3215            
3216             END_XML
3217             }
3218 0           $fields .= " \n";
3219             }
3220             else
3221             {
3222 0           for $i (0..$#{$self->{order}})
  0            
3223             {
3224 0           $fields .= ${$self->{order}}[$i] . '=';
  0            
3225 0           for ($j=0;$j<=$#keyfields;$j++) #JWT: MARK KEY FIELDS.
3226             {
3227 0 0         $fields .= '*' if (${$self->{order}}[$i] eq $keyfields[$j])
  0            
3228             }
3229             #$fields .= ${$self->{types}}{${$self->{order}}[$i]} . '(' #CHGD. TO NEXT 20020110
3230             # . ${$self->{lengths}}{${$self->{order}}[$i]};
3231 0           $fields .= ${$self->{types}}{${$self->{order}}[$i]};
  0            
  0            
3232 0 0         unless (${$self->{types}}{${$self->{order}}[$i]} =~ /$BLOBTYPES/)
  0            
  0            
3233             {
3234 0           $fields .= '(' . ${$self->{lengths}}{${$self->{order}}[$i]};
  0            
  0            
3235 0 0 0       if (${$self->{scales}}{${$self->{order}}[$i]}
  0            
  0            
3236 0           && ${$self->{types}}{${$self->{order}}[$i]} =~ /$NUMERICTYPES/)
  0            
3237             {
3238 0           $fields .= ',' . ${$self->{scales}}{${$self->{order}}[$i]}
  0            
  0            
3239             }
3240             #$fields .= ')' . $self->{_write};
3241 0           $fields .= ')';
3242             }
3243 0           $fields .= '='. ${$self->{defaults}}{${$self->{order}}[$i]}
  0            
3244 0 0         if (length(${$self->{defaults}}{${$self->{order}}[$i]}));
  0            
  0            
3245 0           $fields .= $self->{_write};
3246             }
3247 0           $fields =~ s/$self->{_write}$//;
3248             }
3249              
3250 0 0 0       if ($self->{CBC} && $self->{sprite_Crypt} <= 2) #ADDED: 20020109
3251             {
3252 0           print FILE $self->{CBC}->encrypt($fields).$/;
3253             }
3254             else
3255             {
3256 0           print FILE "$fields$/";
3257             }
3258 0 0         my $rsinit = ($self->{_write} =~ /^xml/io) ? " \n" : '';
3259 0 0         my $rsend = $rsinit ? " \n" : '';
3260              
3261 0           for ($loop=0; $loop < $reccnt; $loop++) {
3262             #++$loop1;
3263 0           $record = $self->{records}->[$loop];
3264              
3265 0 0         next unless (defined $record);
3266              
3267 0           $record_string = $rsinit;
3268             #$record_string =~ s/\?/$loop1/;
3269              
3270 0           foreach $column (@{ $self->{order} })
  0            
3271             {
3272             #if (${$self->{types}}{$column} eq 'CHAR') #CHGD. TO NEXT 20030812.
3273 0 0 0       if (${$self->{types}}{$column} eq 'CHAR' && length($record->{$column}) > 0)
  0            
3274             {
3275             $value = sprintf(
3276 0           '%-'.${$self->{lengths}}{$column}.'s',
3277 0           $record->{$column});
3278             }
3279             #elsif (${$self->{types}}{$column} =~ /$NUMERICTYPES/)
3280             #{
3281             # $value = sprintf(('%.'.${$self->{scales}}{$column}.'f'),
3282             # $record->{$column});
3283             #}
3284             else
3285             {
3286 0           $value = $record->{$column};
3287             }
3288              
3289             #NEXT 2 ADDED 20020111 TO PERMIT EMBEDDED RECORD & FIELD SEPERATORS.
3290 0           $value =~ s/$self->{_record}/\x02\^0jSpR1tE\x02/gso; #PROTECT EMBEDDED RECORD SEPARATORS.
3291 0           $value =~ s/$self->{_write}/\x02\^1jSpR1tE\x02/gso; #PROTECT EMBEDDED RECORD SEPARATORS.
3292 0 0         $record_string .= $rsinit ? (&xmlescape($column,$value)."\n")
3293             : "$self->{_write}$value";
3294             }
3295              
3296             #$record_string =~ s/^$self->{_write}//o; #CHGD TO NEXT LINE 20010917.
3297 0           $record_string =~ s/^$self->{_write}//s;
3298 0           $record_string .= $rsend;
3299              
3300 0 0 0       if ($self->{CBC} && $self->{sprite_Crypt} <= 2) #ADDED: 20020109
3301             {
3302 0           print FILE $self->{CBC}->encrypt($record_string).$/;
3303             }
3304             else
3305             {
3306 0           print FILE "$record_string$/";
3307             }
3308             }
3309 0 0         if ($rsend)
3310             {
3311 0           $rsend = " \n\n";
3312 0 0 0       if ($self->{CBC} && $self->{sprite_Crypt} <= 2) #ADDED: 20020109
3313             {
3314 0           print FILE $self->{CBC}->encrypt($rsend).$/;
3315             }
3316             else
3317             {
3318 0           print FILE "$rsend$/";
3319             }
3320             }
3321 0           close (FILE);
3322              
3323 0           my (@stats) = stat ($new_file);
3324 0           $self->{timestamp} = $stats[9];
3325              
3326 0 0         $self->unlock || $self->display_error (-516);
3327             } else {
3328 0 0         $status = ($status < 1) ? $status : -511;
3329             }
3330 0           return $status;
3331             }
3332              
3333             {
3334             my %xmleschash = (
3335             '<' => '<',
3336             '>' => '>',
3337             '"' => '"',
3338             '--' => '--',
3339             );
3340             sub xmlescape
3341             {
3342 0     0 0   my $res;
3343              
3344 0           $_[1] =~ s/\&/\&/gs;
3345 0           eval "\$_[1] =~ s/(".join('|', keys(%xmleschash)).")/\$xmleschash{\$1}/gs;";
3346             #$_[1] =~ s/([\x01-\x1b\x7f-\xff])/"\&\#".ord($1).';'/egs;
3347 0 0         if ($_[1] =~ /[\x00-\x08\x0A-\x0C\x0E-\x19\x7f-\xff]/o)
3348             {
3349 0           return " <$_[0] xml:encoding=\"base64\">"
3350             . MIME::Base64::encode_base64($_[1]) . "";
3351             }
3352             else
3353             {
3354 0           return " <$_[0]>$_[1]";
3355             }
3356             }
3357             }
3358              
3359             sub load_database
3360             {
3361 0     0 0   my ($self, $file) = @_;
3362              
3363             return -531
3364 0 0 0       if (($self->{_read} =~ /^xml/io) && $self->{CBC} && $self->{sprite_Crypt} <= 2);
      0        
3365              
3366 0           my ($i, $header, @fields, $no_fields, @record, $hash, $loop, $tp, $dflt);
3367 0           local (*FILE);
3368 0           local ($/);
3369 0 0 0       if ($self->{CBC} && $self->{sprite_Crypt} != 2) #ADDED: 20020109
3370             {
3371 0           $/ = "\x03^0jSp".$self->{_record}; #JWT:SUPPORT ANY RECORD-SEPARATOR!
3372             }
3373             else
3374             {
3375 0           $/ = $self->{_record}; #JWT:SUPPORT ANY RECORD-SEPARATOR!
3376             }
3377              
3378             ########$file =~ tr/A-Z/a-z/ unless ($self->{CaseTableNames}); #JWT:TABLE-NAMES ARE NOW CASE-INSENSITIVE!
3379             #$thefid = $file;
3380             # open (FILE, $file) || return (-501);
3381             # binmode FILE; #20000404
3382              
3383 0 0         undef @{ $self->{records} } if (scalar @{ $self->{records} });
  0            
  0            
3384 0           $self->{use_fields} = '';
3385 0           $self->{key_fields} = ''; #20000223 - FIX LOSS OF KEY ASTERISK ON ROLLBACK!
3386 0 0         if ($self->{_read} =~ /^xml/io)
3387             {
3388 0 0         return -532 unless ($XMLavailable);
3389 0           my $xs1 = XML::Simple->new();
3390 0           my $xmldoc;
3391 0           eval {$xmldoc = $xs1->XMLin($file, suppressempty => undef); };
  0            
3392 0           $errdetails = $@;
3393 0 0         return -501 unless ($xmldoc);
3394             @fields = ($xmldoc->{select}->{columns}->{order})
3395             ? split(/\,/, $xmldoc->{select}->{columns}->{order})
3396 0 0         : keys(%{$xmldoc->{select}->{columns}->{column}});
  0            
3397 0           foreach my $i (0..$#fields)
3398             {
3399             #$fields[$i] =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames}); #DON'T *SEEM* TO NEED, BUT ADD IF NEEDED!
3400             $self->{key_fields} .= ($fields[$i] . ',')
3401             if ($xmldoc->{select}->{columns}->{column}->{$fields[$i]}->{key}
3402 0 0         eq 'PRIMARY');
3403 0           ${$self->{types}}{$fields[$i]} =
3404 0           $xmldoc->{select}->{columns}->{column}->{$fields[$i]}->{type};
3405 0           ${$self->{lengths}}{$fields[$i]} =
3406 0           $xmldoc->{select}->{columns}->{column}->{$fields[$i]}->{precision};
3407 0           ${$self->{scales}}{$fields[$i]} =
3408 0           $xmldoc->{select}->{columns}->{column}->{$fields[$i]}->{scale};
3409 0           ${$self->{defaults}}{$fields[$i]} = undef;
  0            
3410 0 0         if (length($xmldoc->{select}->{columns}->{column}->{$fields[$i]}->{default}) > 0)
3411             {
3412 0           ${$self->{defaults}}{$fields[$i]} =
3413 0           $xmldoc->{select}->{columns}->{column}->{$fields[$i]}->{default};
3414             }
3415 0           $self->{use_fields} .= $fields[$i] . ',';
3416             }
3417 0 0         if (ref($xmldoc->{select}->{row}) eq 'ARRAY') #ADDED IF-STMT 20020611 TO HANDLE TABLES W/0 OR 1 RECORD!
    0          
3418             {
3419 0           $self->{records} = $xmldoc->{select}->{row}; #TABLE HAS >1 RECORD.
3420             }
3421             elsif (ref($xmldoc->{select}->{row}) eq 'HASH')
3422             {
3423 0           $self->{records}->[0] = $xmldoc->{select}->{row}; #TABLE HAS 1 RECORD.
3424             }
3425             else
3426             {
3427 0           $self->{records} = undef; #TABLE HAS NO RECORDS!
3428             }
3429 0           $xmldoc = undef;
3430              
3431             #UNESCAPE ALL VALUES.
3432              
3433 0 0         if (ref($self->{records}) eq 'ARRAY') #ADDED IF-STMT 20020611 TO SKIP TABLES W/NO RECORDS!
3434             {
3435 0           require MIME::Base64; #ADDED 20020816!
3436              
3437 0           for (my $i=0;$i<=$#{$self->{records}};$i++)
  0            
3438             {
3439 0           foreach my $j (@fields)
3440             {
3441 0 0         if ($self->{records}->[$i]->{$j}->{'xml:encoding'})
3442             {
3443 0           $self->{records}->[$i]->{$j} = MIME::Base64::decode_base64($self->{records}->[$i]->{$j}->{content});
3444             }
3445 0 0         $self->{records}->[$i]->{$j} = '' if (ref($self->{records}->[$i]->{$j}));
3446 0           $self->{records}->[$i]->{$j} =~ s/\</\
3447 0           $self->{records}->[$i]->{$j} =~ s/\>/\>/gso;
3448 0           $self->{records}->[$i]->{$j} =~ s/\"/\"/gso;
3449 0           $self->{records}->[$i]->{$j} =~ s/\&\#45;/\-/gso;
3450             #$self->{records}->[$i]->{$j} =~ s/\&\#0;/\0/gs;
3451             #$self->{records}->[$i]->{$j} =~ s/\&\#(\d+);/pack('C', $1)/egs;
3452 0           $self->{records}->[$i]->{$j} =~ s/\&/\&/gso;
3453             }
3454             }
3455             }
3456             }
3457             else
3458             {
3459 0 0         open (FILE, $file) || return (-501);
3460 0           binmode FILE; #20000404
3461              
3462             # if (($^O eq 'MSWin32') or ($^O =~ /cygwin/i)) #CHGD. TO NEXT 20020221
3463 0 0         if ($self->{platform} eq 'PC')
3464             {
3465 0 0         $self->lock || $self->display_error (-515);
3466             }
3467             else #GOOD, MUST BE A NON-M$ SYSTEM :-)
3468             {
3469 0 0         eval { flock (FILE, $JSprite::LOCK_EX) || die };
  0            
3470            
3471 0 0         if ($@)
3472             {
3473 0 0 0       $self->lock || $self->display_error (-515) if ($@);
3474             }
3475             }
3476 0           $_ = ;
3477 0           chomp; #JWT:SUPPORT ANY RECORD-SEPARATOR!
3478 0           my $t = $_;
3479 0 0 0       $_ = $self->{CBC}->decrypt($t) if ($self->{CBC} && $self->{sprite_Crypt} != 2); #ADDED: 20020109
3480 0 0         return -527 unless (/^\w+\=/o); #ADDED 20020110
3481              
3482 0           ($header) = /^ *(.*?) *$/o;
3483             #####################$header =~ tr/a-z/A-Z/; #JWT 20000316
3484             #@fields = split (/$self->{_read}/o, $header); #CHGD TO NEXT LINE 20021216.
3485 0           @fields = split (/\Q$self->{_read}\E/, $header);
3486 0           $no_fields = $#fields;
3487              
3488 0           undef %{ $self->{types} };
  0            
3489 0           undef %{ $self->{lengths} };
  0            
3490 0           undef %{ $self->{scales} }; #ADDED 20000306.
  0            
3491              
3492 0           my $ln;
3493 0           foreach $i (0..$#fields)
3494             {
3495 0           $dflt = undef;
3496 0           ($fields[$i],$tp,$dflt) = split(/\=/o ,$fields[$i]);
3497 0 0         $fields[$i] =~ tr/a-z/A-Z/ unless ($self->{sprite_CaseFieldNames});
3498 0 0         $tp = 'VARCHAR(40)' unless($tp);
3499 0           $tp =~ tr/a-z/A-Z/;
3500 0 0         $self->{key_fields} .= $fields[$i] . ','
3501             if ($tp =~ s/^\*//o); #JWT: *TYPE means KEY FIELD!
3502 0           $ln = 40;
3503 0 0         $ln = 10 if ($tp =~ /NUM|INT|FLOAT|DOUBLE/);
3504             #$ln = 5000 if ($tp =~ /$BLOBTYPES/); #CHGD. 20020110.
3505 0 0 0       $ln = $self->{LongReadLen} || 0 if ($tp =~ /$BLOBTYPES/);
3506 0 0         $ln = $2 if ($tp =~ s/(.*)\((.*)\)/$1/);
3507 0           ${$self->{types}}{$fields[$i]} = $tp;
  0            
3508 0           ${$self->{lengths}}{$fields[$i]} = $ln;
  0            
3509 0           ${$self->{defaults}}{$fields[$i]} = undef;
  0            
3510 0 0         ${$self->{defaults}}{$fields[$i]} = $dflt if (defined $dflt);
  0            
3511 0 0         if (${$self->{lengths}}{$fields[$i]} =~ s/\,(\d+)//)
  0 0          
3512             {
3513             #NOTE: ORACLE NEGATIVE SCALES NOT CURRENTLY SUPPORTED!
3514              
3515 0           ${$self->{scales}}{$fields[$i]} = $1;
  0            
3516             }
3517 0           elsif (${$self->{types}}{$fields[$i]} eq 'FLOAT')
3518             {
3519 0           ${$self->{scales}}{$fields[$i]} = ${$self->{lengths}}{$fields[$i]} - 3;
  0            
  0            
3520             }
3521 0 0         ${$self->{scales}}{$fields[$i]} = '0' unless (${$self->{scales}}{$fields[$i]});
  0            
  0            
3522              
3523             # (JWT 8/8/1998) $self->{use_fields} .= $column_string . ','; #JWT
3524 0           $self->{use_fields} .= $fields[$i] . ','; #JWT
3525             }
3526              
3527 0           while ()
3528             {
3529 0           chomp;
3530 0           $t = $_;
3531 0 0 0       $_ = $self->{CBC}->decrypt($t) if ($self->{CBC} && $self->{sprite_Crypt} != 2); #ADDED: 20020109
3532              
3533 0 0         next unless ($_);
3534              
3535             #@record = split (/$self->{_read}/s, $_); #CHGD. TO NEXT LINE 20021216
3536 0           @record = split (/\Q$self->{_read}\E/s, $_);
3537              
3538 0           $hash = {};
3539              
3540 0           for ($loop=0; $loop <= $no_fields; $loop++)
3541             {
3542             #NEXT 2 ADDED 20020111 TO PERMIT EMBEDDED RECORD & FIELD SEPERATORS.
3543 0           $record[$loop] =~ s/\x02\^0jSpR1tE\x02/$self->{_record}/gs; #RESTORE EMBEDDED RECORD SEPARATORS.
3544 0           $record[$loop] =~ s/\x02\^1jSpR1tE\x02/$self->{_read}/gs; #RESTORE EMBEDDED RECORD SEPARATORS.
3545 0           $hash->{ $fields[$loop] } = $record[$loop];
3546             }
3547              
3548 0           push @{ $self->{records} }, $hash;
  0            
3549             }
3550              
3551 0           close (FILE);
3552              
3553 0 0         $self->unlock || $self->display_error (-516);
3554             }
3555              
3556 0 0         chop ($self->{use_fields}) if ($self->{use_fields}); #REMOVE TRAILING ','.
3557 0 0         chop ($self->{key_fields}) if ($self->{key_fields});
3558              
3559 0           undef %{ $self->{fields} };
  0            
3560 0           undef @{ $self->{order} };
  0            
3561              
3562 0           $self->{order} = [ @fields ];
3563 0           $self->{fieldregex} = $self->{use_fields};
3564 0           $self->{fieldregex} =~ s/,/\|/go;
3565              
3566 0           map { $self->{fields}->{$_} = 1 } @fields;
  0            
3567              
3568 0           return (1);
3569             }
3570              
3571             sub load_columninfo
3572             {
3573 0     0 0   my ($self) = shift;
3574 0           my ($sep) = shift;
3575              
3576 0           my $colmlist;
3577              
3578 0 0         if ($#{$self->{order}} >= 0)
  0            
3579             {
3580 0           $colmlist = join($sep, @{$self->{order}});
  0            
3581             }
3582             else
3583             {
3584 0           local (*FILE);
3585 0           local ($_);
3586 0           local ($/) = $self->{_record}; #JWT:SUPPORT ANY RECORD-SEPARATOR!
3587            
3588 0 0         open(FILE, $self->{file}) || return -501;
3589 0           binmode FILE; #20000404
3590 0 0         if ($self->{_read} =~ /^xml/io)
3591             {
3592 0 0 0       return -531 if ($self->{CBC} && $self->{sprite_Crypt} <= 2);
3593 0 0         return -532 unless ($XMLavailable);
3594            
3595 0           my $xs1 = XML::Simple->new();
3596 0           my $xmltext = '';
3597 0           my $xmldoc;
3598             # eval {$xmldoc = $xs1->XMLin($self->{file}, suppressempty => undef); };
3599 0           while ()
3600             {
3601 0 0         last if (/^\s*\\s*$/o);
3602 0           $xmltext .= $_;
3603             }
3604 0           $xmltext .= <
3605            
3606            
3607            
3608             END_XML
3609 0           eval {$xmldoc = $xs1->XMLin($xmltext, suppressempty => undef); };
  0            
3610 0           $errdetails = $@;
3611 0 0         return -501 unless ($xmldoc);
3612 0           $colmlist = $xmldoc->{select}->{columns}->{order};
3613 0 0         if ($colmlist)
3614             {
3615 0           @{$self->{order}} = split(/$sep/, $colmlist);
  0            
3616             }
3617             else
3618             {
3619 0           @{$self->{order}} = keys(%{$xmldoc->{select}->{columns}->{column}});
  0            
  0            
3620 0           $colmlist = join($sep, @{$self->{order}});
  0            
3621             }
3622             }
3623             else
3624             {
3625 0           my $colmlist = ;
3626 0           chomp ($colmlist);
3627             #$colmlist =~ s/$self->{_read}/$sep/g; #CHGD. TO NEXT LINE 20021216
3628 0           $colmlist =~ s/\Q$self->{_read}\E/$sep/g;
3629 0           @{$self->{order}} = split(/$sep/, $colmlist);
  0            
3630             }
3631 0           close FILE;
3632             }
3633 0           return $colmlist;
3634             }
3635              
3636             sub pscolfn
3637             {
3638 0     0 0   my ($self,$id) = @_;
3639 0 0         return $id unless ($id =~ /CURRVAL|NEXTVAL/);
3640 0           my ($value) = '';
3641 0           my ($seq_file,$col) = split(/\./,$id);
3642 0           $seq_file = $self->get_path_info($seq_file) . '.seq';
3643             # $seq_file =~ tr/A-Z/a-z/ unless ($self->{CaseTableNames}); #JWT:TABLE-NAMES ARE NOW CASE-INSENSITIVE! - REMOVED 20011218 (get_path_info HANDLES THIS RIGHT!)
3644             #open (FILE, "<$seq_file") || return (-511);
3645 0 0         unless (open (FILE, "<$seq_file"))
3646             {
3647 0           $errdetails = "$@/$? (file:$seq_file)";
3648 0           return (-511);
3649             }
3650 0           my $x = ;
3651             #chomp($x);
3652 0           $x =~ s/\s+$//o; #20000113
3653 0           my ($incval, $startval) = split(/\,/o ,$x);
3654 0           close (FILE);
3655 0 0         if ($id =~ /NEXTVAL/o)
3656             {
3657             #open (FILE, ">$seq_file") || return (-511);
3658 0 0 0       unlink ($seq_file) if ($self->{sprite_forcereplace} && -e $seq_file); #ADDED 20010912.
3659 0 0         unless (open (FILE, ">$seq_file"))
3660             {
3661 0           $errdetails = "$@/$? (file:$seq_file)";
3662 0           return (-511);
3663             }
3664 0   0       $incval += ($startval || 1);
3665 0           print FILE "$incval,$startval\n";
3666 0           close (FILE);
3667             }
3668 0           $value = $incval;
3669 0           $self->{sprite_lastsequence} = $incval; #ADDED 20020905 TO SUPPORT DBIx::GeneratedKey!
3670 0           return $value;
3671             }
3672              
3673             ##++
3674             ## NOTE: Derived from lib/Text/ParseWords.pm. Thanks Hal!
3675             ##--
3676              
3677             sub quotewords { #SPLIT UP USER'S SEARCH-EXPRESSION INTO "WORDS" (TOKENISE)!
3678              
3679             # THIS CODE WAS COPIED FROM THE PERL "TEXT" MODULE, (ParseWords.pm),
3680             # written by: Hal Pomeranz (pomeranz@netcom.com), 23 March 1994
3681             # (Thanks, Hal!)
3682             # MODIFIED BY JIM TURNER (6/97) TO ALLOW ESCAPED (REGULAR-EXPRESSION)
3683             # CHARACTERS TO BE INCLUDED IN WORDS AND TO COMPRESS MULTIPLE OCCURRANCES
3684             # OF THE DELIMITER CHARACTER TO BE COMPRESSED INTO A SINGLE DELIMITER
3685             # (NO EMPTY WORDS).
3686             #
3687             # The inner "for" loop builds up each word (or $field) one $snippet
3688             # at a time. A $snippet is a quoted string, a backslashed character,
3689             # or an unquoted string. We fall out of the "for" loop when we reach
3690             # the end of $_ or when we hit a delimiter. Falling out of the "for"
3691             # loop, we push the $field we've been building up onto the list of
3692             # @words we'll be returning, and then loop back and pull another word
3693             # off of $_.
3694             #
3695             # The first two cases inside the "for" loop deal with quoted strings.
3696             # The first case matches a double quoted string, removes it from $_,
3697             # and assigns the double quoted string to $snippet in the body of the
3698             # conditional. The second case handles single quoted strings. In
3699             # the third case we've found a quote at the current beginning of $_,
3700             # but it didn't match the quoted string regexps in the first two cases,
3701             # so it must be an unbalanced quote and we croak with an error (which can
3702             # be caught by eval()).
3703             #
3704             # The next case handles backslashed characters, and the next case is the
3705             # exit case on reaching the end of the string or finding a delimiter.
3706             #
3707             # Otherwise, we've found an unquoted thing and we pull of characters one
3708             # at a time until we reach something that could start another $snippet--
3709             # a quote of some sort, a backslash, or the delimiter. This one character
3710             # at a time behavior was necessary if the delimiter was going to be a
3711             # regexp (love to hear it if you can figure out a better way).
3712              
3713 0     0 0   my ($self, $delim, $keep, @lines) = @_;
3714 0           my (@words,$snippet,$field,$q,@quotes);
3715              
3716 0           $_ = join('', @lines);
3717 0           while ($_) {
3718 0           $field = '';
3719 0           for (;;) {
3720 0           $snippet = '';
3721 0           @quotes = ('\'','"');
3722 0 0 0       if (s/^(["'`])(.+?)\1//) {
    0          
    0          
    0          
3723 0           $snippet = $2;
3724 0 0         $snippet = "$1$snippet$1" if ($keep);
3725 0           $field .= $snippet;
3726 0           last;
3727             }
3728             elsif (/^["']/o) {
3729 0           $self->display_error(-512);
3730 0           return ();
3731             }
3732             elsif (s/^\\(.)//o) {
3733 0           $snippet = $1;
3734 0 0         $snippet = "\\$snippet" if ($keep);
3735             }
3736             elsif (!$_ || s/^$delim//) { #REMOVE "+" TO REMOVE DELIMITER-COMPRESSION.
3737 0           last;
3738             }
3739             else {
3740 0   0       while ($_ && !(/^$delim/)) { #ATTEMPT TO HANDLE TWO QUOTES IN A ROW.
3741 0 0 0       last if (/^['"]/ && ($snippet !~ /\\$/o));
3742 0           $snippet .= substr($_, 0, 1);
3743 0           substr($_, 0, 1) = '';
3744             }
3745             }
3746 0           $field .= $snippet;
3747             }
3748 0           push(@words, $field);
3749             }
3750 0           @words;
3751             }
3752              
3753             sub chkcolumnparms #ADDED 20001218 TO CHECK FUNCTION PARAMETERS FOR FIELD-NAMES.
3754             {
3755 0     0 0   my ($self) = shift;
3756 0           my ($evalstr) = shift;
3757              
3758             # $evalstr =~ s/\\\'|\'\'/\x02\^2jSpR1tE\x02/g; #PROTECT QUOTES W/N QUOTES.
3759             # $evalstr =~ s/\\\"|\"\"/\x02\^3jSpR1tE\x02/g; #PROTECT QUOTES W/N QUOTES.
3760 0           $evalstr =~ s/\\\'/\x02\^2jSpR1tE\x02/gso; #PROTECT ESCAPED QUOTES.
3761 0           $evalstr =~ s/\\\"/\x02\^3jSpR1tE\x02/gso; #PROTECT ESCAPED QUOTES.
3762            
3763 0           my $i = -1;
3764 0           my (@strings); #PROTECT ANYTHING BETWEEN QUOTES (FIELD NAMES IN LITERALS).
3765 0           $evalstr =~ s/([\'\"])([^\1]*?)\1/
3766 0           my ($one, $two) = ($1, $2);
3767 0           ++$i;
3768 0           $two =~ s|([\'\"])|$1$1|g;
3769 0           $strings[$i] = "$one$two$one";
3770 0           "\x02\^4jSpR1tE\x02$i";
3771             /egs;
3772              
3773             #FIND EACH FIELD NAME PARAMETER & REPLACE IT WITH IT'S VALUE || NAME || EMPTY-STRING.
3774             #$evalstr =~ s/($fieldregex)/ #CHGD. TO NEXT 20020530 + REMVD THIS VBLE.
3775 0           $evalstr =~ s/($self->{fieldregex})/
3776 0           my ($one) = $1;
3777 0           $one =~ tr!a-z!A-Z!;
3778 0 0         my $res = (defined $_->{$one}) ? $_->{$one} : $one;
3779              
3780             #$res ||= '""'; #CHGD. TO NEXT (20020225)!
3781 0 0         $res = '"'.$res.'"' unless (${$self->{types}}{$one} =~ m#$NUMERICTYPES#i);
  0            
3782 0           $res;
3783             /eigs;
3784              
3785 0           $evalstr =~ s/\x02\^4jSpR1tE\x02(\d+)/$strings[$1]/g; #UNPROTECT LITERALS
3786 0           $evalstr =~ s/\x02\^3jSpR1tE\x02/\\\'/go; #UNPROTECT QUOTES.
3787 0           $evalstr =~ s/\x02\^2jSpR1tE\x02/\\\"/go;
3788 0           return $evalstr;
3789             }
3790              
3791             sub SYSTIME
3792             {
3793 0     0 0   return time;
3794             }
3795              
3796             sub SYSDATE
3797             {
3798 0     0 0   return time;
3799             }
3800              
3801             sub NUM
3802             {
3803 0     0 0   return shift;
3804             }
3805              
3806             sub NULL
3807             {
3808 0     0 0   return '';
3809             }
3810              
3811             sub ROWNUM
3812             {
3813 0     0 0   return (scalar (@$results) + 1);
3814             }
3815              
3816             sub USER
3817             {
3818 0     0 0   return $sprite_user;
3819             }
3820              
3821             sub fn_register #REGISTER SQL-CALLABLE FUNCTIONS.
3822             {
3823 0 0   0 1   shift if (ref($_[0]) eq 'HASH'); #20000224
3824 0           my ($fnname, $packagename) = @_;
3825 0 0         $packagename = 'main' unless ($packagename);
3826              
3827 0           eval <
3828             sub $fnname
3829             {
3830             return &${packagename}::$fnname;
3831             }
3832             END_EVAL
3833             }
3834              
3835             1;