File Coverage

blib/lib/DBD/Sprite.pm
Criterion Covered Total %
statement 82 724 11.3
branch 15 264 5.6
condition 5 123 4.0
subroutine 15 39 38.4
pod 0 1 0.0
total 117 1151 10.1


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             DBD::Sprite - Perl extension for DBI, providing database emmulation via flat files.
4              
5             =head1 AUTHOR
6              
7             This module is Copyright (C) 2000 by
8              
9             Jim Turner
10            
11             Email: jim.turner@lmco.com
12              
13             All rights reserved.
14              
15             You may distribute this module under the terms of either the GNU General
16             Public License or the Artistic License, as specified in the Perl README
17             file.
18              
19             JSprite.pm is a derived work by Jim Turner from Sprite.pm, a module
20             written and copyrighted (c) 1995-1998, by Shishir Gurdavaram
21             (shishir@ora.com).
22              
23             =head1 SYNOPSIS
24              
25             use DBI;
26             $dbh = DBI->connect("DBI:Sprite:spritedb",'user','password')
27             or die "Cannot connect: " . $DBI::errstr;
28             $sth = $dbh->prepare("CREATE TABLE a (id INTEGER, name CHAR(10))")
29             or die "Cannot prepare: " . $dbh->errstr();
30             $sth->execute() or die "Cannot execute: " . $sth->errstr();
31             $sth->finish();
32             $dbh->disconnect();
33              
34             =head1 DESCRIPTION
35              
36             DBD::Sprite is a DBI extension module adding database emulation via flat-files
37             to Perl's database-independent database interface. Unlike other DBD::modules,
38             DBD::Sprite does not require you to purchase or obtain a database. Every
39             thing you need to prototype database-independent applications using Perl and
40             DBI are included here. You will, however, probably wish to obtain a real
41             database, such as "mysql", for your production and larger data needs. This
42             is because emulating databases and SQL with flat text files gets very slow as
43             the size of your "database" grows to a non-trivial size (a few dozen records
44             or so per table).
45              
46             DBD::Sprite is built upon an old Perl module called "Sprite", written by
47             Shishir Gurdavaram. This code was used as a starting point. It was completly
48             reworked and many new features were added, producing a module called
49             "JSprite.pm" (Jim Turner's Sprite). This was then merged in to DBI::DBD to
50             produce what you are installing now. (DBD::Sprite). JSprite.pm is included
51             in this module as a separate file, and is required.
52              
53             Many thanks go to Mr. Gurdavaram.
54              
55             The main advantage of DBD::Sprite is the ability to develop and test
56             prototype applications on personal machines (or other machines which do not
57             have an Oracle licence or some other "mainstream" database) before releasing
58             them on "production" machines which do have a "real" database. This can all
59             be done with minimal or no changes to your Perl code.
60              
61             Another advantage of DBD::Sprite is that you can use Perl's regular
62             expressions to search through your data. Maybe, someday, more "real"
63             databases will include this feature too!
64              
65             DBD::Sprite provides the ability to emulate basic database tables
66             and SQL calls via flat-files. The primary use envisioned
67             for this to permit website developers who can not afford
68             to purchase an Oracle licence to prototype and develop Perl
69             applications on their own equipment for later hosting at
70             larger customer sites where Oracle is used. :-)
71              
72             DBD::Sprite attempts to do things in as database-independent manner as possible,
73             but where differences occurr, JSprite most closely emmulates Oracle, for
74             example "sequences/autonumbering". JSprite uses tiny one-line text files
75             called "sequence files" (.seq). and "seq_file_name.NEXTVAL" function to
76             insert into autonumbered fields. The reason for this is that the Author
77             works in an Oracle shop and wrote this module to allow himself to work on
78             code on his PC, and machines which did not have Oracle on them, since
79             obtaining Oracle licences was sometimes time-consuming.
80              
81             DBD::Sprite is similar to DBD::CSV, but differs in the following ways:
82              
83             1) It creates and works on true "databases" with user-ids and passwords,
84             real datatypes like numeric, varchar, blob, etc. with max. precisions and
85             scales.
86              
87             2) The database author specifies the field delimiters, record delimiters,
88             user, password, table file path, AND extension for each database.
89              
90             3) Transactions (commits and rollbacks) are fully supported!
91              
92             4) Autonumbering and user-defined functions are supported.
93              
94             5) You don't need any other modules or databases. (NO prerequisites
95             except Perl 5 and the DBI module!
96              
97             6) Quotes are not used around data.
98              
99             7) It is not necessary to call the "$dbh->quote()" method all the time
100             in your sql.
101              
102             8) NULL is handled as an empty string.
103              
104             9) Users can "register" their own data-conversion functions for use in
105             sql. See "fn_register" method below.
106              
107             10) Optional data encryption.
108              
109             11) Optional table storage in XML format.
110            
111             12) Two-table joins now supported!
112              
113              
114             =head1 INSTALLATION
115              
116             Installing this module (and the prerequisites from above) is quite
117             simple. You just fetch the archive, extract it with
118              
119             gzip -cd DBD-Sprite-0.1000.tar.gz | tar xf -
120              
121             (this is for Unix users, Windows users would prefer WinZip or something
122             similar) and then enter the following:
123              
124             cd DBD-Sprite-#.###
125             perl Makefile.PL
126             make
127             make test
128              
129             If any tests fail, let me know. Otherwise go on with
130              
131             make install
132              
133             Note that you almost definitely need root or administrator permissions.
134             If you don't have them, read the ExtUtils::MakeMaker man page for
135             details on installing in your own directories. the ExtUtils::MakeMaker
136             manpage.
137              
138             NOTE: You may also need to copy "makesdb.pl" to /usr/local/bin or
139             somewhere in your path.
140              
141             =head1 GETTING STARTED:
142              
143             1) cd to where you wish to store your database.
144             2) run makesdb.pl to create your database, ie.
145            
146             Database name: mydb
147             Database user: me
148             User password: mypassword
149             Database path: .
150             Table file extension (default .stb):
151             Record delimiter (default \n):
152             Field delimiter (default ::):
153              
154             This will create a new database text file (mydb.sdb) in the current
155             directory. This ascii file contains the information you enterred
156             above. To add additional user-spaces, simply rerun makesdb.pl with
157             "mydb" as your database name, and enter additional users (name,
158             password, path, extension, and delimiters). For an example, after
159             running "make test", look at the file "test.sdb".
160            
161             When connecting to a Sprite database, Sprite will look in the current
162             directory, then, if specified, the path in the SPRITE_HOME environment
163             variable.
164              
165             The database name, user, and password are used in the "db->connect()"
166             method described below. The "database path" is where your tables will
167             be created and reside. Table files are ascii text files which will
168             have, by default, the extension ".stb" (Sprite table). By default,
169             each record will be written to a single line (separated by \n --
170             Windows users should probably use "\r\n"). Each field datum will be
171             written without quotes separated by the "field delimiter (default:
172             double-colon). The first line of the table file consists of the
173             a field name, an equal ("=") sign, an asterisk if it is a key field,
174             then the datatype and size. This information is included for each
175             field and separated by the field separator. For an example, after
176             running "make test", look at the file "testtable.stb".
177              
178             3) write your script to use DBI, ie:
179            
180             #!/usr/bin/perl
181             use DBI;
182            
183             $dbh = DBI->connect('DBI:Sprite:mydb','me','mypassword') ||
184             die "Could not connect (".$DBI->err.':'.$DBI->errstr.")!";
185             ...
186             #CREATE A TABLE, INSERT SOME RECORDS, HAVE SOME FUN!
187            
188             4) get your application working.
189            
190             5) rehost your application on a "production" machine and change "Sprite"
191             to a DBI driver for a "real" database!
192              
193             =head1 CREATING AND DROPPING TABLES
194              
195             You can create and drop tables with commands like the following:
196              
197             $dbh->do("CREATE TABLE $table (id INTEGER, name CHAR(64))");
198             $dbh->do("DROP TABLE $table");
199              
200             Column names, datatypes, precision, scales, and autonumber sequences are
201             stored on the top line as COLUNM_NAME(PRECISION[,SCALE])=DEFAULT_VALUE
202              
203             A drop just removes the file without any warning.
204              
205             See the DBI(3) manpage for more details.
206              
207             Table names cannot be arbitrary, due to restrictions of the SQL syntax.
208             I recommend that table names are valid SQL identifiers: The first
209             character is alphabetic, followed by an arbitrary number of alphanumeric
210             characters. If you want to use other files, the file names must start
211             with '/', './' or '../' and they must not contain white space.
212              
213             =head1 INSERTING, FETCHING AND MODIFYING DATA
214              
215             The following examples insert some data in a table and fetch it back:
216             First all data in the string:
217              
218             $dbh->do("INSERT INTO $table VALUES (1, 'foobar')");
219              
220             Note the use of the quote method for escaping the word 'foobar'. Any
221             string must be escaped, even if it doesn't contain binary data.
222              
223             Next an example using parameters:
224              
225             $dbh->do("INSERT INTO $table VALUES (?, ?)", undef,
226             2, "It's a string!");
227              
228             To retrieve data, you can use the following:
229              
230             my($query) = "SELECT * FROM $table WHERE id > 1 ORDER BY id";
231             my($sth) = $dbh->prepare($query);
232             $sth->execute();
233             while (my $row = $sth->fetchrow_hashref) {
234             print("Found result row: id = ", $row->{'id'},
235             ", name = ", $row->{'name'});
236             }
237             $sth->finish();
238              
239             Again, column binding works: The same example again.
240              
241             my($query) = "SELECT * FROM $table WHERE id > 1 ORDER BY id";
242             my($sth) = $dbh->prepare($query);
243             $sth->execute();
244             my($id, $name);
245             $sth->bind_columns(undef, \$id, \$name);
246             while ($sth->fetch) {
247             print("Found result row: id = $id, name = $name\n");
248             }
249             $sth->finish();
250              
251             Of course you can even use input parameters. Here's the same example for
252             the third time:
253              
254             my($query) = "SELECT * FROM $table WHERE id = ?";
255             my($sth) = $dbh->prepare($query);
256             $sth->bind_columns(undef, \$id, \$name);
257             for (my($i) = 1; $i <= 2; $i++) {
258             $sth->execute($id);
259             if ($sth->fetch) {
260             print("Found result row: id = $id, name = $name\n");
261             }
262             $sth->finish();
263             }
264              
265             See the DBI(3) manpage for details on these methods. See the
266             SQL::Statement(3) manpage for details on the WHERE clause.
267              
268             Data rows are modified with the UPDATE statement:
269              
270             $dbh->do("UPDATE $table SET id = 3 WHERE id = 1");
271              
272             Likewise you use the DELETE statement for removing rows:
273              
274             $dbh->do("DELETE FROM $table WHERE id > 1");
275              
276             I
277              
278             Method takes 2 arguments: Function name and optionally, a
279             package name (default is "main").
280              
281             $dbh->fn_register ('myfn','mypackage');
282            
283             -or-
284              
285             use JSprite;
286             JSprite::fn_register ('myfn',__PACKAGE__);
287              
288             Then, you could say in sql:
289              
290             insert into mytable values (myfn(?))
291            
292             and bind some value to "?", which is passed to "myfn", and the return-value
293             is inserted into the database. You could also say (without binding):
294              
295             insert into mytable values (myfn('mystring'))
296            
297             -or (if the function takes a number)-
298              
299             select field1, field2 from mytable where field3 = myfn(123)
300            
301             I
302              
303             None
304              
305             =head1 ERROR HANDLING
306              
307             In the above examples we have never cared about return codes. Of course,
308             this cannot be recommended. Instead we should have written (for
309             example):
310              
311             my($query) = "SELECT * FROM $table WHERE id = ?";
312             my($sth) = $dbh->prepare($query)
313             or die "prepare: " . $dbh->errstr();
314             $sth->bind_columns(undef, \$id, \$name)
315             or die "bind_columns: " . $dbh->errstr();
316             for (my($i) = 1; $i <= 2; $i++) {
317             $sth->execute($id)
318             or die "execute: " . $dbh->errstr();
319             if ($sth->fetch) {
320             print("Found result row: id = $id, name = $name\n");
321             }
322             }
323             $sth->finish($id)
324             or die "finish: " . $dbh->errstr();
325              
326             Obviously this is tedious. Fortunately we have DBI's *RaiseError*
327             attribute:
328              
329             $dbh->{'RaiseError'} = 1;
330             $@ = '';
331             eval {
332             my($query) = "SELECT * FROM $table WHERE id = ?";
333             my($sth) = $dbh->prepare($query);
334             $sth->bind_columns(undef, \$id, \$name);
335             for (my($i) = 1; $i <= 2; $i++) {
336             $sth->execute($id);
337             if ($sth->fetch) {
338             print("Found result row: id = $id, name = $name\n");
339             }
340             }
341             $sth->finish($id);
342             };
343             if ($@) { die "SQL database error: $@"; }
344              
345             This is not only shorter, it even works when using DBI methods within
346             subroutines.
347              
348             =head1 METADATA
349              
350             The following attributes are handled by DBI itself and not by DBD::File,
351             thus they should all work as expected: I have only used the last 3.
352              
353             I
354              
355             I
356              
357             I
358              
359             I (Not used)
360              
361             I
362              
363             I
364              
365             I
366              
367             I
368              
369             I
370              
371             The following DBI attributes are handled by DBD::Sprite:
372              
373             B
374              
375             Works
376              
377             B
378              
379             Should Work
380              
381             B
382              
383             Valid after `$sth->execute'
384              
385             B
386              
387             Valid after `$sth->prepare'
388              
389             B
390              
391             Valid after `$sth->execute'; undef for Non-Select statements.
392              
393             B
394              
395             Not really working. Always returns an array ref of one's, as
396             DBD::Sprite always allows NULL (handled as an empty string).
397             Valid after `$sth->execute'.
398            
399             B
400              
401             Works
402            
403             B
404              
405             Works
406              
407             B
408              
409             Should work
410              
411             B
412              
413             Works
414              
415             These attributes and methods are not supported:
416              
417             B
418              
419             B
420              
421              
422             In addition to the DBI attributes, you can use the following dbh
423             attributes. These attributes are read-only after "connect".
424              
425             I
426              
427             Path to tables for database.
428            
429             I
430              
431             File extension used on table files in the database.
432            
433             I
434              
435             Current database user.
436            
437             I
438              
439             Field delimiter string in use for the database.
440            
441             I
442              
443             Record delimiter string in use for the database.
444              
445             The following are environment variables specifically recognized by Sprite.
446              
447             I
448             Environment variable specifying a path to search for Sprite
449             databases (*.sdb) files.
450              
451              
452             The following are Sprite-specific options which can be set when connecting.
453              
454             I => 0 | 1
455              
456             By default, table names are case-insensitive (as they are in Oracle),
457             to make table names case-sensitive (as in MySql), so that one could
458             have two separate tables such as "test" and "TEST", set this option
459             to 1.
460              
461             I => 0 | 1
462              
463             By default, field names are case-insensitive (as they are in Oracle),
464             to make field names case-sensitive, so that one could
465             have two separate fields such as "test" and "TEST", set this option
466             to 1. The default is 1 (case-sensitive) if XML.
467              
468             I => 0 | 1
469              
470             CHAR fields are always right-padded with spaces to fill out
471             the field. Old (pre 5.17) Sprite behaviour was to require the
472             padding be included in literals used for testing equality in
473             "where" clauses. I discovered that Oracle and some other databases
474             do not require this when testing DBIx-Recordset, so Sprite will
475             automatically right-pad literals when testing for equality.
476             To disable this and force the old behavior, set this option to 1.
477            
478             I => [encrypt=|decrypt=][Crypt]::CBC;][[IDEA[_PP]|DES[_PP]|BLOWFISH[_PP];]keystring
479            
480             Optional encryption and/or decryption of data stored in tables. By
481             omitting "encrypt=" and "decrypt=", data will be decrypted when read
482             from the table and encrypted when written to the table using the
483             "keystring" as the key.
484            
485             I => 0 | 1
486            
487             This option forces the table file to first be deleted before being
488             overwritten. Default is 0 (do not delete, just overwrite it). This
489             was need by the author on certain network filesystems on one jobsite.
490            
491             I => xsl_stylesheet_url
492            
493             Optional xsl stylesheet url to be included in database tables in XML
494             format. Otherwise, ignored. Default none.
495              
496             I => 0 | 1
497            
498             By default, on error, Sprite prints the legacy
499             "Oops! Sprite encountered the following error when processing your request..."
500             multiline error message carried over from the original Sprite by
501             Shishir Gurdavaram. Set to 1 to silense this, if it annoys you, or if you
502             are using Sprite in a CGI script.
503              
504             The following attributes can be specified as a hash reference in "prepare"
505             statements:
506            
507             I => #
508            
509             Limit processing the table to # records. This is NOT the same as a
510             "LIMIT #" clause in selects. This limits the query to the first #
511             records in the table UNSORTED - BEFORE any constraints or sorting are
512             applied. This is useful for limiting queries to, say 1 record
513             simply to populate the column metadata.
514            
515             I => #
516            
517             This is the same as adding a "LIMIT #" clause to a select statement
518             when preparing it, as it will limit a query to returning # records
519             AFTER applying any constraints and sorting.
520            
521             =head1 DRIVER PRIVATE METHODS
522              
523             B->B()
524              
525             The `data_sources' method returns a list of "databases" (.sdb files)
526             found in the current directory and, if specified, the path in
527             the SPRITE_HOME environment variable.
528            
529             $dbh->B()
530              
531             This method returns a list of table names specified in the current
532             database.
533             Example:
534              
535             my($dbh) = DBI->connect("DBI:Sprite:mydatabase",'me','mypswd');
536             my(@list) = $dbh->func('tables');
537              
538             B('myfn', __PACKAGE__);
539              
540             This method takes the name of a user-defined data-conversion function
541             for use in SQL commands. Your function can optionally take arguments,
542             but should return a single number or string. Unless your function
543             is defined in package "main", you must also specify the package name
544             or "__PACKAGE__" for the current package. For an example, see the
545             section "INSERTING, FETCHING AND MODIFYING DATA" above or (JSprite(3)).
546            
547             =head1 OTHER SUPPORTING UTILITIES
548              
549             B
550              
551             This utility lets you build new Sprite databases and later add
552             additional user-spaces to them. Simply cd to the directory where
553             you wish to create / modify a database, and run. It prompts as
554             follows:
555            
556             Database name: Enter a 1-word name for your database.
557             Database user: Enter a 1-word user-name.
558             User password: Enter a 1-word password for this user.
559             Database path: Enter a path (no trailing backslash) to store tables.
560             Table file extension (default .stb):
561             Record delimiter (default \n):
562             Field delimiter (default ::):
563              
564             The last 6 prompts repeat until you do not enter another user-name
565             allowing you to set up multiple users in a single database. Each
566             "user" can have it's own separate tables by specifying different
567             paths, file-extensions, password, and delimiters! You can invoke
568             "makesdb.pl" on an existing database to add new users. You can
569             edit it with vi to remove users, delete the 5 lines starting with
570             the path for that user. The file is all text, except for the
571             password, which is encrypted for your protection!
572            
573             =head1 RESTRICTIONS
574              
575             Although DBD::Sprite supports the following datatypes:
576             NUMBER FLOAT DOUBLE INT INTEGER NUM CHAR VARCHAR VARCHAR2
577             DATE LONG BLOB and MEMO, there are really only 4 basic datatypes
578             (NUMBER, CHAR, VARCHAR, and BLOB). This is because Perl treates
579             everything as simple strings. The first 5 are all treated as "numbers"
580             by Perl for sorting purposes and the rest as strings. This is seen
581             when sorting, ie NUMERIC types sort as 1,5,10,40,200, whereas
582             STRING types sort these as 1,10,200,40,5. CHAR fields are right-
583             padded with spaces when stored. LONG-type fields are subject to
584             truncation by the "LongReadLen" attribute value.
585              
586             DBD::Sprite works with the tieDBI module, if "Sprite => 1" lines are added
587             to the "%CAN_BIND" and "%CAN_BINDSELECT" hashes. This should not be
588             necessary, and I will investigate when I have time.
589            
590             =head1 KNOWN BUGS
591              
592             * The module is using flock() internally. However, this function is
593             not available on platforms. Using flock() is disabled on MacOS
594             and Windows 95: There's no locking at all (perhaps not so
595             important on these operating systems, as they are for single
596             users anyways).
597              
598              
599             =head1 SEE ALSO
600              
601             B, B
602              
603             =cut
604              
605             package DBD::Sprite;
606              
607             #no warnings 'uninitialized';
608              
609 1     1   2479 use strict;
  1         1  
  1         30  
610             #use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
611 1     1   5 use vars qw($VERSION $err $errstr $state $sqlstate $drh $i $j $dbcnt);
  1         1  
  1         250  
612              
613             #require Exporter;
614              
615             #@ISA = qw(Exporter AutoLoader);
616             # Items to export into callers namespace by default. Note: do not export
617             # names by default without a very good reason. Use EXPORT_OK instead.
618             # Do not simply export all your public functions/methods/constants.
619             #@EXPORT = qw(
620              
621             #);
622             $VERSION = '6.1';
623              
624             # Preloaded methods go here.
625              
626             $err = 0; # holds error code for DBI::err
627             $errstr = ''; # holds error string for DBI::errstr
628             $sqlstate = '';
629             $drh = undef; # holds driver handle once initialised
630              
631             sub driver{
632 1 50   1 0 936012 return $drh if $drh;
633 1         8 my($class, $attr) = @_;
634              
635 1         7 $class .= "::dr";
636              
637             # not a 'my' since we use it above to prevent multiple drivers
638 1         54 $drh = DBI::_new_drh($class, { 'Name' => 'Sprite',
639             'Version' => $VERSION,
640             'Err' => \$DBD::Sprite::err,
641             'Errstr' => \$DBD::Sprite::errstr,
642             'State' => \$DBD::Sprite::state,
643             'Attribution' => 'DBD::Sprite by Shishir Gurdavaram & Jim Turner',
644             });
645 1         110 $drh;
646             }
647              
648             sub DESTROY #ADDED 20001108
649       0     {
650             }
651              
652             #sub AUTOLOAD {
653             # print "***** AUTOLOAD CALLED! *****\n";
654             #}
655              
656             1;
657              
658              
659             package DBD::Sprite::dr; # ====== DRIVER ======
660 1     1   5 use strict;
  1         9  
  1         26  
661 1     1   5 use vars qw($imp_data_size);
  1         1  
  1         1665  
662              
663             $DBD::Sprite::dr::imp_data_size = 0;
664              
665             sub connect {
666 1     1   180 my($drh, $dbname, $dbuser, $dbpswd, $attr, $old_driver, $connect_meth) = @_;
667              
668             #DON'T PASS ATTRIBUTES IN AS A STRING, MUST BE A HASH-REF!
669              
670 1         4 my($port);
671 1         2 my($cWarn, $i, $j);
672              
673 1         8 $_ = ''; #ONLY WAY I KNOW HOW TO RETURN ERRORS FROM HERE ($DBI::err WON'T WORK!)
674              
675             # Avoid warnings for undefined values
676 1   50     7 $dbuser ||= '';
677 1   50     6 $dbpswd ||= '';
678              
679             # create a 'blank' dbh
680 1         10 my($privateattr) = {
681             'Name' => $dbname,
682             'user' => $dbuser,
683             'dbpswd' => $dbpswd
684             };
685             #if (!defined($this = DBI::_new_dbh($drh, {
686 1         16 my $this = DBI::_new_dbh($drh, {
687             'Name' => $dbname,
688             'USER' => $dbuser,
689             'CURRENT_USER' => $dbuser,
690             });
691            
692             # Call Sprite Connect function
693             # and populate internal handle data.
694 1 50       104 if ($this) #ADDED 20010226 TO FIX BAD ERROR MESSAGE HANDLING IF INVALID UN/PW ENTERED.
695             {
696 1         4 my $dbfid = $dbname;
697 1 50       23 $dbfid .= '.sdb' unless ($dbfid =~ /\.\w+$/);
698 1   50     40 $ENV{SPRITE_HOME} ||= '';
699 1 50       10 if ($dbfid =~ m#^/#)
700             {
701 0 0       0 unless (open(DBFILE, "<$dbfid"))
702             {
703             #DBI::set_err($this, -1, "No such database ($dbname)!"); #REPLACED W/NEXT LINE 20021021!
704 0 0       0 warn "No such database ($dbname)!" if ($attr->{PrintError});
705 0         0 $_ = "-1:No such database ($dbname)!";
706 0         0 return undef;
707             }
708             }
709             else
710             {
711 1 50       266 unless (open(DBFILE, "<$ENV{SPRITE_HOME}/$dbfid"))
712             {
713 1 50       17 unless (open(DBFILE, "<$dbfid"))
714             {
715 1 50       183 unless (open(DBFILE, "<$ENV{HOME}/$dbfid")) #NEXT 4 ADDED 20040909
716             {
717 1         7 my $pgmhome = $0;
718 1         10 $pgmhome =~ s#[^/\\]*$##; #SET NAME TO SQL.PL FOR ORAPERL!
719 1   50     7 $pgmhome ||= '.';
720 1 50 33     8 $pgmhome .= '/' unless ($pgmhome =~ m#\/$# || $dbfid =~ m#^\/#);
721 1 50       11 unless (open(DBFILE, "<${pgmhome}$dbfid"))
722             {
723 1         39 $_ = "-1:No such database ($dbname) ($!)!";
724 1         17 DBI::set_err($this, -1, $_); #REPLACED W/NEXT LINE 20021021!
725 1 50       49 warn $DBI::errstr if ($attr->{PrintError});
726 1         28 return undef;
727             }
728             }
729             }
730             }
731             }
732 0         0 my (@dbinputs) = ;
733 0         0 foreach $i (0..$#dbinputs)
734             {
735 0         0 chomp ($dbinputs[$i]);
736             }
737 0         0 my ($inputcnt) = $#dbinputs;
738 0         0 my ($dfltattrs, %dfltattr);
739 0         0 for ($i=0;$i<=$inputcnt;$i+=5) #SHIFT OFF LINES UNTIL RIGHT USER FOUND.
740             {
741 0 0       0 last if ($dbinputs[1] eq $dbuser);
742 0 0       0 if ($dbinputs[1] =~ s/^$dbuser\:(.*)/$dbuser/)
743             {
744 0         0 $dfltattrs = $1;
745 0         0 eval "\%dfltattr = ($dfltattrs)";
746 0         0 foreach my $j (keys %dfltattr)
747             {
748             #$attr->{$j} = $dfltattr{$j}; #CHGD. TO NEXT 20030207
749 0 0       0 $attr->{$j} = $dfltattr{$j} unless (defined $attr->{$j});
750             }
751 0         0 last;
752             }
753 0         0 for ($j=0;$j<=4;$j++)
754             {
755 0         0 shift (@dbinputs);
756             }
757             }
758             #foreach my $x (keys %{$attr}) { print STDERR "-attr($x)=$attr->{$x}=\n"; };
759 0 0       0 if ($dbinputs[1] eq $dbuser)
760             {
761             #if ($dbinputs[2] eq crypt($dbpswd, substr($dbuser,0,2)))
762 0         0 my ($crypted);
763 0         0 eval { $crypted = crypt($dbpswd, substr($dbuser,0,2)); };
  0         0  
764 0 0 0     0 if ($dbinputs[2] eq $crypted || $@ =~ /excessive paranoia/)
765             {
766 0         0 ++$DBD::Sprite::dbcnt;
767 0         0 $this->STORE('sprite_dbname',$dbname);
768 0         0 $this->STORE('sprite_dbuser',$dbuser);
769 0         0 $this->STORE('sprite_dbpswd',$dbpswd);
770 0         0 close (DBFILE);
771             #$this->STORE('sprite_autocommit',0); #CHGD TO NEXT 20010912.
772 0   0     0 $this->STORE('sprite_autocommit',($attr->{AutoCommit} || 0));
773 0         0 $this->STORE('sprite_SpritesOpen',{});
774 0         0 my ($t) = $dbinputs[0];
775 0         0 $t =~ s#(.*)/.*#$1#;
776 0 0       0 if ($dbinputs[0] =~ /(.*)(\..*)/)
777             {
778 0         0 $this->STORE('sprite_dbdir', $t);
779 0         0 $this->STORE('sprite_dbext', $2);
780             }
781             else
782             {
783 0         0 $this->STORE('sprite_dbdir', $dbinputs[0]);
784 0         0 $this->STORE('sprite_dbext', '.stb');
785             }
786 0         0 for (my $i=0;$i<=$#dbinputs;$i++)
787             {
788 0         0 $dbinputs[$i] =~ /^(.*)$/;
789 0         0 $dbinputs[$i] = $1;
790             }
791 0   0     0 $this->STORE('sprite_dbfdelim', $attr->{sprite_read} || $attr->{sprite_field} || eval("return(\"$dbinputs[3]\");") || '::');
792 0   0     0 $this->STORE('sprite_dbwdelim', $attr->{sprite_write} || $attr->{sprite_field} || eval("return(\"$dbinputs[3]\");") || '::');
793 0   0     0 $this->STORE('sprite_dbrdelim', $attr->{sprite_record} || eval("return(\"$dbinputs[4]\");") || "\n");
794 0         0 $this->STORE('sprite_attrhref', $attr);
795 0   0     0 $this->STORE('AutoCommit', ($attr->{AutoCommit} || 0));
796              
797 0   0     0 $this->STORE('sprite_autocommit',($attr->{AutoCommit} || 0));
798              
799             #NOTE: "PrintError" and "AutoCommit" are ON by DEFAULT!
800             #I KNOW OF NO WAY TO DETECT WHETHER AUTOCOMMIT IS SET BY
801             #DEFAULT OR BY USER IN "AutoCommit => 1", THEREFORE I CAN'T
802             #FORCE THE DEFAULT TO ZERO. JWT
803              
804 0         0 return $this;
805             }
806             }
807             }
808 0         0 close (DBFILE);
809             #DBI::set_err($this, -1, "Invalid username/password!"); #REPLACED W/NEXT LINE 20021021!
810 0 0       0 warn "Invalid username/password!" if ($attr->{PrintError});
811 0         0 $_ = "-1:Invalid username/password!";
812 0         0 return undef;
813             }
814              
815             sub data_sources
816             {
817 1     1   58 my ($self) = shift;
818              
819 1         6 my (@dsources) = ();
820 1         6 my $path;
821 1 50       11 if (defined $ENV{SPRITE_HOME})
822             {
823 0         0 $path = "$ENV{SPRITE_HOME}/*.sdb";
824 0         0 my $code = "while (my \$i = <$path>)\n";
825 0         0 $code .= <<'END_CODE';
826             {
827             chomp ($i);
828             push (@dsources,"DBI:Sprite:$1") if ($i =~ m#([^\/\.]+)\.sdb$#);
829             }
830             END_CODE
831 0         0 eval $code;
832 0         0 $code =~ s/\.sdb([\>\$])/\.SDB$1/g; #HANDLE WINDOWSEY FILENAMES :(
833 0         0 eval $code;
834             }
835 1         9 $path = '*.sdb';
836 1         7 my $code = "while (my \$i = <$path>)\n";
837 1         5 $code .= <<'END_CODE';
838             {
839             chomp ($i);
840             push (@dsources,"DBI:Sprite:$1") if ($i =~ m#([^\/\.]+)\.sdb$#);
841             }
842             END_CODE
843 1         318 eval $code;
844 1         37 $code =~ s/\.sdb([\>\$])/\.SDB$1/g; #HANDLE WINDOWSEY FILENAMES :(
845 1         186 eval $code;
846 1 50       11 unless (@dsources)
847             {
848 1 50       12 if (defined $ENV{HOME})
849             {
850 1         7 $path = "$ENV{HOME}/*.sdb";
851 1         7 my $code = "while (my \$i = <$path>)\n";
852 1         4 $code .= <<'END_CODE';
853             {
854             chomp ($i);
855             push (@dsources,"DBI:Sprite:$1") if ($i =~ m#([^\/\.]+)\.sdb$#);
856             }
857             END_CODE
858 1         187 eval $code;
859 1         25 $code =~ s/\.sdb([\>\$])/\.SDB$1/g; #HANDLE WINDOWSEY FILENAMES :(
860 1         179 eval $code;
861             }
862             }
863 1         18 return (@dsources);
864             }
865              
866             sub DESTROY
867             {
868 0     0   0 my($drh) = shift;
869            
870             # if ($drh->FETCH('AutoCommit') == 1) #REMOVED 20020225 TO ELIMINATE -w WARNING.
871             # {
872             # $drh->STORE('AutoCommit',0);
873             # $drh->rollback(); #COMMIT IT IF AUTOCOMMIT ON!
874             # $drh->STORE('AutoCommit',1);
875             # }
876 0         0 $drh = undef;
877             }
878              
879             sub disconnect_all
880       1     {
881             }
882              
883             sub admin { #I HAVE NO IDEA WHAT THIS DOES!
884 0     0   0 my($drh) = shift;
885 0         0 my($command) = shift;
886              
887 0 0 0     0 my($dbname) = ($command eq 'createdb' || $command eq 'dropdb') ?
888             shift : '';
889 0   0     0 my($host, $port) = DBD::Sprite->_OdbcParseHost(shift(@_) || '');
890 0   0     0 my($user) = shift || '';
891 0   0     0 my($password) = shift || '';
892              
893 0   0     0 $drh->func(undef, $command,
      0        
      0        
894             $dbname || '',
895             $host || '',
896             $port || '',
897             $user, $password, '_admin_internal');
898             }
899              
900             1;
901              
902              
903             package DBD::Sprite::db; # ====== DATABASE ======
904 1     1   6 use strict;
  1         2  
  1         29  
905 1     1   1171 use JSprite;
  1         3  
  1         49  
906              
907             $DBD::Sprite::db::imp_data_size = 0;
908 1     1   24 use vars qw($imp_data_size);
  1         2  
  1         4958  
909              
910             sub last_insert_id #MUST BE CALLED W/"$dbh->func"! ADDED 20040407 TO SUPPORT NEW DBI FUNCTION.
911             {
912 0     0   0 my ($resptr, $cat, $schema, $tablename, $seqfield) = @_;
913 0 0 0     0 return $resptr->{sprite_insertid} if (defined $resptr->{sprite_insertid} && $resptr->{sprite_insertid} =~ /\d$/);
914 0         0 my $mycsr;
915 0 0       0 if ($mycsr = $resptr->prepare("select ${seqfield}.CURRVAL from DUAL"))
916             {
917 0         0 my $myexe;
918 0 0       0 if ($myexe = $mycsr->execute())
919             {
920 0         0 my ($lastseq) = $mycsr->fetchrow_array();
921 0         0 $mycsr->finish();
922             ###return $lastseq if ($lastseq =~ /\d$/); #CHGD. TO NEXT 20061006 TO HANDLE ERRORS, IE. WHEN SEQ IS AN AUTONUMBER, NOT A SEQ!
923 0 0 0     0 return $lastseq if ($lastseq =~ /\d$/ && $lastseq > 0);
924             }
925             }
926 0 0       0 if ($seqfield) #IF ALL ELSE FAILS, FETCH A DESCENDING LIST OF VALUES FOR THE FIELD THE SEQUENCE WAS INSERTED INTO (USER MUST SPECIFY THE FIELD!)
927             {
928 0         0 my $sql = <
929             select $seqfield
930             from $tablename
931             order by $seqfield desc
932             END_SQL
933 0 0       0 if ($mycsr = $resptr->prepare($sql))
934             {
935 0         0 my $myexe;
936 0 0       0 if ($myexe = $mycsr->execute())
937             {
938 0         0 my ($lastseq) = $mycsr->fetchrow_array();
939 0         0 $mycsr->finish();
940 0         0 return $lastseq;
941             }
942             else
943             {
944 0         0 return undef;
945             }
946             }
947 0         0 return undef;
948             }
949 0         0 return undef;
950             }
951              
952             sub Statement #MUST BE CALLED W/"dbh->func([undef, undef, 'tablename', 'seq/field name',] 'last_insert_id')"! ADDED 20040407 TO SUPPORT NEW DBI FUNCTION.
953             {
954 0 0   0   0 return undef unless ($_[0]);
955 0         0 return $_[0]->FETCH('sprite_last_prepare_sql');
956             }
957              
958             sub prepare
959             {
960 0     0   0 my ($resptr, $sqlstr, $attribs) = @_;
961 0         0 my ($indx, @QS);
962 0         0 local ($_);
963             #$sqlstr =~ s/\n/ /g; #REMOVED 20011107.
964            
965             #DBI::set_err($resptr, 0, ''); #CHGD. TO NEXT 20041104.
966 0         0 DBI::set_err($resptr, undef);
967              
968 0 0       0 my $limit = ($sqlstr =~ s/^(.+)\s*limit\s+(\d+)\s*$/$1/i) ? $2 : 0; #ADDED 20160111 TO SUPPORT "limit #" ON QUERIES.
969 0         0 $sqlstr =~ s/^\s*listfields\s+(\w+)/select * from $1 where 1 = 0/i; #ADDED 20030901.
970 0         0 my $csr = DBI::_new_sth($resptr, {
971             'Statement' => $sqlstr,
972             });
973              
974 0         0 my ($spritefid);
975 0         0 $resptr->STORE('sprite_last_prepare_sql', $sqlstr);
976 0         0 $csr->STORE('sprite_fetchcnt', 0);
977 0         0 $csr->STORE('sprite_reslinev','');
978 0         0 $sqlstr =~ s/\\\'|\'\'/\x02\^3jSpR1tE\x02/gso; #PROTECT "\'" IN QUOTES.
979 0         0 $sqlstr =~ s/\\\"|\"\"/\x02\^4jSpR1tE\x02/gso; #PROTECT "\"" IN QUOTES.
980 0         0 $indx = 0;
981 0         0 $indx++ while ($sqlstr =~ s/([\'\"])([^\1]*?)\1/
982 0         0 $QS[$indx] = "$1$2"; "\$QS\[$indx]"/e);
  0         0  
983             #$sqlstr =~ /(into|from|update|table) \s*(\w+)/gi; #CHANGED 20000831 TO NEXT LINE!
984             #$sqlstr =~ /(into|from|update|table|sequence)\s+(\w+)/is; #CHGD. 20040305 TO NEXT.
985 0 0       0 $spritefid = $2 if ($sqlstr =~ /(into|from|update|table|sequence)\s+(\w+)/ios);
986 0 0       0 $spritefid = $1 if ($sqlstr =~ /primary_key_info\s+(\w+)/ios);
987 0 0       0 unless ($spritefid) #ADDED 20061010 TO SUPPORT "select fn" (like MySQL, et al.)
988             {
989 0 0       0 $spritefid = 'DUAL' if ($sqlstr =~ s/^(\s*select\s+\w+\s*)(\(.*\))?$/$1$2 from DUAL/is);
990             }
991            
992 0 0       0 unless ($spritefid) #NEXT 5 ADDED 20000831!
993             {
994 0         0 DBI::set_err($resptr, -1, "Prepare:(bad sql) Must specify a table name!");
995 0         0 return undef;
996             }
997 0 0       0 $spritefid =~ tr/A-Z/a-z/ unless ($resptr->{sprite_attrhref}->{sprite_CaseTableNames});
998 0         0 $csr->STORE('sprite_spritefid', $spritefid);
999              
1000 0         0 my $join = 0;
1001 0         0 my $joininfo;
1002             #$joininfo = $1 if ($sqlstr =~ /from\s+([\w\.\, ]+)\s*(?:where|order\s+by)/is);
1003             #$joininfo = $1 if (!$joininfo && $sqlstr =~ /from\s+([\w\.\, ]+)/is);
1004             #LAST 2 CHGD. TO NEXT 2 20040914.
1005 0 0       0 $joininfo = $1 if ($sqlstr =~ /from\s+([\w\.\,\s]+)\s*(?:where|order\s+by)/iso);
1006 0 0 0     0 $joininfo = $1 if (!$joininfo && $sqlstr =~ /from\s+([\w\.\,\s]+)/iso);
1007 0         0 my @joinfids;
1008 0 0       0 @joinfids = split(/\,\s*/o, $joininfo) if (defined $joininfo);
1009 0         0 my (@joinfid, @joinalias);
1010 0 0       0 if ($#joinfids >= 1)
1011             {
1012 0 0       0 unless ($#joinfids == 1)
1013             {
1014 0         0 DBI::set_err($resptr, -1, "Only 2-table joins currently supported!");
1015 0         0 return undef;
1016             }
1017 0         0 for (my $i=0;$i<=$#joinfids;$i++)
1018             {
1019 0         0 ($joinfid[$i], $joinalias[$i]) = split(/\s+/o, $joinfids[$i]);
1020 0   0     0 $joinfid[$i] ||= $joinfids[$i];
1021 0 0       0 $joinfid[$i] =~ tr/A-Z/a-z/ unless ($resptr->{sprite_attrhref}->{sprite_CaseTableNames});
1022             }
1023 0         0 $csr->STORE('sprite_joinfid', \@joinfid);
1024 0         0 $csr->STORE('sprite_joinalias', \@joinalias);
1025 0         0 $join = 1;
1026             }
1027             #CHECK TO SEE IF A PREVIOUSLY-CLOSED SPRITE OBJECT EXISTS FOR THIS TABLE.
1028             #IF SET, THE "RECYCLE" OPTION TELLS SPRITE NOT TO RELOAD THE TABLE DATA.
1029             #THIS IS USEFUL TO SAVE TIME AND MEMORY FOR APPS DOING MULTIPLE
1030             #TRANSACTIONS ON SEVERAL LARGE TABLES.
1031             #RELOADING IS NECESSARY, HOWEVER, IF ANOTHER USER CAN CHANGE THE
1032             #DATA SINCE YOUR LAST COMMIT, SO RECYCLE IS OFF BY DEFAULT!
1033             #THE SPRITE HANDLE AND ALL IT'S BASIC CONFIGURATION IS RECYCLED REGARDLESS.
1034 0         0 my (@spritedbs) = (qw(sprite_spritedb sprite_joindb));
1035 0         0 my ($myspriteref);
1036 0         0 my $i = 0;
1037 0         0 $myspriteref = undef;
1038 0         0 foreach my $fid ($spritefid, $joinfid[1])
1039             {
1040 0 0       0 last unless ($fid);
1041 0 0 0     0 if (ref($resptr->{'sprite_SpritesOpen'}) && ref($resptr->{'sprite_SpritesOpen'}->{$fid}))
1042             {
1043 0         0 $myspriteref = ${$resptr->{'sprite_SpritesOpen'}->{$fid}};
  0         0  
1044 0         0 $csr->STORE($spritedbs[$i], ${$resptr->{'sprite_SpritesOpen'}->{$fid}});
  0         0  
1045 0         0 $myspriteref->{TYPE} = undef;
1046 0         0 $myspriteref->{NAME} = undef;
1047 0         0 $myspriteref->{PRECISION} = undef;
1048 0         0 $myspriteref->{SCALE} = undef;
1049             }
1050             else #CREATE A NEW SPRITE OBJECT.
1051             {
1052 0         0 $myspriteref = new JSprite(%{$resptr->{sprite_attrhref}});
  0         0  
1053 0 0       0 unless ($myspriteref)
1054             {
1055 0         0 DBI::set_err($resptr, -1, "Unable to create JSprite handle ($@)!");
1056 0         0 return undef;
1057             }
1058 0         0 $csr->STORE($spritedbs[$i], $myspriteref);
1059 0         0 my ($openhash) = $resptr->FETCH('sprite_SpritesOpen');
1060 0         0 $openhash->{$fid} = \$myspriteref;
1061 0   0     0 $myspriteref->set_delimiter("-read",($attribs->{sprite_read} || $attribs->{sprite_field} || $resptr->FETCH('sprite_dbfdelim')));
1062 0   0     0 $myspriteref->set_delimiter("-write",($attribs->{sprite_write} || $attribs->{sprite_field} || $resptr->FETCH('sprite_dbwdelim')));
1063 0   0     0 $myspriteref->set_delimiter("-record",($attribs->{sprite_record} || $attribs->{sprite_field} || $resptr->FETCH('sprite_dbrdelim')));
1064 0         0 $myspriteref->set_db_dir($resptr->FETCH('sprite_dbdir'));
1065 0         0 $myspriteref->set_db_ext($resptr->FETCH('sprite_dbext'));
1066 0         0 $myspriteref->{CaseTableNames} = $resptr->{sprite_attrhref}->{sprite_CaseTableNames};
1067 0         0 $myspriteref->{sprite_CaseFieldNames} = $resptr->{sprite_attrhref}->{sprite_CaseFieldNames};
1068 0         0 $myspriteref->{StrictCharComp} = $resptr->{sprite_attrhref}->{sprite_StrictCharComp};
1069             #DON'T NEED!#$myspriteref->{Crypt} = $resptr->{sprite_attrhref}->{sprite_Crypt}; #ADDED 20020109.
1070 0         0 $myspriteref->{sprite_forcereplace} = $resptr->{sprite_attrhref}->{sprite_forcereplace}; #ADDED 20010912.
1071 0         0 $myspriteref->{dbuser} = $resptr->FETCH('sprite_dbuser'); #ADDED 20011026.
1072 0         0 $myspriteref->{dbname} = $resptr->FETCH('sprite_dbname'); #ADDED 20011026.
1073 0         0 $myspriteref->{dbhandle} = $resptr; #ADDED 20020516
1074             }
1075 0         0 $myspriteref->{LongTruncOk} = $resptr->FETCH('LongTruncOk');
1076 0         0 my ($silent) = $resptr->FETCH('PrintError');
1077 0 0       0 $myspriteref->{silent} = ($silent ? 0 : 1); #ADDED 20000103 TO SUPPRESS "OOPS" MSG ON WEBSITES!
1078 0 0       0 $myspriteref->{sprite_reclimit} = (defined $attribs->{sprite_reclimit}) ? $attribs->{sprite_reclimit} : 0; #ADDED 20020123.
1079 0 0       0 $myspriteref->{sprite_sizelimit} = (defined $attribs->{sprite_sizelimit}) ? $attribs->{sprite_sizelimit} : 0; #ADDED 20020530.
1080 0         0 $myspriteref->{sprite_actlimit} = $limit; #ADDED 20160111 TO SUPPORT "limit #" ON QUERIES.
1081 0         0 ++$i;
1082             }
1083              
1084             #PARSE OUT SQL IF JOIN.
1085              
1086 0         0 my $num_of_params;
1087             my @bindindices;
1088 0         0 my @joinsql;
1089 0 0       0 if ($join)
1090             {
1091 0         0 my ($whereclause, $joinfid);
1092 0         0 my %addfields; #FIELDS IN UNION CRITERIA THAT MUST BE ADDED TO FETCH.
1093 0         0 my @selectfields; #FIELD NAMES OF FIELDS TO BE FETCHED.
1094 0         0 my $addthesefields; #COLLECT LIST OF FIELDS THAT ACTUALLY NEED ADDING.
1095 0         0 my @union; #LIST OF FIELDS IN THE JOIN UNION(S).
1096 0         0 my $listprefix;
1097              
1098 0         0 for (my $jj=0;$jj<=1;$jj++)
1099             {
1100 0         0 $joinsql[$jj] = $sqlstr;
1101 0 0       0 $joinfid = $joinalias[$jj] ? $joinalias[$jj] : $joinfid[$jj];
1102 0         0 %addfields = ();
1103              
1104 0         0 $joinsql[$jj] =~ s/^\s+//gso; #STRIP LEADING, TRAILING SPACES.
1105 0         0 $joinsql[$jj] =~ s/\s+$//gso;
1106              
1107             #CONVERT ALL "jointable.fieldname" to "fieldname" & REMOVE ALL "othertables.fieldname".
1108              
1109 0         0 $joinsql[$jj] =~ s!^\s*select(?:\s*distinct)?\s+(.+)\s+from\s+!
1110 0         0 my $one = $1;
1111 0         0 $one =~ s/$joinfid\.//g;
1112 0         0 $one =~ s/\w+\.\w+(?:\s*\,)?//go;
1113 0         0 $one =~ s/\,\s*$//o;
1114 0         0 "select $one from "
1115             !eis;
1116              
1117 0 0       0 $whereclause = $1 if ($joinsql[$jj] =~ s/\s+where\s+(.+)$/ /iso);
1118             # $csr->STORE("sprite_where0", $whereclause) unless ($jj);
1119 0 0       0 unless ($jj)
1120             {
1121 0         0 my $unprotectedWhere = $whereclause;
1122 0 0       0 if ($whereclause =~ /\S/o)
1123             {
1124             #RESTORE QUOTED STRINGS AND ESCAPED QUOTES WITHIN THEM.
1125 0         0 1 while ($unprotectedWhere =~ s/\$QS\[(\d+)\]/
1126 0         0 my $one = $1;
1127 0         0 my $quotechar = substr($QS[$one],0,1);
1128 0         0 ($quotechar.substr($QS[$one],1).$quotechar)
1129             /es);
1130 0         0 $unprotectedWhere =~ s/\x02\^4jSpR1tE\x02/\"\"/gso; #UNPROTECT QUOTES WITHIN QUOTES!
1131 0         0 $unprotectedWhere =~ s/\x02\^3jSpR1tE\x02/\'\'/gso;
1132             }
1133 0         0 $csr->STORE("sprite_where0", $unprotectedWhere);
1134             }
1135              
1136              
1137             # $whereclause =~ s/([\'\"])([^\1]*?)\1//g; #STRIP OUT QUOTED STRINGS TO PREVENT INTERFEARANCE W/OTHER REGICES.
1138 0 0       0 $_ = $1 if ($joinsql[$jj] =~ /select\s+(.+?)\s+from\s+/o);
1139 0         0 s/\s+//go;
1140 0         0 @selectfields = split(/\,/o, $_);
1141              
1142             #DEAL WITH THE ORDER-BY CLAUSE, IF ANY.
1143              
1144 0 0 0     0 if ($whereclause =~ s/\s+order\s+by\s*(.*)//iso || $joinsql[$jj] =~ s/\s+order\s+by\s*(.*)//iso)
1145             {
1146 0         0 my $ordbyclause = $1;
1147 0 0       0 if ($jj)
1148             {
1149 0         0 $ordbyclause =~ s/(?:$joinalias[0]|$joinfid[0])\.\w+(?:\s+desc)?//gis;
1150             }
1151             else
1152             {
1153 0 0       0 $csr->STORE('sprite_joinorder', (
1154             ($ordbyclause =~ /^(?:$joinalias[1]|$joinfid[1])\./)
1155             ? 1 : 0));
1156 0         0 $ordbyclause =~ s/(?:$joinalias[1]|$joinfid[1])\.\w+(?:\s+desc)?\s*\,?//gis;
1157             }
1158 0         0 $ordbyclause =~ s/\w+\.(\w+)/$1/gs;
1159 0         0 $ordbyclause =~ s/\,\s*$//so;
1160 0         0 $ordbyclause =~ s/^\s*\,//so;
1161 0 0       0 $joinsql[$jj] .= " order by $ordbyclause" if ($ordbyclause =~ /\S/o);
1162             }
1163            
1164             #ADD ANY FIELDS IN WHERE-CLAUSE BUT NOT FETCHED (WE MUST FETCH THEM)!
1165 0         0 @union = ();
1166 0         0 while ($whereclause =~ s/$joinfid\.(\w+)//is)
1167             {
1168 0         0 $addfields{$1} = 1;
1169 0         0 push (@union, "$joinfid.$1");
1170             }
1171 0         0 $csr->STORE("sprite_union$jj", [@union]);
1172 0         0 $joinsql[$jj] =~ s/$joinfid\.(\w+)/$1/gs;
1173             # unless ($whereclause)
1174             # {
1175             # DBI::set_err($resptr, -1, 'Join queries require "where"-clause!');
1176             # return undef;
1177             # }
1178              
1179             #REMOVE THE OTHER TABLES FROM THE FROM CLAUSE.
1180              
1181             #$joinsql[$jj] =~ s!\s+from\s+(\w+.*?)(\s+where.*)?$!" from $joinfid[$jj] $2"!egs;
1182 0         0 $joinsql[$jj] =~ s!\s+from\s+(\w+.*?)(\s+(?:where|order\s+by).*)?$!" from $joinfid[$jj] $2"!egs;
  0         0  
1183              
1184             #APPEND UNION FIELDS FROM JOINTABLE NOT IN SELECT LIST TO SELECT LIST.
1185              
1186 0         0 $addthesefields = '';
1187 0         0 $listprefix = '';
1188 0 0       0 unless ($selectfields[0] eq '*')
1189             {
1190 0         0 outer: foreach my $j (keys %addfields)
1191             {
1192 0         0 for (my $k=0;$k<=$#selectfields;$k++)
1193             {
1194 0 0       0 next outer if ($selectfields[$k] eq $j);
1195             # $listprefix = ','; #REMOVED 20040913
1196             }
1197             #$addthesefields .= $listprefix . $j; #CHGD. TO NEXT 20040913
1198 0         0 $addthesefields .= $listprefix . $j . ',';
1199             }
1200 0         0 $addthesefields =~ s/\,$//o;
1201             #$joinsql[$jj] =~ s/\s+from\s+/ $addthesefields from /; #CHGD. TO NEXT IF-STMT. 20040929.
1202 0 0       0 if ($addthesefields)
1203             {
1204 0 0       0 ($joinsql[$jj] =~ s/^\s*select\s+from\s+$joinfid[$jj]/select $addthesefields from $joinfid[$jj]/is)
1205             or
1206             ($joinsql[$jj] =~ s/\s+from\s+$joinfid[$jj]/,$addthesefields from $joinfid[$jj]/is);
1207             }
1208             }
1209             #$csr->STORE("sprite_bi$jj", $bindindices[$jj]);
1210 0         0 $csr->STORE("sprite_joinnops$jj", 0);
1211              
1212             #RESTORE QUOTED STRINGS AND ESCAPED QUOTES WITHIN THEM.
1213 0         0 1 while ($joinsql[$jj] =~ s/\$QS\[(\d+)\]/
1214 0         0 my $one = $1;
1215 0         0 my $quotechar = substr($QS[$one],0,1);
1216 0         0 ($quotechar.substr($QS[$one],1).$quotechar)
1217             /es);
1218 0         0 $joinsql[$jj] =~ s/\x02\^4jSpR1tE\x02/\"\"/gso; #UNPROTECT QUOTES WITHIN QUOTES!
1219 0         0 $joinsql[$jj] =~ s/\x02\^3jSpR1tE\x02/\'\'/gso;
1220 0         0 $csr->STORE("sprite_joinstmt$jj", $joinsql[$jj]);
1221             }
1222 0         0 $csr->STORE('sprite_joinparams', []);
1223             }
1224             else
1225             {
1226 0         0 $sqlstr =~ s/select\s+(.*?)\s+from\s+(\w+)\s+(\w+)\s+(where\s+.+|order\s+.+)?$/
1227 0         0 my ($one, $two, $three, $four) = ($1, $2, $3, $4);
1228 0         0 $one =~ s|\b$three\.(\w)|$1|g;
1229 0         0 $four =~ s|\b$three\.(\w)|$1|g;
1230 0         0 "select $one from $two $four"
1231             /eis;
1232             }
1233            
1234             #SET UP STMT. PARAMETERS.
1235            
1236 0         0 $csr->STORE('sprite_params', []);
1237 0         0 $num_of_params = ($sqlstr =~ tr/\?//);
1238 0         0 $sqlstr =~ s/\x02\^2jSpR1tE\x02/\?/gso;
1239 0         0 $csr->STORE('NUM_OF_PARAMS', $num_of_params);
1240 0 0       0 $sqlstr = $joinsql[0] if ($joinsql[0]);
1241              
1242             #RESTORE QUOTED STRINGS.
1243 0         0 1 while ($sqlstr =~ s/\$QS\[(\d+)\]/
1244 0         0 my $one = $1;
1245 0         0 my $quotechar = substr($QS[$one],0,1);
1246 0         0 ($quotechar.substr($QS[$one],1).$quotechar)
1247             /es);
1248             #$sqlstr =~ s/\x02\^3jSpR1tE\x02/\"\"/gs; #BUGFIX: CHGD NEXT 2 TO FOLLOWING 2 20050429.
1249             #$sqlstr =~ s/\x02\^2jSpR1tE\x02/\'\'/gs;
1250 0         0 $sqlstr =~ s/\x02\^4jSpR1tE\x02/\"\"/gso; #UNPROTECT QUOTES WITHIN QUOTES!
1251 0         0 $sqlstr =~ s/\x02\^3jSpR1tE\x02/\'\'/gso;
1252 0         0 $csr->STORE('sprite_statement', $sqlstr);
1253 0         0 return ($csr);
1254             }
1255              
1256             sub parseParins #RECURSIVELY ASSIGN ALL PARENTHAASZED EXPRESSIONS TO AN ARRAY TO PROTECT FROM OTHER REGICES.
1257             {
1258 0     0   0 my ($T, $tindx, $s) = @_;
1259              
1260 0         0 $tindx++ while ($s =~ s/\(([^\(\)]+)\)/
1261 0         0 $T->[$tindx] = &parseParins($T, $tindx, $1);
1262 0         0 "\$T\[$tindx]"
1263             /e);
1264 0         0 return $s;
1265             }
1266              
1267             sub commit
1268             {
1269 0     0   0 my ($dB) = shift;
1270              
1271 0 0 0     0 if ($dB->FETCH('AutoCommit') && $dB->FETCH('Warn'))
1272             {
1273 0         0 warn ('Commit ineffective while AutoCommit is ON!');
1274 0         0 return 1;
1275             }
1276 0         0 my ($commitResult) = 1; #ADDED 20000103
1277              
1278 0         0 foreach (keys %{$dB->{sprite_SpritesOpen}})
  0         0  
1279             {
1280 0 0       0 next unless (defined($dB->{'sprite_SpritesOpen'}->{$_}));
1281 0 0       0 next if (/^(USER|ALL)_TABLES$/i);
1282 0 0       0 next unless (defined(${$dB->{'sprite_SpritesOpen'}->{$_}}));
  0         0  
1283 0         0 $commitResult = ${$dB->{'sprite_SpritesOpen'}->{$_}}->commit($_);
  0         0  
1284 0 0 0     0 return undef if (!defined($commitResult) || $commitResult <= 0);
1285             }
1286 0         0 return 1;
1287             }
1288              
1289             sub rollback
1290             {
1291 0     0   0 my ($dB) = shift;
1292              
1293 0 0 0     0 if (!shift && $dB->FETCH('AutoCommit') && $dB->FETCH('Warn'))
      0        
1294             {
1295 0         0 warn ('Rollback ineffective while AutoCommit is ON!');
1296 0         0 return 1;
1297             }
1298            
1299 0         0 foreach my $s (keys %{$dB->{sprite_SpritesOpen}})
  0         0  
1300             {
1301 0 0       0 next unless (defined($dB->{'sprite_SpritesOpen'}->{$s}));
1302 0 0       0 next if ($s =~ /^(USER|ALL)_TABLES$/i);
1303 0 0       0 next unless (defined(${$dB->{'sprite_SpritesOpen'}->{$s}}));
  0         0  
1304 0         0 ${$dB->{'sprite_SpritesOpen'}->{$s}}->rollback($s);
  0         0  
1305             }
1306 0         0 return 1;
1307             }
1308              
1309             sub STORE
1310             {
1311 0     0   0 my($dbh, $attr, $val) = @_;
1312 0 0       0 if ($attr eq 'AutoCommit')
1313             {
1314             # AutoCommit is currently the only standard attribute we have
1315             # to consider.
1316              
1317 0 0 0     0 $dbh->commit() if ($val == 1 && !$dbh->FETCH('AutoCommit'));
1318 0         0 $dbh->{AutoCommit} = $val;
1319 0         0 return 1;
1320             }
1321 0 0       0 if ($attr =~ /^sprite/o)
1322             {
1323             # Handle only our private attributes here
1324             # Note that we could trigger arbitrary actions.
1325             # Ideally we should catch unknown attributes.
1326 0         0 $dbh->{$attr} = $val; # Yes, we are allowed to do this,
1327 0         0 return 1; # but only for our private attributes
1328             }
1329             # Else pass up to DBI to handle for us
1330 0         0 $dbh->SUPER::STORE($attr, $val);
1331             }
1332              
1333             sub FETCH
1334             {
1335 1     1   3 my($dbh, $attr) = @_;
1336 1 50       4 if ($attr eq 'AutoCommit') { return $dbh->{AutoCommit}; }
  1         16  
1337 0 0       0 if ($attr =~ /^sprite_/o)
1338             {
1339             # Handle only our private attributes here
1340             # Note that we could trigger arbitrary actions.
1341 0         0 return $dbh->{$attr}; # Yes, we are allowed to do this,
1342             # but only for our private attributes
1343 0         0 return $dbh->{$attr};
1344             }
1345             # Else pass up to DBI to handle
1346 0         0 $dbh->SUPER::FETCH($attr);
1347             }
1348              
1349             sub disconnect
1350             {
1351 0     0   0 my ($db) = shift;
1352            
1353             #DBI::set_err($db, 0, ''); #CHGD. TO NEXT 20041104.
1354 0         0 DBI::set_err($db, undef);
1355 0         0 return (1); #20000114: MAKE WORK LIKE DBI!
1356             }
1357              
1358             sub do
1359             {
1360 0     0   0 my ($dB, $sqlstr, $attr, @bind_values) = @_;
1361 0 0       0 my ($csr) = $dB->prepare($sqlstr, $attr) or return undef;
1362              
1363             #DBI::set_err($dB, 0, ''); #CHGD. TO NEXT 20041104.
1364 0         0 DBI::set_err($dB, undef);
1365            
1366             #my $retval = $csr->execute(@bind_values) || undef;
1367 0   0     0 return ($csr->execute(@bind_values) || undef);
1368             }
1369              
1370             sub table_info
1371             {
1372 0     0   0 my($dbh) = @_; # XXX add qualification
1373 0 0       0 my $sth = $dbh->prepare('select TABLE_NAME from USER_TABLES')
1374             or return undef;
1375 0 0       0 $sth->execute or return undef;
1376 0         0 return $sth;
1377             }
1378              
1379             sub primary_key_info #ADDED 20060613 TO SUPPORT DBI primary_key/primary_key_info FUNCTIONS!
1380             {
1381 0     0   0 my ($dbh, $cat, $schema, $tablename) = @_;
1382 0 0       0 my $sth = $dbh->prepare("PRIMARY_KEY_INFO $tablename")
1383             or return undef;
1384 0 0       0 $sth->execute() or return undef;
1385 0         0 return $sth;
1386             }
1387              
1388             sub type_info_all #ADDED 20010312, BORROWED FROM "Oracle.pm".
1389             {
1390 0     0   0 my ($dbh) = @_;
1391 0         0 my $names =
1392             {
1393             TYPE_NAME => 0,
1394             DATA_TYPE => 1,
1395             COLUMN_SIZE => 2,
1396             LITERAL_PREFIX => 3,
1397             LITERAL_SUFFIX => 4,
1398             CREATE_PARAMS => 5,
1399             NULLABLE => 6,
1400             CASE_SENSITIVE => 7,
1401             SEARCHABLE => 8,
1402             UNSIGNED_ATTRIBUTE => 9,
1403             FIXED_PREC_SCALE =>10,
1404             AUTO_UNIQUE_VALUE =>11,
1405             LOCAL_TYPE_NAME =>12,
1406             MINIMUM_SCALE =>13,
1407             MAXIMUM_SCALE =>14,
1408             }
1409             ;
1410             # Based on the values from Oracle 8.0.4 ODBC driver
1411 0         0 my $ti = [
1412             $names,
1413             [ 'LONG RAW', -4, '2147483647', '\'', '\'', undef, 1, '0', '0',
1414             undef, '0', undef, undef, undef, undef
1415             ],
1416             [ 'RAW', -2, 255, '\'', '\'', 'max length', 1, '0', 3,
1417             undef, '0', undef, undef, undef, undef
1418             ],
1419             [ 'LONG', -1, '2147483647', '\'', '\'', undef, 1, 1, '0',
1420             undef, '0', undef, undef, undef, undef
1421             ],
1422             [ 'CHAR', 1, 255, '\'', '\'', 'max length', 1, 1, 3,
1423             undef, '0', '0', undef, undef, undef
1424             ],
1425             [ 'NUMBER', 3, 38, undef, undef, 'precision,scale', 1, '0', 3,
1426             '0', '0', '0', undef, '0', 38
1427             ],
1428             [ 'AUTONUMBER', 4, 38, undef, undef, 'precision,scale', 1, '0', 3,
1429             '0', '0', '0', undef, '0', 38
1430             ],
1431             [ 'DOUBLE', 8, 15, undef, undef, undef, 1, '0', 3,
1432             '0', '0', '0', undef, undef, undef
1433             ],
1434             [ 'DATE', 11, 19, '\'', '\'', undef, 1, '0', 3,
1435             undef, '0', '0', undef, '0', '0'
1436             ],
1437             [ 'VARCHAR2', 12, 2000, '\'', '\'', 'max length', 1, 1, 3,
1438             undef, '0', '0', undef, undef, undef
1439             ]
1440             ];
1441 0         0 return $ti;
1442             }
1443             sub tables #CONVENIENCE METHOD FOR FETCHING LIST OF TABLES IN THE DATABASE.
1444             {
1445 0     0   0 my($dbh) = @_; # XXX add qualification
1446              
1447 0         0 my $sth = $dbh->table_info();
1448            
1449 0 0       0 return undef unless ($sth);
1450            
1451 0         0 my ($row, @tables);
1452            
1453 0         0 while ($row = $sth->fetchrow_arrayref())
1454             {
1455 0         0 push (@tables, $row->[0]);
1456             }
1457 0         0 $sth->finish();
1458 0 0       0 return undef unless ($#tables >= 0);
1459 0         0 return (@tables);
1460             }
1461              
1462             sub rows
1463             {
1464 0     0   0 return $DBI::rows;
1465             }
1466              
1467             sub DESTROY #ADDED 20001108
1468             {
1469 1     1   2 my($drh) = shift;
1470            
1471 1 50       18 if ($drh->FETCH('AutoCommit') == 1)
1472             {
1473 0         0 $drh->STORE('AutoCommit',0);
1474 0         0 $drh->rollback(); #COMMIT IT IF AUTOCOMMIT ON!
1475 0         0 $drh->STORE('AutoCommit',1);
1476             }
1477 1         31 $drh = undef;
1478             }
1479              
1480             1;
1481              
1482              
1483             package DBD::Sprite::st; # ====== STATEMENT ======
1484 1     1   9 use strict;
  1         1  
  1         110  
1485              
1486             my (%typehash) = (
1487             'LONG RAW' => -4,
1488             'RAW' => -3,
1489             'LONG' => -1,
1490             'CHAR' => 1,
1491             'NUMBER' => 3,
1492             'AUTONUMBER' => 4,
1493             'DOUBLE' => 8,
1494             'DATE' => 11,
1495             'VARCHAR' => 12,
1496             'VARCHAR2' => 12,
1497             'BOOLEAN' => -7, #ADDED 20000308!
1498             'BLOB' => 113, #ADDED 20020110!
1499             'MEMO' => -1, #ADDED 20020110!
1500              
1501             'DATE' => 9,
1502             'REAL' => 7,
1503             'TINYINT' => -6,
1504             'NCHAR' => -8,
1505             'NVARCHAR' => -9,
1506             'NTEXT' => -10,
1507             'SMALLDATETIME' => 93,
1508             'BIGINT' => -5,
1509             'DECIMAL' => 3,
1510             'INTEGER' => 4,
1511             );
1512              
1513             $DBD::Sprite::st::imp_data_size = 0;
1514 1     1   4 use vars qw($imp_data_size *fetch);
  1         2  
  1         4321  
1515              
1516             sub bind_param
1517             {
1518 0     0     my($sth, $pNum, $val, $attr) = @_;
1519 0 0         my $type = (ref $attr) ? $attr->{TYPE} : $attr;
1520              
1521 0 0         if ($type)
1522             {
1523 0           my $dbh = $sth->{Database};
1524 0           $val = $dbh->quote($val, $type);
1525 0           $val =~ s/^\'//o;
1526 0           $val =~ s/\'$//o;
1527             }
1528 0           my $params = $sth->FETCH('sprite_params');
1529 0           $params->[$pNum-1] = $val;
1530              
1531             #${$sth->{bindvars}}[($pNum-1)] = $val; #FOR SPRITE. #REMOVED 20010312 (LVALUE NOT FOUND ANYWHERE ELSE).
1532              
1533 0           $sth->STORE('sprite_params', $params);
1534 0           return 1;
1535             }
1536              
1537             sub execute
1538             {
1539 0     0     my ($sth, @bind_values) = @_;
1540 0 0         my $params = (@bind_values) ? \@bind_values : $sth->FETCH('sprite_params');
1541 0           my @ocolnames;
1542 0           for (my $i=0;$i<=$#{$params};$i++) #ADDED 20000303 FIX QUOTE PROBLEM WITH BINDS.
  0            
1543             {
1544 0           $params->[$i] =~ s/\'/\'\'/go;
1545             }
1546 0           my $numParam = $sth->FETCH('NUM_OF_PARAMS');
1547              
1548 0 0 0       if ($params && scalar(@$params) != $numParam) #CHECK FOR RIGHT # PARAMS.
1549             {
1550 0           DBI::set_err($sth, (scalar(@$params)-$numParam),
1551             "..execute: Wrong number of bind variables (".(scalar(@$params)-$numParam)
1552             ." too many!)");
1553 0           return undef;
1554             }
1555             #my $sqlstr = $sth->{'Statement'}; #CHGD. TO NEXT 20040205 TO PERMIT JOINS.
1556 0           my $sqlstr = $sth->FETCH('sprite_statement');
1557             #NEXT 8 LINES ADDED 20010911 TO FIX BUG WHEN QUOTED VALUES CONTAIN "?"s.
1558 0           $sqlstr =~ s/\\\'/\x02\^3jSpR1tE\x02/gso; #PROTECT ESCAPED DOUBLE-QUOTES.
1559 0           $sqlstr =~ s/\'\'/\x02\^4jSpR1tE\x02/gso; #PROTECT DOUBLED DOUBLE-QUOTES.
1560 0           $sqlstr =~ s/\'([^\']*?)\'/
1561 0           my ($str) = $1;
1562 0           $str =~ s|\?|\x02\^2jSpR1tE\x02|gs; #PROTECT QUESTION-MARKS WITHIN QUOTES.
1563 0           "'$str'"/egs;
1564 0           $sqlstr =~ s/\x02\^4jSpR1tE\x02/\'\'/gso; #UNPROTECT DOUBLED DOUBLE-QUOTES.
1565 0           $sqlstr =~ s/\x02\^3jSpR1tE\x02/\\\'/gso; #UNPROTECT ESCAPED DOUBLE-QUOTES.
1566              
1567             #CONVERT REMAINING QUESTION-MARKS TO BOUND VALUES.
1568              
1569             # my $bindindices = $sth->FETCH('sprite_bi0') || [0..($numParam-1)];
1570             # foreach my $i (@$bindindices)
1571 0           for (my $i = 0; $i < $numParam; $i++)
1572             {
1573 0           $params->[$i] =~ s/\?/\x02\^2jSpR1tE\x02/gso; #ADDED 20001023 TO FIX BUG WHEN PARAMETER OTHER THAN LAST CONTAINS A "?"!
1574 0           $sqlstr =~ s/\?/"'".$params->[$i]."'"/es;
  0            
1575             }
1576 0           $sqlstr =~ s/\x02\^2jSpR1tE\x02/\?/gso; #ADDED 20001023! - UNPROTECT PROTECTED "?"s.
1577 0           my ($spriteref) = $sth->FETCH('sprite_spritedb');
1578              
1579             #CALL JSPRITE TO DO THE SQL!
1580 0           my (@resv) = $spriteref->sql($sqlstr);
1581             #!!! HANDLE SPRITE ERRORS HERE (SEE SPRITE.PM)!!!
1582 0           my ($retval) = undef;
1583 0 0         if ($#resv < 0) #GENERAL ERROR!
    0          
1584             {
1585             DBI::set_err($sth, ($spriteref->{lasterror} || -601),
1586 0   0       ($spriteref->{lastmsg} || 'Unknown Error!'));
      0        
1587 0           return $retval;
1588             }
1589             elsif ($resv[0]) #NORMAL ACTION IF NON SELECT OR >0 ROWS SELECTED.
1590             {
1591 0           $retval = $resv[0];
1592 0           my $dB = $sth->{Database};
1593             #if ($dB->FETCH('AutoCommit') == 1 && $sth->FETCH('Statement') !~ /^\s*select/i) #CHGD. TO NEXT 20040205 TO PERMIT JOINS.
1594 0 0         if ($sth->FETCH('sprite_statement') !~ /^\s*(?:select|primary_key_info)/io)
1595             {
1596 0 0         if ($dB->FETCH('AutoCommit') == 1)
1597             {
1598 0 0         $retval = undef unless ($spriteref->commit()); #ADDED 20010911 TO MAKE AUTOCOMMIT WORK (OOPS :( )
1599             #$dB->STORE('AutoCommit',1); #COMMIT DONE HERE!
1600             }
1601             }
1602             else
1603             {
1604             #OCOL* = ORIGINAL SQL.
1605             #ICOL* = BASE SQL.
1606             #JCOL* = JOIN SQL.
1607 0           $sqlstr = $sth->FETCH('sprite_joinstmt1');
1608 0 0         if ($sqlstr)
1609             {
1610 0           $sqlstr =~ s/\\\'/\x02\^3jSpR1tE\x02/gso; #PROTECT ESCAPED DOUBLE-QUOTES.
1611 0           $sqlstr =~ s/\'\'/\x02\^4jSpR1tE\x02/gso; #PROTECT DOUBLED DOUBLE-QUOTES.
1612 0           $sqlstr =~ s/\'([^\']*?)\'/
1613 0           my ($str) = $1;
1614 0           $str =~ s|\?|\x02\^2jSpR1tE\x02|gso; #PROTECT QUESTION-MARKS WITHIN QUOTES.
1615 0           "'$str'"/egs;
1616 0           $sqlstr =~ s/\x02\^4jSpR1tE\x02/\'\'/gso; #UNPROTECT DOUBLED DOUBLE-QUOTES.
1617 0           $sqlstr =~ s/\x02\^3jSpR1tE\x02/\\\'/gso; #UNPROTECT ESCAPED DOUBLE-QUOTES.
1618              
1619             #CONVERT REMAINING QUESTION-MARKS TO BOUND VALUES.
1620              
1621             #!!! my $bindindices = $sth->FETCH('sprite_bi1');
1622             # foreach my $i (@$bindindices)
1623             # {
1624             # $params->[$i] =~ s/\?/\x02\^2jSpR1tE\x02/gs; #ADDED 20001023 TO FIX BUG WHEN PARAMETER OTHER THAN LAST CONTAINS A "?"!
1625             # $sqlstr =~ s/\?/"'".$params->[$i]."'"/es;
1626             # }
1627 0           $sqlstr =~ s/\x02\^2jSpR1tE\x02/\?/gso; #ADDED 20001023! - UNPROTECT PROTECTED "?"s.
1628 0           my @icolnames = split(/\,/o, $spriteref->{use_fields});
1629 0           my %icolHash;
1630 0           for (my $i=0;$i<=$#icolnames;$i++)
1631             {
1632 0           $icolHash{$icolnames[$i]} = $i;
1633             }
1634 0           my $origsql = $sth->FETCH('Statement');
1635 0           $origsql =~ s/select\s+(.+)?\s+from\s+.+$/$1/is;
1636 0           $origsql =~ s/\s+//g;
1637 0           my $joinfids = $sth->FETCH('sprite_joinfid');
1638 0           my $joinalii = $sth->FETCH('sprite_joinalias');
1639             # unless ($spriteref->{sprite_CaseFieldNames}) #CHGD. TO NEXT 20040929.
1640 0 0         $origsql =~ tr/a-z/A-Z/ unless ($spriteref->{sprite_CaseFieldNames});
1641 0 0         unless ($spriteref->{sprite_CaseTableNames})
1642             {
1643 0           for (my $i=0;$i<=$#{$joinfids};$i++)
  0            
1644             {
1645 0           $joinfids->[$i] =~ tr/a-z/A-Z/;
1646 0           $joinalii->[$i] =~ tr/a-z/A-Z/;
1647             }
1648            
1649             }
1650             #CALL JSPRITE TO DO THE SQL!
1651              
1652 0           my $joinspriteref = $sth->FETCH('sprite_joindb');
1653 0           my (@joinresv) = $joinspriteref->sql($sqlstr);
1654 0           my $joinunion0 = $sth->FETCH('sprite_union0');
1655              
1656             #BUILD ARRAYS OF INDICES FOR UNION FIELDS TO BE COMPARED.
1657 0           my @icolindx;
1658 0           for (my $i=0;$i<=$#{$joinunion0};$i++)
  0            
1659             {
1660 0           $joinunion0->[$i] =~ s/[^\.]*\.(.*)/$1/;
1661             $joinunion0->[$i] =~ tr/a-z/A-Z/
1662 0 0         unless ($joinspriteref->{sprite_CaseFieldNames});
1663 0           for (my $j=0;$j<=$#icolnames;$j++)
1664             {
1665 0 0         if ($joinunion0->[$i] eq $icolnames[$j])
1666             {
1667 0           push (@icolindx, $j);
1668 0           last;
1669             }
1670             }
1671             }
1672 0           my $joinunion1 = $sth->FETCH('sprite_union1');
1673 0           my @jcolnames = split(/\,/o, $joinspriteref->{use_fields});
1674 0           my %jcolHash;
1675 0           for (my $i=0;$i<=$#jcolnames;$i++)
1676             {
1677 0           $jcolHash{$jcolnames[$i]} = $i;
1678             }
1679 0           my @jcolindx;
1680 0           for (my $i=0;$i<=$#{$joinunion1};$i++)
  0            
1681             {
1682 0           $joinunion1->[$i] =~ s/[^\.]*\.(.*)/$1/;
1683             $joinunion1->[$i] =~ tr/a-z/A-Z/
1684 0 0         unless ($joinspriteref->{sprite_CaseFieldNames});
1685 0           for (my $j=0;$j<=$#jcolnames;$j++)
1686             {
1687 0 0         if ($joinunion1->[$i] eq $jcolnames[$j])
1688             {
1689 0           push (@jcolindx, $j);
1690 0           last;
1691             }
1692             }
1693             }
1694 0           @ocolnames = split(/\,/o, $origsql);
1695 0           my ($tbl,$fld);
1696 0           my (@ocolwhich, %newtypes, %newlens, %newscales);
1697              
1698 0           I1: for (my $i=0;$i<=$#ocolnames;$i++)
1699             {
1700 0           ($tbl,$fld) = split(/\./o, $ocolnames[$i]);
1701 0           $ocolnames[$i] = $fld;
1702 0 0 0       if ($tbl eq $joinfids->[1] || $tbl eq $joinalii->[1])
1703             {
1704 0           $ocolwhich[$i] = 1;
1705 0           for (my $j=0;$j<=$#jcolindx;$j++)
1706             {
1707 0 0         if ($fld eq $jcolnames[$j])
1708             {
1709 0           $newtypes{$fld} = ${$joinspriteref->{types}}{$fld};
  0            
1710 0           $newlens{$fld} = ${$joinspriteref->{lengths}}{$fld};
  0            
1711 0           $newscales{$fld} = ${$joinspriteref->{scales}}{$fld};
  0            
1712 0           next I1;
1713             }
1714             }
1715             }
1716             else
1717             {
1718 0           $ocolwhich[$i] = 0;
1719 0           for (my $j=0;$j<=$#icolindx;$j++)
1720             {
1721 0 0         if ($fld eq $icolnames[$j])
1722             {
1723 0           $newtypes{$fld} = ${$spriteref->{types}}{$fld};
  0            
1724 0           $newlens{$fld} = ${$spriteref->{lengths}}{$fld};
  0            
1725 0           $newscales{$fld} = ${$spriteref->{scales}}{$fld};
  0            
1726 0           next I1;
1727             }
1728             }
1729             }
1730             }
1731 0           %{$spriteref->{types}} = %newtypes;
  0            
1732 0           %{$spriteref->{lengths}} = %newlens;
  0            
1733 0           %{$spriteref->{scales}} = %newscales;
  0            
1734 0           $spriteref->{TYPE} = undef;
1735 0           my $jrow = shift(@joinresv);
1736 0           my $row = shift(@resv);
1737 0           my $orig_whereclause = $sth->FETCH('sprite_where0');
1738 0           $orig_whereclause =~ s/\s+order\s+by\s+[\w\,\.\s]+$//is;
1739 0   0       my @tblname = (($joinalii->[0] || $joinfids->[0]),
      0        
1740             ($joinalii->[1] || $joinfids->[1]));
1741 0           my $validColumnnames = "(?:$tblname[0].".$spriteref->{use_fields};
1742 0           $validColumnnames =~ s/\,/\|$tblname[0]\./g;
1743 0           $validColumnnames .= "|$tblname[1].".$joinspriteref->{use_fields}.')';
1744 0           $validColumnnames =~ s/\,/\|$tblname[1]\./g;
1745             #DE-ALIAS ALL TABLE-ALIASES IN THE WHERE-CLAUSE.
1746 0 0         if ($spriteref->{sprite_CaseTableNames}) #CONDITION ADDED 20040929.
1747             {
1748 0           for (my $i=0;$i<=1;$i++)
1749             {
1750 0           $orig_whereclause =~ s/ $joinalii->[$i]\./ $joinfids->[$i]\./gs;
1751             }
1752             }
1753             else
1754             {
1755 0           for (my $i=0;$i<=1;$i++)
1756             {
1757 0           $orig_whereclause =~ s/ $joinalii->[$i]\./ $joinfids->[$i]\./igs;
1758             }
1759             }
1760              
1761             #NOW, BIND ALL BIND VARIABLES HERE!
1762 0           $orig_whereclause =~ s/\\\'/\x02\^3jSpR1tE\x02/gso; #PROTECT ESCAPED DOUBLE-QUOTES.
1763 0           $orig_whereclause =~ s/\'\'/\x02\^4jSpR1tE\x02/gso; #PROTECT DOUBLED DOUBLE-QUOTES.
1764 0           $orig_whereclause =~ s/\'([^\']*?)\'/
1765 0           my ($str) = $1;
1766 0           $str =~ s|\?|\x02\^2jSpR1tE\x02|gso; #PROTECT QUESTION-MARKS WITHIN QUOTES.
1767 0           "'$str'"/egs;
1768 0           $orig_whereclause =~ s/\x02\^4jSpR1tE\x02/\'\'/gso; #UNPROTECT DOUBLED DOUBLE-QUOTES.
1769 0           $orig_whereclause =~ s/\x02\^3jSpR1tE\x02/\\\'/gso; #UNPROTECT ESCAPED DOUBLE-QUOTES.
1770              
1771             #CONVERT REMAINING QUESTION-MARKS TO BOUND VALUES.
1772              
1773 0           for (my $i = 0; $i < $numParam; $i++)
1774             {
1775 0           $params->[$i] =~ s/\?/\x02\^2jSpR1tE\x02/gso; #ADDED 20001023 TO FIX BUG WHEN PARAMETER OTHER THAN LAST CONTAINS A "?"!
1776 0           $orig_whereclause =~ s/\?/"'".$params->[$i]."'"/es;
  0            
1777             }
1778 0           $orig_whereclause =~ s/\x02\^2jSpR1tE\x02/\?/gso; #ADDED 20001023! - UNPROTECT PROTECTED "?"s.
1779 0           my $cond = $spriteref->parse_expression($orig_whereclause, $validColumnnames);
1780             #$cond =~ s/\$\_\-\>\{\w+\.(\w+)\}/BASE($icolHash{$1})/g;
1781             #$cond =~ s/\$\_\-\>\{\w+\.(\w+)\}/\$baseresv\-\>\[\$icolHash\{$1\}\]/g;
1782             #$cond =~ s/\$\_\-\>\{\w+\.(\w+)\}/JOIN($jcolHash{$1})/g;
1783 0           $cond =~ s/\$\_\-\>\{$tblname[0]\.(\w+)\}/\$baserow\-\>\[\$icolHash\{$1\}\]/g;
1784 0           $cond =~ s/\$\_\-\>\{$tblname[1]\.(\w+)\}/\$joinrow\-\>\[\$jcolHash\{$1\}\]/g;
1785             #DONT NEED?$cond =~ s/[\r\n\t]/ /gs;
1786              
1787             #NOW EVAL THE *ORIGINAL* WHERE-CLAUSE CONDITION TO WEED OUT UNDESIRED RECORDS.
1788              
1789 0           my ($j, $k, $baserow, $joinrow, @newresv, @newrow);
1790 0 0         if ($sth->FETCH('sprite_joinorder'))
1791             {
1792 0           while (@joinresv)
1793             {
1794 0           $joinrow = shift(@joinresv);
1795 0           J2A: for ($j=0;$j<$row;$j++)
1796             {
1797 0           $baserow = $resv[$j];
1798 0           $@ = '';
1799 0   0       $_ = ($cond !~ /\S/o || eval $cond);
1800 0 0         next J2A unless ($_);
1801 0           for ($k=0;$k<=$#ocolnames;$k++)
1802             {
1803 0 0         if ($ocolwhich[$k])
1804             {
1805 0           push (@newrow, $joinrow->[$jcolHash{$ocolnames[$k]}]);
1806             }
1807             else
1808             {
1809 0           push (@newrow, $baserow->[$icolHash{$ocolnames[$k]}]);
1810             }
1811             }
1812 0           push (@newresv, [@newrow]);
1813 0           @newrow = ();
1814             }
1815             }
1816             }
1817             else
1818             {
1819 0           while (@resv)
1820             {
1821 0           $baserow = shift(@resv);
1822 0           J2B: for ($j=0;$j<$jrow;$j++)
1823             {
1824 0           $joinrow = $joinresv[$j];
1825 0           $@ = '';
1826 0   0       $_ = ($cond !~ /\S/o || eval $cond);
1827 0 0         next J2B unless ($_);
1828 0           for ($k=0;$k<=$#ocolnames;$k++)
1829             {
1830 0 0         if ($ocolwhich[$k])
1831             {
1832 0           push (@newrow, $joinrow->[$jcolHash{$ocolnames[$k]}]);
1833             }
1834             else
1835             {
1836 0           push (@newrow, $baserow->[$icolHash{$ocolnames[$k]}]);
1837             }
1838             }
1839 0           push (@newresv, [@newrow]);
1840 0           @newrow = ();
1841             }
1842             }
1843             }
1844 0           @resv = (scalar(@newresv), @newresv);
1845 0   0       $retval = $resv[0] || '0E0';
1846             }
1847             }
1848             }
1849             else #SELECT SELECTED ZERO RECORDS.
1850             {
1851 0 0         if ($spriteref->{lasterror})
1852             {
1853 0           DBI::set_err($sth, $spriteref->{lasterror}, $spriteref->{lastmsg});
1854 0           $retval = undef;
1855             }
1856 0           $retval = '0E0';
1857             }
1858              
1859             #EVERYTHING WORKED, SO SAVE SPRITE RESULT (# ROWS) AND FETCH FIELD INFO.
1860              
1861             #if ($retval) #CHGD TO NEXT 20020606.
1862 0 0 0       if (defined($retval) && $retval)
1863             {
1864 0           $sth->{'driver_rows'} = $retval; # number of rows
1865 0           $sth->{'sprite_rows'} = $retval; # number of rows
1866 0           $sth->STORE('sprite_rows', $retval);
1867 0           $sth->STORE('driver_rows', $retval);
1868             }
1869             else
1870             {
1871 0           $sth->{'driver_rows'} = 0; # number of rows
1872 0           $sth->{'sprite_rows'} = 0; # number of rows
1873 0           $sth->STORE('sprite_rows', 0);
1874 0           $sth->STORE('driver_rows', 0);
1875             }
1876              
1877             #### NOTE #### IF THIS FAILS, IT PROBABLY NEEDS TO BE "sprite_rows"?
1878              
1879 0           shift @resv; #REMOVE 1ST COLUMN FROM DATA RETURNED (THE SPRITE RESULT).
1880 0 0         my @l = ($#ocolnames >= 0) ? @ocolnames : split(/,/,$spriteref->{use_fields});
1881 0           $sth->STORE('NUM_OF_FIELDS',($#l+1));
1882 0           my (@keyfields) = split(',', $spriteref->{key_fields}); #ADDED 20030520 TO IMPROVE NULLABLE.
1883              
1884 0 0         unless ($spriteref->{TYPE})
1885             {
1886 0           @{$spriteref->{NAME}} = @l;
  0            
1887 0           for my $i (0..$#l)
1888             {
1889 0 0         if (defined ${$spriteref->{types}}{$l[$i]})
  0            
1890             {
1891 0           ${$spriteref->{TYPE}}[$i] = $typehash{"\U${$spriteref->{types}}{$l[$i]}\E"};
  0            
  0            
1892 0           ${$spriteref->{PRECISION}}[$i] = ${$spriteref->{lengths}}{$l[$i]};
  0            
  0            
1893 0           ${$spriteref->{SCALE}}[$i] = ${$spriteref->{scales}}{$l[$i]};
  0            
  0            
1894             }
1895             else
1896             {
1897 0           ${$spriteref->{TYPE}}[$i] = '';
  0            
1898 0           ${$spriteref->{PRECISION}}[$i] = 0;
  0            
1899 0           ${$spriteref->{SCALE}}[$i] = 0;
  0            
1900             }
1901 0           ${$spriteref->{NULLABLE}}[$i] = 1;
  0            
1902 0           foreach my $j (@keyfields) #ADDED 20030520 TO IMPROVE NULLABLE.
1903             {
1904 0 0         if (${$spriteref->{NAME}}[$i] eq $j)
  0            
1905             {
1906 0           ${$spriteref->{NULLABLE}}[$i] = 0;
  0            
1907 0           last;
1908             }
1909             }
1910             }
1911             }
1912              
1913             #TRANSFER SPRITE'S FIELD DATA TO DBI.
1914              
1915 0           $sth->{'driver_data'} = \@resv;
1916 0           $sth->STORE('sprite_data', \@resv);
1917             #$sth->STORE('sprite_rows', ($#resv+1)); # number of rows
1918 0           $sth->{'TYPE'} = \@{$spriteref->{TYPE}};
  0            
1919 0           $sth->{'NAME'} = \@{$spriteref->{NAME}};
  0            
1920 0           for (my $i=0;$i<=$#{$sth->{'NAME'}};$i++)
  0            
1921             {
1922             $sth->{'NAME'}->[$i] = $spriteref->{ASNAMES}->{$sth->{'NAME'}->[$i]}
1923 0 0         if ($spriteref->{ASNAMES}->{$sth->{'NAME'}->[$i]});
1924             }
1925 0           $sth->{'PRECISION'} = \@{$spriteref->{PRECISION}};
  0            
1926 0           $sth->{'SCALE'} = \@{$spriteref->{SCALE}};
  0            
1927 0           $sth->{'NULLABLE'} = \@{$spriteref->{NULLABLE}};
  0            
1928 0           $sth->STORE('sprite_resv',\@resv);
1929             #ADDED NEXT LINE 20020905 TO SUPPORT DBIx::GeneratedKey!
1930 0           $sth->{Database}->STORE('sprite_insertid', $spriteref->{'sprite_lastsequence'});
1931 0 0         if (defined $retval)
1932             {
1933 0 0         return $retval ? $retval : '0E0';
1934             }
1935 0           return undef;
1936             }
1937              
1938             sub fetchrow_arrayref
1939             {
1940 0     0     my($sth) = @_;
1941 0           my $data = $sth->FETCH('driver_data');
1942 0           my $row = shift @$data;
1943             #return undef if (!$row || !scalar(@$row)); #CHGD. TO NEXT 20040913 TO AVOID _FBAV ERROR IF NO ROWS RETURNED!
1944 0 0 0       return undef if (!$row || !scalar(@$row));
1945             #my ($longreadlen) = $sth->{Database}->FETCH('LongReadLen'); #CHGD. TO NEXT 20020606 AS WORKAROUND FOR DBI::PurePerl;
1946 0   0       my ($longreadlen) = $sth->{Database}->FETCH('LongReadLen') || 0;
1947 0 0         if ($longreadlen > 0)
1948             {
1949 0 0         if ($sth->FETCH('ChopBlanks'))
1950             {
1951 0           for (my $i=0;$i<=$#{$row};$i++)
  0            
1952             {
1953 0 0         if (${$sth->{TYPE}}[$i] < 0) #LONG, LONG RAW, etc.
  0            
1954             {
1955 0           my ($t) = substr($row->[$i],0,$longreadlen);
1956 0 0 0       return undef unless (($row->[$i] eq $t) || $sth->{Database}->FETCH('LongTruncOk'));
1957 0           $row->[$i] = $t;
1958             }
1959             }
1960 0           map { $_ =~ s/\s+$//; } @$row;
  0            
1961             }
1962             }
1963             else
1964             {
1965 0 0         if ($sth->FETCH('ChopBlanks'))
1966             {
1967 0           map { $_ =~ s/\s+$//; } @$row;
  0            
1968             }
1969             }
1970 0           my $myres;
1971 0           eval { $myres = $sth->_set_fbav($row); };
  0            
1972             # $myres = $sth->_set_fbav($row);
1973 0           return $myres;
1974             }
1975              
1976             *fetch = \&fetchrow_arrayref; # required alias for fetchrow_arrayref
1977             sub rows
1978             {
1979 0     0     my($sth) = @_;
1980 0 0 0       return $sth->FETCH('driver_rows') or $sth->FETCH('sprite_rows') or $sth->{drv_rows};
1981             }
1982             #### NOTE #### IF THIS FAILS, IT PROBABLY NEEDS TO BE "sprite_rows"?
1983              
1984              
1985             sub STORE
1986             {
1987 0     0     my($dbh, $attr, $val) = @_;
1988 0 0         if ($attr eq 'AutoCommit')
1989             {
1990             # AutoCommit is currently the only standard attribute we have
1991             # to consider.
1992             #if (!$val) { die "Can't disable AutoCommit"; }
1993              
1994 0           $dbh->{AutoCommit} = $val;
1995 0           return 1;
1996             }
1997 0 0         if ($attr =~ /^sprite/o)
1998             {
1999             # Handle only our private attributes here
2000             # Note that we could trigger arbitrary actions.
2001             # Ideally we should catch unknown attributes.
2002 0           $dbh->{$attr} = $val; # Yes, we are allowed to do this,
2003 0           return 1; # but only for our private attributes
2004             }
2005             # Else pass up to DBI to handle for us
2006 0           eval {$dbh->SUPER::STORE($attr, $val);};
  0            
2007             }
2008              
2009             sub FETCH
2010             {
2011 0     0     my($dbh, $attr) = @_;
2012 0 0         if ($attr eq 'AutoCommit') { return $dbh->{AutoCommit}; }
  0            
2013 0 0         if ($attr =~ /^sprite_/o)
2014             {
2015             # Handle only our private attributes here
2016             # Note that we could trigger arbitrary actions.
2017 0           return $dbh->{$attr}; # Yes, we are allowed to do this,
2018             # but only for our private attributes
2019 0           return $dbh->{$attr};
2020             }
2021             # Else pass up to DBI to handle
2022 0           $dbh->SUPER::FETCH($attr);
2023             }
2024              
2025             sub DESTROY #ADDED 20010221
2026       0     {
2027             }
2028              
2029             1;
2030              
2031             package DBD::Sprite; # ====== HAD TO HAVE TO PREVENT MAKE ERROR! ======
2032              
2033             1;
2034              
2035             __END__