Force no_defer on DBIC-internal quote_sub() invocations
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / _Util.pm
1 package # hide from PAUSE
2   DBIx::Class::_Util;
3
4 use DBIx::Class::StartupCheck;  # load es early as we can, usually a noop
5
6 use warnings;
7 use strict;
8
9 use constant SPURIOUS_VERSION_CHECK_WARNINGS => ( "$]" < 5.010 ? 1 : 0);
10
11 BEGIN {
12   package # hide from pause
13     DBIx::Class::_ENV_;
14
15   use Config;
16
17   use constant {
18
19     # but of course
20     BROKEN_FORK => ($^O eq 'MSWin32') ? 1 : 0,
21
22     BROKEN_GOTO => ( "$]" < 5.008003 ) ? 1 : 0,
23
24     HAS_ITHREADS => $Config{useithreads} ? 1 : 0,
25
26     UNSTABLE_DOLLARAT => ( "$]" < 5.013002 ) ? 1 : 0,
27
28     ( map
29       #
30       # the "DBIC_" prefix below is crucial - this is what makes CI pick up
31       # all envvars without further adjusting its scripts
32       # DO NOT CHANGE to the more logical { $_ => !!( $ENV{"DBIC_$_"} ) }
33       #
34       { substr($_, 5) => !!( $ENV{$_} ) }
35       qw(
36         DBIC_SHUFFLE_UNORDERED_RESULTSETS
37         DBIC_ASSERT_NO_INTERNAL_WANTARRAY
38         DBIC_ASSERT_NO_INTERNAL_INDIRECT_CALLS
39         DBIC_STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE
40         DBIC_STRESSTEST_COLUMN_INFO_UNAWARE_STORAGE
41       )
42     ),
43
44     IV_SIZE => $Config{ivsize},
45
46     OS_NAME => $^O,
47   };
48
49   if ( "$]" < 5.009_005) {
50     require MRO::Compat;
51     constant->import( OLD_MRO => 1 );
52   }
53   else {
54     require mro;
55     constant->import( OLD_MRO => 0 );
56   }
57
58   # Both of these are no longer used for anything. However bring
59   # them back after they were purged in 08a8d8f1, as there appear
60   # to be outfits with *COPY PASTED* pieces of lib/DBIx/Class/Storage/*
61   # in their production codebases. There is no point in breaking these
62   # if whatever they used actually continues to work
63   my $warned;
64   my $sigh = sub {
65
66     require Carp;
67     my $cluck = "The @{[ (caller(1))[3] ]} constant is no more - adjust your code" . Carp::longmess();
68
69     warn $cluck unless $warned->{$cluck}++;
70
71     0;
72   };
73   sub DBICTEST () { &$sigh }
74   sub PEEPEENESS () { &$sigh }
75 }
76
77 # FIXME - this is not supposed to be here
78 # Carp::Skip to the rescue soon
79 use DBIx::Class::Carp '^DBIx::Class|^DBICTest';
80
81 use B ();
82 use Carp 'croak';
83 use Storable 'nfreeze';
84 use Scalar::Util qw(weaken blessed reftype refaddr);
85 use Sub::Quote qw(qsub);
86 use Sub::Name ();
87
88 # Already correctly prototyped: perlbrew exec perl -MStorable -e 'warn prototype \&Storable::dclone'
89 BEGIN { *deep_clone = \&Storable::dclone }
90
91 use base 'Exporter';
92 our @EXPORT_OK = qw(
93   sigwarn_silencer modver_gt_or_eq modver_gt_or_eq_and_lt
94   fail_on_internal_wantarray fail_on_internal_call
95   refdesc refcount hrefaddr set_subname
96   scope_guard detected_reinvoked_destructor
97   is_exception dbic_internal_try
98   quote_sub qsub perlstring serialize deep_clone dump_value
99   parent_dir mkdir_p
100   UNRESOLVABLE_CONDITION
101 );
102
103 use constant UNRESOLVABLE_CONDITION => \ '1 = 0';
104
105 BEGIN {
106   Sub::Quote->VERSION(2.002);
107 }
108 # Override forcing no_defer, and adding naming consistency checks
109 sub quote_sub {
110   Carp::confess( "Anonymous quoting not supported by the DBIC sub_quote override - supply a sub name" ) if
111     @_ < 2
112       or
113     ! defined $_[1]
114       or
115     length ref $_[1]
116   ;
117
118   Carp::confess( "The DBIC sub_quote override expects sub name '$_[0]' to be fully qualified" )
119     unless $_[0] =~ /::/;
120
121   Carp::confess( "The DBIC sub_quote override expects the sub name '$_[0]' to match the supplied 'package' argument" ) if
122     $_[3]
123       and
124     defined $_[3]->{package}
125       and
126     index( $_[0], $_[3]->{package} ) != 0
127   ;
128
129   my @caller = caller(0);
130   my $sq_opts = {
131     package => $caller[0],
132     hints => $caller[8],
133     warning_bits => $caller[9],
134     hintshash => $caller[10],
135     %{ $_[3] || {} },
136
137     # explicitly forced for everything
138     no_defer => 1,
139   };
140
141   my $cref = Sub::Quote::quote_sub( $_[0], $_[1], $_[2]||{}, $sq_opts );
142 }
143
144 sub sigwarn_silencer ($) {
145   my $pattern = shift;
146
147   croak "Expecting a regexp" if ref $pattern ne 'Regexp';
148
149   my $orig_sig_warn = $SIG{__WARN__} || sub { CORE::warn(@_) };
150
151   return sub { &$orig_sig_warn unless $_[0] =~ $pattern };
152 }
153
154 sub perlstring ($) { q{"}. quotemeta( shift ). q{"} };
155
156 sub hrefaddr ($) { sprintf '0x%x', &refaddr||0 }
157
158 sub refdesc ($) {
159   croak "Expecting a reference" if ! length ref $_[0];
160
161   # be careful not to trigger stringification,
162   # reuse @_ as a scratch-pad
163   sprintf '%s%s(0x%x)',
164     ( defined( $_[1] = blessed $_[0]) ? "$_[1]=" : '' ),
165     reftype $_[0],
166     refaddr($_[0]),
167   ;
168 }
169
170 sub refcount ($) {
171   croak "Expecting a reference" if ! length ref $_[0];
172
173   # No tempvars - must operate on $_[0], otherwise the pad
174   # will count as an extra ref
175   B::svref_2object($_[0])->REFCNT;
176 }
177
178 # FIXME In another life switch this to a polyfill like the one in namespace::clean
179 sub set_subname ($$) {
180
181   # fully qualify name
182   splice @_, 0, 1, caller(0) . "::$_[0]"
183     if $_[0] !~ /::|'/;
184
185   &Sub::Name::subname;
186 }
187
188 sub serialize ($) {
189   local $Storable::canonical = 1;
190   nfreeze($_[0]);
191 }
192
193 my $dd_obj;
194 sub dump_value ($) {
195   local $Data::Dumper::Indent = 1
196     unless defined $Data::Dumper::Indent;
197
198   my $dump_str = (
199     $dd_obj
200       ||=
201     do {
202       require Data::Dumper;
203       my $d = Data::Dumper->new([])
204         ->Purity(0)
205         ->Pad('')
206         ->Useqq(1)
207         ->Terse(1)
208         ->Freezer('')
209         ->Quotekeys(0)
210         ->Bless('bless')
211         ->Pair(' => ')
212         ->Sortkeys(1)
213         ->Deparse(1)
214       ;
215
216       $d->Sparseseen(1) if modver_gt_or_eq (
217         'Data::Dumper', '2.136'
218       );
219
220       $d;
221     }
222   )->Values([$_[0]])->Dump;
223
224   $dd_obj->Reset->Values([]);
225
226   $dump_str;
227 }
228
229 sub scope_guard (&) {
230   croak 'Calling scope_guard() in void context makes no sense'
231     if ! defined wantarray;
232
233   # no direct blessing of coderefs - DESTROY is buggy on those
234   bless [ $_[0] ], 'DBIx::Class::_Util::ScopeGuard';
235 }
236 {
237   package #
238     DBIx::Class::_Util::ScopeGuard;
239
240   sub DESTROY {
241     &DBIx::Class::_Util::detected_reinvoked_destructor;
242
243     local $@ if DBIx::Class::_ENV_::UNSTABLE_DOLLARAT;
244
245     eval {
246       $_[0]->[0]->();
247       1;
248     }
249       or
250     Carp::cluck(
251       "Execution of scope guard $_[0] resulted in the non-trappable exception:\n\n$@"
252     );
253   }
254 }
255
256
257 sub is_exception ($) {
258   my $e = $_[0];
259
260   # FIXME
261   # this is not strictly correct - an eval setting $@ to undef
262   # is *not* the same as an eval setting $@ to ''
263   # but for the sake of simplicity assume the following for
264   # the time being
265   return 0 unless defined $e;
266
267   my ($not_blank, $suberror);
268   {
269     local $SIG{__DIE__} if $SIG{__DIE__};
270     local $@;
271     eval {
272       # The ne() here is deliberate - a plain length($e), or worse "$e" ne
273       # will entirely obviate the need for the encolsing eval{}, as the
274       # condition we guard against is a missing fallback overload
275       $not_blank = ( $e ne '' );
276       1;
277     } or $suberror = $@;
278   }
279
280   if (defined $suberror) {
281     if (length (my $class = blessed($e) )) {
282       carp_unique( sprintf(
283         'External exception class %s implements partial (broken) overloading '
284       . 'preventing its instances from being used in simple ($x eq $y) '
285       . 'comparisons. Given Perl\'s "globally cooperative" exception '
286       . 'handling this type of brokenness is extremely dangerous on '
287       . 'exception objects, as it may (and often does) result in silent '
288       . '"exception substitution". DBIx::Class tries to work around this '
289       . 'as much as possible, but other parts of your software stack may '
290       . 'not be even aware of this. Please submit a bugreport against the '
291       . 'distribution containing %s and in the meantime apply a fix similar '
292       . 'to the one shown at %s, in order to ensure your exception handling '
293       . 'is saner application-wide. What follows is the actual error text '
294       . "as generated by Perl itself:\n\n%s\n ",
295         $class,
296         $class,
297         'http://v.gd/DBIC_overload_tempfix/',
298         $suberror,
299       ));
300
301       # workaround, keeps spice flowing
302       $not_blank = !!( length $e );
303     }
304     else {
305       # not blessed yet failed the 'ne'... this makes 0 sense...
306       # just throw further
307       die $suberror
308     }
309   }
310   elsif (
311     # a ref evaluating to '' is definitively a "null object"
312     ( not $not_blank )
313       and
314     length( my $class = ref $e )
315   ) {
316     carp_unique( sprintf(
317       "Objects of external exception class '%s' stringify to '' (the "
318     . 'empty string), implementing the so called null-object-pattern. '
319     . 'Given Perl\'s "globally cooperative" exception handling using this '
320     . 'class of exceptions is extremely dangerous, as it may (and often '
321     . 'does) result in silent discarding of errors. DBIx::Class tries to '
322     . 'work around this as much as possible, but other parts of your '
323     . 'software stack may not be even aware of the problem. Please submit '
324     . 'a bugreport against the distribution containing %s',
325
326       ($class) x 2,
327     ));
328
329     $not_blank = 1;
330   }
331
332   return $not_blank;
333 }
334
335 {
336   my $callstack_state;
337
338   # Recreate the logic of try(), while reusing the catch()/finally() as-is
339   #
340   # FIXME: We need to move away from Try::Tiny entirely (way too heavy and
341   # yes, shows up ON TOP of profiles) but this is a batle for another maint
342   sub dbic_internal_try (&;@) {
343
344     my $try_cref = shift;
345     my $catch_cref = undef;  # apparently this is a thing... https://rt.perl.org/Public/Bug/Display.html?id=119311
346
347     for my $arg (@_) {
348
349       if( ref($arg) eq 'Try::Tiny::Catch' ) {
350
351         croak 'dbic_internal_try() may not be followed by multiple catch() blocks'
352           if $catch_cref;
353
354         $catch_cref = $$arg;
355       }
356       elsif ( ref($arg) eq 'Try::Tiny::Finally' ) {
357         croak 'dbic_internal_try() does not support finally{}';
358       }
359       else {
360         croak(
361           'dbic_internal_try() encountered an unexpected argument '
362         . "'@{[ defined $arg ? $arg : 'UNDEF' ]}' - perhaps "
363         . 'a missing semi-colon before or ' # trailing space important
364         );
365       }
366     }
367
368     my $wantarray = wantarray;
369     my $preexisting_exception = $@;
370
371     my @ret;
372     my $all_good = eval {
373       $@ = $preexisting_exception;
374
375       local $callstack_state->{in_internal_try} = 1
376         unless $callstack_state->{in_internal_try};
377
378       # always unset - someone may have snuck it in
379       local $SIG{__DIE__} if $SIG{__DIE__};
380
381       if( $wantarray ) {
382         @ret = $try_cref->();
383       }
384       elsif( defined $wantarray ) {
385         $ret[0] = $try_cref->();
386       }
387       else {
388         $try_cref->();
389       }
390
391       1;
392     };
393
394     my $exception = $@;
395     $@ = $preexisting_exception;
396
397     if ( $all_good ) {
398       return $wantarray ? @ret : $ret[0]
399     }
400     elsif ( $catch_cref ) {
401       for ( $exception ) {
402         return $catch_cref->($exception);
403       }
404     }
405
406     return;
407   }
408
409   sub in_internal_try { !! $callstack_state->{in_internal_try} }
410 }
411
412 {
413   my $destruction_registry = {};
414
415   sub CLONE {
416     %$destruction_registry = map {
417       (defined $_)
418         ? ( refaddr($_) => $_ )
419         : ()
420     } values %$destruction_registry;
421
422     weaken($_) for values %$destruction_registry;
423
424     # Dummy NEXTSTATE ensuring the all temporaries on the stack are garbage
425     # collected before leaving this scope. Depending on the code above, this
426     # may very well be just a preventive measure guarding future modifications
427     undef;
428   }
429
430   # This is almost invariably invoked from within DESTROY
431   # throwing exceptions won't work
432   sub detected_reinvoked_destructor {
433
434     # quick "garbage collection" pass - prevents the registry
435     # from slowly growing with a bunch of undef-valued keys
436     defined $destruction_registry->{$_} or delete $destruction_registry->{$_}
437       for keys %$destruction_registry;
438
439     if (! length ref $_[0]) {
440       printf STDERR '%s() expects a blessed reference %s',
441         (caller(0))[3],
442         Carp::longmess,
443       ;
444       return undef; # don't know wtf to do
445     }
446     elsif (! defined $destruction_registry->{ my $addr = refaddr($_[0]) } ) {
447       weaken( $destruction_registry->{$addr} = $_[0] );
448       return 0;
449     }
450     else {
451       carp_unique ( sprintf (
452         'Preventing *MULTIPLE* DESTROY() invocations on %s - an *EXTREMELY '
453       . 'DANGEROUS* condition which is *ALMOST CERTAINLY GLOBAL* within your '
454       . 'application, affecting *ALL* classes without active protection against '
455       . 'this. Diagnose and fix the root cause ASAP!!!%s',
456       refdesc $_[0],
457         ( ( $INC{'Devel/StackTrace.pm'} and ! do { local $@; eval { Devel::StackTrace->VERSION(2) } } )
458           ? " (likely culprit Devel::StackTrace\@@{[ Devel::StackTrace->VERSION ]} found in %INC, http://is.gd/D_ST_refcap)"
459           : ''
460         )
461       ));
462
463       return 1;
464     }
465   }
466 }
467
468 my $module_name_rx = qr/ \A [A-Z_a-z] [0-9A-Z_a-z]* (?: :: [0-9A-Z_a-z]+ )* \z /x;
469 my $ver_rx =         qr/ \A [0-9]+ (?: \. [0-9]+ )* (?: \_ [0-9]+ )*        \z /x;
470
471 sub modver_gt_or_eq ($$) {
472   my ($mod, $ver) = @_;
473
474   croak "Nonsensical module name supplied"
475     if ! defined $mod or $mod !~ $module_name_rx;
476
477   croak "Nonsensical minimum version supplied"
478     if ! defined $ver or $ver !~ $ver_rx;
479
480   no strict 'refs';
481   my $ver_cache = ${"${mod}::__DBIC_MODULE_VERSION_CHECKS__"} ||= ( $mod->VERSION
482     ? {}
483     : croak "$mod does not seem to provide a version (perhaps it never loaded)"
484   );
485
486   ! defined $ver_cache->{$ver}
487     and
488   $ver_cache->{$ver} = do {
489
490     local $SIG{__WARN__} = sigwarn_silencer( qr/\Qisn't numeric in subroutine entry/ )
491       if SPURIOUS_VERSION_CHECK_WARNINGS;
492
493     local $SIG{__DIE__} if $SIG{__DIE__};
494     local $@;
495     eval { $mod->VERSION($ver) } ? 1 : 0;
496   };
497
498   $ver_cache->{$ver};
499 }
500
501 sub modver_gt_or_eq_and_lt ($$$) {
502   my ($mod, $v_ge, $v_lt) = @_;
503
504   croak "Nonsensical maximum version supplied"
505     if ! defined $v_lt or $v_lt !~ $ver_rx;
506
507   return (
508     modver_gt_or_eq($mod, $v_ge)
509       and
510     ! modver_gt_or_eq($mod, $v_lt)
511   ) ? 1 : 0;
512 }
513
514
515 #
516 # Why not just use some higher-level module or at least File::Spec here?
517 # Because:
518 # 1)  This is a *very* rarely used function, and the deptree is large
519 #     enough already as it is
520 #
521 # 2)  (more importantly) Our tooling is utter shit in this area. There
522 #     is no comprehensive support for UNC paths in PathTools and there
523 #     are also various small bugs in representation across different
524 #     path-manipulation CPAN offerings.
525 #
526 # Since this routine is strictly used for logical path processing (it
527 # *must* be able to work with not-yet-existing paths), use this seemingly
528 # simple but I *think* complete implementation to feed to other consumers
529 #
530 # If bugs are ever uncovered in this routine, *YOU ARE URGED TO RESIST*
531 # the impulse to bring in an external dependency. During runtime there
532 # is exactly one spot that could potentially maybe once in a blue moon
533 # use this function. Keep it lean.
534 #
535 sub parent_dir ($) {
536   ( $_[0] =~ m{  [\/\\]  ( \.{0,2} ) ( [\/\\]* ) \z }x )
537     ? (
538       $_[0]
539         .
540       ( ( length($1) and ! length($2) ) ? '/' : '' )
541         .
542       '../'
543     )
544     : (
545       require File::Spec
546         and
547       File::Spec->catpath (
548         ( File::Spec->splitpath( "$_[0]" ) )[0,1],
549         '/',
550       )
551     )
552   ;
553 }
554
555 sub mkdir_p ($) {
556   require File::Path;
557   # do not ask for a recent version, use 1.x API calls
558   File::Path::mkpath([ "$_[0]" ]);  # File::Path does not like objects
559 }
560
561
562 {
563   my $list_ctx_ok_stack_marker;
564
565   sub fail_on_internal_wantarray () {
566     return if $list_ctx_ok_stack_marker;
567
568     if (! defined wantarray) {
569       croak('fail_on_internal_wantarray() needs a tempvar to save the stack marker guard');
570     }
571
572     my $cf = 1;
573     while ( ( (CORE::caller($cf+1))[3] || '' ) =~ / :: (?:
574
575       # these are public API parts that alter behavior on wantarray
576       search | search_related | slice | search_literal
577
578         |
579
580       # these are explicitly prefixed, since we only recognize them as valid
581       # escapes when they come from the guts of CDBICompat
582       CDBICompat .*? :: (?: search_where | retrieve_from_sql | retrieve_all )
583
584     ) $/x ) {
585       $cf++;
586     }
587
588     my ($fr, $want, $argdesc);
589     {
590       package DB;
591       $fr = [ CORE::caller($cf) ];
592       $want = ( CORE::caller($cf-1) )[5];
593       $argdesc = ref $DB::args[0]
594         ? DBIx::Class::_Util::refdesc($DB::args[0])
595         : 'non '
596       ;
597     };
598
599     if (
600       $want and $fr->[0] =~ /^(?:DBIx::Class|DBICx::)/
601     ) {
602       DBIx::Class::Exception->throw( sprintf (
603         "Improper use of %s instance in list context at %s line %d\n\n    Stacktrace starts",
604         $argdesc, @{$fr}[1,2]
605       ), 'with_stacktrace');
606     }
607
608     weaken( $list_ctx_ok_stack_marker = my $mark = [] );
609
610     $mark;
611   }
612 }
613
614 sub fail_on_internal_call {
615   my ($fr, $argdesc);
616   {
617     package DB;
618     $fr = [ CORE::caller(1) ];
619     $argdesc = ref $DB::args[0]
620       ? DBIx::Class::_Util::refdesc($DB::args[0])
621       : ( $DB::args[0] . '' )
622     ;
623   };
624
625   if (
626     $argdesc
627       and
628     $fr->[0] =~ /^(?:DBIx::Class|DBICx::)/
629       and
630     $fr->[1] !~ /\b(?:CDBICompat|ResultSetProxy)\b/  # no point touching there
631   ) {
632     DBIx::Class::Exception->throw( sprintf (
633       "Illegal internal call of indirect proxy-method %s() with argument '%s': examine the last lines of the proxy method deparse below to determine what to call directly instead at %s on line %d\n\n%s\n\n    Stacktrace starts",
634       $fr->[3], $argdesc, @{$fr}[1,2], ( $fr->[6] || do {
635         require B::Deparse;
636         no strict 'refs';
637         B::Deparse->new->coderef2text(\&{$fr->[3]})
638       }),
639     ), 'with_stacktrace');
640   }
641 }
642
643 1;