2

I have a large script that has no database connection yet. I need one for a tiny new feature. Is it safe to add a naked block to require DBI where I need it or do I need to import something?

# Lots of no-database code here...
my $obj;
{
  require DBI;
  my $dbh = DBI->connect('dsn');
  $obj = ModuleThatNeedsDBH->new(dbh => $dbh);
}
$obj->fancyStuff();
# More no-database code...

The block is to keep $dbh hidden from the rest of the program of course.

simbabque
  • 53,749
  • 8
  • 73
  • 136

2 Answers2

3

By default the DBI module imports nothing into the calling package so yes, in theory you could use require instead of use.

But what are you hoping to gain from this? In this case use DBI is equivalent to BEGIN { require DBI }, and if you omit the BEGIN block you will be imposing the lengthy process of loading the package during run time which is undesirable.

Note that you must also handle any failure to connect to the database.

my $dbh = DBI->connect('dsn', 'user', 'pass')
            or die $DBI::errstr;

although dying may be a little extreme in your case.

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 4
    It's quite desirable if the feature that loads `DBI` isn't used very frequently. – mob Jul 02 '12 at 15:45
  • It will only be used in 1 out of 50 calls. One run of the program takes a couple of minutes. It's a cronjob that creates loads of files. I don't mind if it slows down the runtime once, but I don't like it to be included every time since it is not neeeded. – simbabque Jul 02 '12 at 15:52
  • 1
    @mob: I don't buy it. `DBI` uses less than a megabyte and by default doesn't pollute the local namespace at all. It depends on the application, but I would generally prefer the marginal footprint over extended response times the first time a DB handle is needed. – Borodin Jul 02 '12 at 15:52
  • @simbabque: it's your call, but I think you are over-complicating things for no benefit – Borodin Jul 02 '12 at 15:54
  • @Borodin: That might be the case. Still, it's good to know, isn't it? – simbabque Jul 02 '12 at 15:58
  • it is funny, when Perl provides so many TimTowtdis, that people can't understand, what would happen in next line in current chosen way :) `require` and `use` are not alone in Perl package/module model :) – gaussblurinc Jul 02 '12 at 16:14
  • Why is loading the package once at run-time less desirable than loading the package once at compile-time? That's exactly the same amount of time. – ikegami Jul 02 '12 at 16:31
  • Lazy loading sounds like a micro-optimization. Perhaps staying persistent is a better choice. Look how persistence runs circles around naked CGI. – DavidO Jul 02 '12 at 16:33
  • @DavidO -You want to compare loading DBI.pm once every 50 calls/runs/requests, to Forking a web server, execing Perl, parsing the program and then executing it on EACH request? I'd say there's far more interesting stuff to be learned in contrasting them rather than comparing them. – Len Jaffe Jul 02 '12 at 17:34
  • @Borodin: I'd like to know why you don't hide DBI inside ModuleThatNeedsDBH instead of insisting on passing the $dbh into it. I'll understand is you say dependency injection, but you can do that inside ModuleThatNeedsDBH and then only load ModuleThatNeedsDBH when you need it. Of course, you've only given us a small view-port into your application and like the curious cats that we are, we all want to know more so we can find you the kill macro-optimization the we all know is lurking around here someplace. ;-) – Len Jaffe Jul 02 '12 at 17:38
  • One reason not to not hide DBI inside a module that needs it, as opposed to having a `$dbh` passed into it is that by passing a `$dbh` in, you can use modules such as DBIx::Connector to manage the database connection. Consider a number of "model" modules, each opening their own database handle as they need it. To me it seems advantageous to open one database handle, or better yet, one connector object that can provide a database handle upon request. – DavidO Jul 02 '12 at 18:11
  • @Len Jaffe: I think you mean simbabque – Borodin Jul 02 '12 at 18:17
  • @LenJaffe: The whole system is in need of a giant macro-optimization that will not happen. I can only do so much as not to make it worse. I now figured out that there is also a `require`d dependency that puts itself in the `main::` namespace and wants the DBH, but only if we use ModuleTatNeedsDBH. So I ended up with a three-way mixture of brian's suggestion and my own. I don't think it's very elegant. If there's an arg, use it. If not, make on for yourself. If there is no DBH in `main::`, put yours there so the dependency doesn't break. – simbabque Jul 03 '12 at 09:28
  • @DavidO - dependency injection. But simbabque hasn't mentioned any of that as a need. He simply said that there is one module that requires a database handle. In this single-use instance, it is perfectly acceptable, IMO, to let that module handle its own dbh manipulation. It can lead to more clean code in the logic that calls it. Since it appears to be a special case, it is okay to push its special needs "under the hood". In the end, this has been an interesting conversation, and I'm glad that OP had found a solution that works for now. – Len Jaffe Jul 05 '12 at 16:15
2

Although by default DBI imports nothing, that doesn't mean that it doesn't do any internal processing or setup when you use it. Unless a module's documentation says otherwise, you should use the full process to ensure that everything that should happen actually does. You might want to see How can I dynamically include Perl modules without using eval?.

Also, I'd consider making ModuleThatNeedsDBH automatically making one if it doesn't get on in its argument list. Dependency injection is nice, but that doesn't mean you are required to force the higher level to create the things that you need.

Community
  • 1
  • 1
brian d foy
  • 129,424
  • 31
  • 207
  • 592