File Coverage

blib/lib/JSprite.pm
Criterion Covered Total %
statement 1078 1880 57.3
branch 282 790 35.7
condition 78 320 24.3
subroutine 37 55 67.2
pod 14 46 30.4
total 1489 3091 48.1


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