{
    "content": [
        {
            "type": "text",
            "text": "# Ima::DBI (perldoc)\n\n## NAME\n\nIma::DBI - Database connection caching and organization\n\n## SYNOPSIS\n\npackage Foo;\nuse base 'Ima::DBI';\n# Class-wide methods.\nFoo->setdb($dbname, $datasource, $user, $password);\nFoo->setdb($dbname, $datasource, $user, $password, \\%attr);\nmy @databasenames   = Foo->dbnames;\nmy @databasehandles = Foo->dbhandles;\nFoo->setsql($sqlname, $statement, $dbname);\nFoo->setsql($sqlname, $statement, $dbname, $cache);\nmy @statementnames   = Foo->sqlnames;\n# Object methods.\n$dbh = $obj->db*;      # Where * is the name of the db connection.\n$sth = $obj->sql*;     # Where * is the name of the sql statement.\n$sth = $obj->sql*(@sqlpieces);\n$obj->DBIwarn($what, $doing);\nmy $rc = $obj->commit;\nmy $rc = $obj->commit(@dbnames);\nmy $rc = $obj->rollback;\nmy $rc = $obj->rollback(@dbnames);\n\n## DESCRIPTION\n\nIma::DBI attempts to organize and facilitate caching and more efficient use of database\nconnections and statement handles by storing DBI and SQL information with your class (instead of\nas separate objects). This allows you to pass around just one object without worrying about a\ntrail of DBI handles behind it.\n\n## Sections\n\n- **NAME**\n- **SYNOPSIS**\n- **DESCRIPTION** (1 subsections)\n- **USAGE**\n- **TAINTING**\n- **Class Methods** (2 subsections)\n- **Object Methods**\n- **Modified database handle methods** (1 subsections)\n- **EXAMPLE**\n- **MAINTAINERS**\n- **ORIGINAL AUTHOR**\n- **LICENSE**\n- **THANKS MUCHLY**\n- **SEE ALSO**\n\nUse structuredContent.sections for detailed options, examples, and full documentation.\n"
        }
    ],
    "structuredContent": {
        "command": "Ima::DBI",
        "section": "",
        "mode": "perldoc",
        "summary": "Ima::DBI - Database connection caching and organization",
        "synopsis": "package Foo;\nuse base 'Ima::DBI';\n# Class-wide methods.\nFoo->setdb($dbname, $datasource, $user, $password);\nFoo->setdb($dbname, $datasource, $user, $password, \\%attr);\nmy @databasenames   = Foo->dbnames;\nmy @databasehandles = Foo->dbhandles;\nFoo->setsql($sqlname, $statement, $dbname);\nFoo->setsql($sqlname, $statement, $dbname, $cache);\nmy @statementnames   = Foo->sqlnames;\n# Object methods.\n$dbh = $obj->db*;      # Where * is the name of the db connection.\n$sth = $obj->sql*;     # Where * is the name of the sql statement.\n$sth = $obj->sql*(@sqlpieces);\n$obj->DBIwarn($what, $doing);\nmy $rc = $obj->commit;\nmy $rc = $obj->commit(@dbnames);\nmy $rc = $obj->rollback;\nmy $rc = $obj->rollback(@dbnames);",
        "tldr_summary": null,
        "tldr_examples": [],
        "tldr_source": null,
        "flags": [],
        "examples": [
            "package Foo;",
            "use base qw(Ima::DBI);",
            "# Set up database connections (but don't connect yet)",
            "Foo->setdb('Users', 'dbi:Oracle:Foo', 'admin', 'passwd');",
            "Foo->setdb('Customers', 'dbi:Oracle:Foo', 'Staff', 'passwd');",
            "# Set up SQL statements to be used through out the program.",
            "Foo->setsql('FindUser', <<\"SQL\", 'Users');",
            "SELECT  *",
            "FROM    Users",
            "WHERE   Name LIKE ?",
            "SQL",
            "Foo->setsql('ChangeLanguage', <<\"SQL\", 'Customers');",
            "UPDATE  Customers",
            "SET     Language = ?",
            "WHERE   Country = ?",
            "SQL",
            "# rest of the class as usual.",
            "package main;",
            "$obj = Foo->new;",
            "eval {",
            "# Does connect & prepare",
            "my $sth = $obj->sqlFindUser;",
            "# bindparams, execute & bindcolumns",
            "$sth->execute(['Likmi%'], [\\($name)]);",
            "while( $sth->fetch ) {",
            "print $name;",
            "# Uses cached database and statement handles",
            "$sth = $obj->sqlFindUser;",
            "# bindparams & execute.",
            "$sth->execute('%Hock');",
            "@names = $sth->fetchall;",
            "# connects, prepares",
            "$rowsaltered = $obj->sqlChangeLanguage->execute(qw(esMX mx));",
            "};",
            "unless ($@) {",
            "# Everything went okay, commit the changes to the customers.",
            "$obj->commit('Customers');",
            "else {",
            "$obj->rollback('Customers');",
            "warn \"DBI failure:  $@\";",
            "USE WITH MODPERL, FASTCGI, ETC.",
            "To help with use in forking environments, Ima::DBI database handles keep track of the PID of the",
            "process they were openend under. If they notice a change (because you forked a new process), a",
            "new handle will be opened in the new process. This prevents a common problem seen in",
            "environments like modperl where people would open a handle in the parent process and then run",
            "into trouble when they try to use it from a child process.",
            "Because Ima::DBI handles keeping database connections persistent and prevents problems with",
            "handles openend before forking, it is not necessary to use Apache::DBI when using Ima::DBI.",
            "However, there is one feature of Apache::DBI which you will need in a modperl or FastCGI",
            "environment, and that's the automatic rollback it does at the end of each request. This rollback",
            "provides safety from transactions left hanging when some perl code dies -- a serious problem",
            "which could grind your database to a halt with stale locks.",
            "To replace this feature on your own under modperl, you can add something like this in a handler",
            "at any phase of the request:",
            "$r->pushhandlers(PerlCleanupHandler => sub {",
            "MyImaDBI->rollback();",
            "});",
            "Here \"MyImaDBI\" is your subclass of Ima::DBI. You could also make this into an actual module and",
            "set the PerlCleanupHandler from your httpd.conf. A similar approach should work in any",
            "long-running environment which has a hook for running some code at the end of each request.",
            "TODO, Caveat, BUGS, etc....",
            "I seriously doubt that it's thread safe.",
            "You can bet cupcackes to sno-cones that much havoc will be wrought if Ima::DBI is used in a",
            "threaded Perl.",
            "Should make use of private* handle method to store information",
            "The docs stink.",
            "The docs were originally written when I didn't have a good handle on the module and how it",
            "will be used in practical cases. I need to rewrite the docs from the ground up.",
            "Need to add debugging hooks.",
            "The thing which immediately comes to mind is a Verbose flag to print out SQL statements as",
            "they are made as well as mention when database connections are made, etc..."
        ],
        "see_also": [],
        "section_outline": [
            {
                "name": "NAME",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "SYNOPSIS",
                "lines": 28,
                "subsections": []
            },
            {
                "name": "DESCRIPTION",
                "lines": 103,
                "subsections": [
                    {
                        "name": "Why shouldn't I use this thing.",
                        "lines": 20
                    }
                ]
            },
            {
                "name": "USAGE",
                "lines": 13,
                "subsections": []
            },
            {
                "name": "TAINTING",
                "lines": 6,
                "subsections": []
            },
            {
                "name": "Class Methods",
                "lines": 57,
                "subsections": [
                    {
                        "name": "db_names",
                        "lines": 18
                    },
                    {
                        "name": "set_sql",
                        "lines": 2
                    }
                ]
            },
            {
                "name": "Object Methods",
                "lines": 55,
                "subsections": []
            },
            {
                "name": "Modified database handle methods",
                "lines": 21,
                "subsections": [
                    {
                        "name": "rollback",
                        "lines": 4
                    }
                ]
            },
            {
                "name": "EXAMPLE",
                "lines": 92,
                "subsections": []
            },
            {
                "name": "MAINTAINERS",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "ORIGINAL AUTHOR",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "LICENSE",
                "lines": 3,
                "subsections": []
            },
            {
                "name": "THANKS MUCHLY",
                "lines": 5,
                "subsections": []
            },
            {
                "name": "SEE ALSO",
                "lines": 4,
                "subsections": []
            }
        ],
        "sections": {
            "NAME": {
                "content": "Ima::DBI - Database connection caching and organization\n",
                "subsections": []
            },
            "SYNOPSIS": {
                "content": "package Foo;\nuse base 'Ima::DBI';\n\n# Class-wide methods.\nFoo->setdb($dbname, $datasource, $user, $password);\nFoo->setdb($dbname, $datasource, $user, $password, \\%attr);\n\nmy @databasenames   = Foo->dbnames;\nmy @databasehandles = Foo->dbhandles;\n\nFoo->setsql($sqlname, $statement, $dbname);\nFoo->setsql($sqlname, $statement, $dbname, $cache);\n\nmy @statementnames   = Foo->sqlnames;\n\n# Object methods.\n$dbh = $obj->db*;      # Where * is the name of the db connection.\n$sth = $obj->sql*;     # Where * is the name of the sql statement.\n$sth = $obj->sql*(@sqlpieces);\n\n$obj->DBIwarn($what, $doing);\n\nmy $rc = $obj->commit;\nmy $rc = $obj->commit(@dbnames);\n\nmy $rc = $obj->rollback;\nmy $rc = $obj->rollback(@dbnames);\n",
                "subsections": []
            },
            "DESCRIPTION": {
                "content": "Ima::DBI attempts to organize and facilitate caching and more efficient use of database\nconnections and statement handles by storing DBI and SQL information with your class (instead of\nas separate objects). This allows you to pass around just one object without worrying about a\ntrail of DBI handles behind it.\n\nOne of the things I always found annoying about writing large programs with DBI was making sure\nthat I didn't have duplicate database handles open. I was also annoyed by the somewhat wasteful\nnature of the prepare/execute/finish route I'd tend to go through in my subroutines. The new\nDBI->connectcached and DBI->preparecached helped a lot, but I still had to throw around global\ndatasource, username and password information.\n\nSo, after a while I grew a small library of DBI helper routines and techniques. Ima::DBI is the\nculmination of all this, put into a nice(?), clean(?) class to be inherited from.\n\nWhy should I use this thing?\nIma::DBI is a little odd, and it's kinda hard to explain. So lemme explain why you'd want to use\nthis thing...\n\n*   Consolidation of all SQL statements and database information\n\nNo matter what, embedding one language into another is messy. DBI alleviates this somewhat,\nbut I've found a tendency to have that scatter the SQL around inside the Perl code. Ima::DBI\nallows you to easily group the SQL statements in one place where they are easier to maintain\n(especially if one developer is writing the SQL, another writing the Perl). Alternatively,\nyou can place your SQL statement alongside the code which uses it. Whatever floats your\nboat.\n\nDatabase connection information (data source, username, password, atrributes, etc...) can\nalso be consolidated together and tracked.\n\nBoth the SQL and the connection info are probably going to change a lot, so having them well\norganized and easy to find in the code is a Big Help.\n\n*   Holds off opening a database connection until necessary.\n\nWhile Ima::DBI is informed of all your database connections and SQL statements at\ncompile-time, it will not connect to the database until you actually prepare a statement on\nthat connection.\n\nThis is obviously very good for programs that sometimes never touch the database. It's also\ngood for code that has lots of possible connections and statements, but which typically only\nuse a few. Kinda like an autoloader.\n\n*   Easy integration of the DBI handles into your class\n\nIma::DBI causes each database handle to be associated with your class, allowing you to pull\nhandles from an instance of your object, as well as making many oft-used DBI methods\navailable directly from your instance.\n\nThis gives you a cleaner OO design, since you can now just throw around the object as usual\nand it will carry its associated DBI baggage with it.\n\n*   Honors taint mode\n\nIt always struck me as a design deficiency that tainted SQL statements could be passed to\n$sth->prepare(). For example:\n\n# $user is from an untrusted source and is tainted.\n$user = getuserdatafromtheoutsideworld;\n$sth = $dbh->prepare('DELETE FROM Users WHERE User = $user');\n\nLooks innocent enough... but what if $user was the string \"1 OR User LIKE '%'\". You just\nblew away all your users. Hope you have backups.\n\nIma::DBI turns on the DBI->connect Taint attribute so that all DBI methods (except\nexecute()) will no longer accept tainted data. See \"Taint\" in DBI for details.\n\n*   Taints returned data\n\nDatabases should be like any other system call. It's the scary Outside World, thus it should\nbe tainted. Simple. Ima::DBI turns on DBI's Taint attribute on each connection. This feature\nis overridable by passing your own Taint attribute to setdb as normal for DBI. See \"Taint\"\nin DBI for details.\n\n*   Encapsulation of some of the more repetitive bits of everyday DBI usage\n\nI get lazy a lot and I forget to do things I really should, like using bindcols(), or\nrigorous error checking. Ima::DBI does some of this stuff automatically, other times it just\nmakes it more convenient.\n\n*   Encapsulation of DBI's cache system\n\nDBI's automatic handle caching system is relatively new, and some people aren't aware of its\nuse. Ima::DBI uses it automatically, so you don't have to worry about it. (It even makes it\na bit more efficient)\n\n*   Sharing of database and sql information amongst inherited classes\n\nAny SQL statements and connections created by a class are available to its children via\nnormal method inheritance.\n\n*   Guarantees one connection per program.\n\nOne program, one database connection (per database user). One program, one prepared\nstatement handle (per statement, per database user). That's what Ima::DBI enforces.\nExtremely handy in persistent environments (servers, daemons, modperl, FastCGI, etc...)\n\n*   Encourages use of bind parameters and columns\n\nBind parameters are safer and more efficient than embedding the column information straight\ninto the SQL statement. Bind columns are more efficient than normal fetching. Ima::DBI\npretty much requires the usage of the former, and eases the use of the latter.\n",
                "subsections": [
                    {
                        "name": "Why shouldn't I use this thing.",
                        "content": "*   It's all about OO\n\nAlthough it is possible to use Ima::DBI as a stand-alone module as part of a\nfunction-oriented design, its generally not to be used unless integrated into an\nobject-oriented design.\n\n*   Overkill for small programs\n\n*   Overkill for programs with only one or two SQL statements\n\nIts up to you whether the trouble of setting up a class and jumping through the necessary\nIma::DBI hoops is worth it for small programs. To me, it takes just as much time to set up\nan Ima::DBI subclass as it would to access DBI without it... but then again I wrote the\nmodule. YMMV.\n\n*   Overkill for programs that only use their SQL statements once\n\nIma::DBI's caching might prove to be an unnecessary performance hog if you never use the\nsame SQL statement twice. Not sure, I haven't looked into it.\n"
                    }
                ]
            },
            "USAGE": {
                "content": "The basic steps to \"DBIing\" a class are:\n\n1   Inherit from Ima::DBI\n\n2   Set up and name all your database connections via setdb()\n\n3   Set up and name all your SQL statements via setsql()\n\n4   Use sql* to retrieve your statement handles ($sth) as needed and db* to retrieve database\nhandles ($dbh).\n\nHave a look at EXAMPLE below.\n",
                "subsections": []
            },
            "TAINTING": {
                "content": "Ima::DBI, by default, uses DBI's Taint flag on all connections.\n\nThis means that Ima::DBI methods do not accept tainted data, and that all data fetched from the\ndatabase will be tainted. This may be different from the DBI behavior you're used to. See\n\"Taint\" in DBI for details.\n",
                "subsections": []
            },
            "Class Methods": {
                "content": "setdb\nFoo->setdb($dbname, $datasource, $user, $password);\nFoo->setdb($dbname, $datasource, $user, $password, \\%attr);\n\nThis method is used in place of DBI->connect to create your database handles. It sets up a new\nDBI database handle associated to $dbname. All other arguments are passed through to\nDBI->connectcached.\n\nA new method is created for each db you setup. This new method is called \"db$dbname\"... so,\nfor example, Foo->setdb(\"foo\", ...) will create a method called \"dbfoo()\". (Spaces in $dbname\nwill be translated into underscores: '')\n\n%attr is combined with a set of defaults (RaiseError => 1, AutoCommit => 0, PrintError => 0,\nTaint => 1). This is a better default IMHO, however it does give databases without transactions\n(such as MySQL when used with the default MyISAM table type) a hard time. Be sure to turn\nAutoCommit back on if your database does not support transactions.\n\nThe actual database handle creation (and thus the database connection) is held off until a\nprepare is attempted with this handle.\n\nsetsql\nFoo->setsql($sqlname, $statement, $dbname);\nFoo->setsql($sqlname, $statement, $dbname, $cache);\n\nThis method is used in place of DBI->prepare to create your statement handles. It sets up a new\nstatement handle associated to $sqlname using the database connection associated with $dbname.\n$statement is passed through to either DBI->prepare or DBI->preparecached (depending on $cache)\nto create the statement handle.\n\nIf $cache is true or isn't given, then preparecached() will be used to prepare the statement\nhandle and it will be cached. If $cache is false then a normal prepare() will be used and the\nstatement handle will be recompiled on every sql*() call. If you have a statement which changes\na lot or is used very infrequently you might not want it cached.\n\nA new method is created for each statement you set up. This new method is \"sql$sqlname\"... so,\nas with setdb(), Foo->setsql(\"bar\", ..., \"foo\"); will create a method called \"sqlbar()\" which\nuses the database connection from \"dbfoo()\". Again, spaces in $sqlname will be translated into\nunderscores ('').\n\nThe actual statement handle creation is held off until sql* is first called on this name.\n\ntransformsql\nTo make up for the limitations of bind parameters, $statement can contain sprintf() style\nformatting (ie. %s and such) to allow dynamically generated SQL statements (so to get a real\npercent sign, use '%%').\n\nThe translation of the SQL happens in transformsql(), which can be overridden to do more\ncomplex transformations. See Class::DBI for an example.\n\ndbnames / dbhandles\nmy @databasenames   = Foo->dbnames;\nmy @databasehandles = Foo->dbhandles;\nmy @databasehandles = Foo->dbhandles(@dbnames);\n\nReturns a list of the database handles set up for this class using setdb(). This includes all\ninherited handles.\n",
                "subsections": [
                    {
                        "name": "db_names",
                        "content": "converting it to a method name and calling that db method...\n\nmy @dbnames = Foo->dbnames;\nmy $dbmeth = 'db'.$dbnames[0];\nmy $dbh = $foo->$dbmeth;\n\nIcky, eh? Fortunately, dbhandles() does this for you and returns a list of database handles in\nthe same order as dbnames(). Use this sparingly as it will connect you to the database if you\nweren't already connected.\n\nIf given @dbnames, dbhandles() will return only the handles for those connections.\n\nThese both work as either class or object methods.\n\nsqlnames\nmy @statementnames   = Foo->sqlnames;\n\nSimilar to dbnames() this returns the names of all SQL statements set up for this class using"
                    },
                    {
                        "name": "set_sql",
                        "content": "There is no corresponding sqlhandles() because we can't know what arguments to pass in.\n"
                    }
                ]
            },
            "Object Methods": {
                "content": "db*\n$dbh = $obj->db*;\n\nThis is how you directly access a database handle you set up with setdb.\n\nThe actual particular method name is derived from what you told setdb.\n\ndb* will handle all the issues of making sure you're already connected to the database.\n\nsql*\n$sth = $obj->sql*;\n$sth = $obj->sql*(@sqlpieces);\n\nsql*() is a catch-all name for the methods you set up with setsql(). For instance, if you did:\n\nFoo->setsql('GetAllFoo', 'Select * From Foo', 'SomeDb');\n\nyou'd run that statement with sqlGetAllFoo().\n\nsql* will handle all the issues of making sure the database is already connected, and the\nstatement handle is prepared. It returns a prepared statement handle for you to use. (You're\nexpected to execute() it)\n\nIf sql*() is given a list of @sqlpieces it will use them to fill in your statement, assuming\nyou have sprintf() formatting tags in your statement. For example:\n\nFoo->setsql('GetTable', 'Select * From %s', 'Things');\n\n# Assuming we have created an object... this will prepare the\n# statement 'Select * From Bar'\n$sth = $obj->sqlSearch('Bar');\n\nBe very careful with what you feed this function. It cannot do any quoting or escaping for you,\nso it is totally up to you to take care of that. Fortunately if you have tainting on you will be\nspared the worst.\n\nIt is recommended you only use this in cases where bind parameters will not work.\n\nDBIwarn\n$obj->DBIwarn($what, $doing);\n\nProduces a useful error for exceptions with DBI.\n\nI'm not particularly happy with this interface\n\nMost useful like this:\n\neval {\n$self->sqlSomething->execute($self->{ID}, @stuff);\n};\nif($@) {\n$self->DBIwarn($self->{ID}, 'Something');\nreturn;\n}\n",
                "subsections": []
            },
            "Modified database handle methods": {
                "content": "Ima::DBI makes some of the methods available to your object that are normally only available via\nthe database handle. In addition, it spices up the API a bit.\n\ncommit\n$rc = $obj->commit;\n$rc = $obj->commit(@dbnames);\n\nDerived from $dbh->commit() and basically does the same thing.\n\nIf called with no arguments, it causes commit() to be called on all database handles associated\nwith $obj. Otherwise it commits all database handles whose names are listed in @dbnames.\n\nAlternatively, you may like to do: $rc = $obj->dbName->commit;\n\nIf all the commits succeeded it returns true, false otherwise.\n\nrollback\n$rc = $obj->rollback;\n$rc = $obj->rollback(@dbnames);\n\nDerived from $dbh->rollback, this acts just like Ima::DBI->commit, except that it calls",
                "subsections": [
                    {
                        "name": "rollback",
                        "content": "Alternatively, you may like to do: $rc = $obj->dbName->rollback;\n\nIf all the rollbacks succeeded it returns true, false otherwise.\n"
                    }
                ]
            },
            "EXAMPLE": {
                "content": "package Foo;\nuse base qw(Ima::DBI);\n\n# Set up database connections (but don't connect yet)\nFoo->setdb('Users', 'dbi:Oracle:Foo', 'admin', 'passwd');\nFoo->setdb('Customers', 'dbi:Oracle:Foo', 'Staff', 'passwd');\n\n# Set up SQL statements to be used through out the program.\nFoo->setsql('FindUser', <<\"SQL\", 'Users');\nSELECT  *\nFROM    Users\nWHERE   Name LIKE ?\nSQL\n\nFoo->setsql('ChangeLanguage', <<\"SQL\", 'Customers');\nUPDATE  Customers\nSET     Language = ?\nWHERE   Country = ?\nSQL\n\n# rest of the class as usual.\n\npackage main;\n\n$obj = Foo->new;\n\neval {\n# Does connect & prepare\nmy $sth = $obj->sqlFindUser;\n# bindparams, execute & bindcolumns\n$sth->execute(['Likmi%'], [\\($name)]);\nwhile( $sth->fetch ) {\nprint $name;\n}\n\n# Uses cached database and statement handles\n$sth = $obj->sqlFindUser;\n# bindparams & execute.\n$sth->execute('%Hock');\n@names = $sth->fetchall;\n\n# connects, prepares\n$rowsaltered = $obj->sqlChangeLanguage->execute(qw(esMX mx));\n};\nunless ($@) {\n# Everything went okay, commit the changes to the customers.\n$obj->commit('Customers');\n}\nelse {\n$obj->rollback('Customers');\nwarn \"DBI failure:  $@\";\n}\n\nUSE WITH MODPERL, FASTCGI, ETC.\nTo help with use in forking environments, Ima::DBI database handles keep track of the PID of the\nprocess they were openend under. If they notice a change (because you forked a new process), a\nnew handle will be opened in the new process. This prevents a common problem seen in\nenvironments like modperl where people would open a handle in the parent process and then run\ninto trouble when they try to use it from a child process.\n\nBecause Ima::DBI handles keeping database connections persistent and prevents problems with\nhandles openend before forking, it is not necessary to use Apache::DBI when using Ima::DBI.\nHowever, there is one feature of Apache::DBI which you will need in a modperl or FastCGI\nenvironment, and that's the automatic rollback it does at the end of each request. This rollback\nprovides safety from transactions left hanging when some perl code dies -- a serious problem\nwhich could grind your database to a halt with stale locks.\n\nTo replace this feature on your own under modperl, you can add something like this in a handler\nat any phase of the request:\n\n$r->pushhandlers(PerlCleanupHandler => sub {\nMyImaDBI->rollback();\n});\n\nHere \"MyImaDBI\" is your subclass of Ima::DBI. You could also make this into an actual module and\nset the PerlCleanupHandler from your httpd.conf. A similar approach should work in any\nlong-running environment which has a hook for running some code at the end of each request.\n\nTODO, Caveat, BUGS, etc....\nI seriously doubt that it's thread safe.\nYou can bet cupcackes to sno-cones that much havoc will be wrought if Ima::DBI is used in a\nthreaded Perl.\n\nShould make use of private* handle method to store information\nThe docs stink.\nThe docs were originally written when I didn't have a good handle on the module and how it\nwill be used in practical cases. I need to rewrite the docs from the ground up.\n\nNeed to add debugging hooks.\nThe thing which immediately comes to mind is a Verbose flag to print out SQL statements as\nthey are made as well as mention when database connections are made, etc...\n",
                "subsections": []
            },
            "MAINTAINERS": {
                "content": "Tony Bowden <tony@tmtm.com> and Perrin Harkins <perrin@elem.com>\n",
                "subsections": []
            },
            "ORIGINAL AUTHOR": {
                "content": "Michael G Schwern <schwern@pobox.com>\n",
                "subsections": []
            },
            "LICENSE": {
                "content": "This module is free software. You may distribute under the same terms as Perl itself. IT COMES\nWITHOUT WARRANTY OF ANY KIND.\n",
                "subsections": []
            },
            "THANKS MUCHLY": {
                "content": "Tim Bunce, for enduring many DBI questions and adding Taint, preparecached and connectcached\nmethods to DBI, simplifying this greatly!\n\nArena Networks, for effectively paying for Mike to write most of this module.\n",
                "subsections": []
            },
            "SEE ALSO": {
                "content": "DBI.\n\nYou may also choose to check out Class::DBI which hides most of this from view.\n",
                "subsections": []
            }
        }
    }
}