summaryrefslogtreecommitdiff
path: root/t/03handle.t
blob: 7440ad0d15fdad07e11f33d66a01f940ef9e9181 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
#!perl -w
$|=1;

use strict;

use Test::More tests => 137;

## ----------------------------------------------------------------------------
## 03handle.t - tests handles
## ----------------------------------------------------------------------------
# This set of tests exercises the different handles; Driver, Database and 
# Statement in various ways, in particular in their interactions with one
# another
## ----------------------------------------------------------------------------

BEGIN { 
    use_ok( 'DBI' );
}

# installed drivers should start empty
my %drivers = DBI->installed_drivers();
is(scalar keys %drivers, 0);

## ----------------------------------------------------------------------------
# get the Driver handle

my $driver = "ExampleP";

my $drh = DBI->install_driver($driver);
isa_ok( $drh, 'DBI::dr' );

SKIP: {
    skip "Kids attribute not supported under DBI::PurePerl", 1 if $DBI::PurePerl;
    
    cmp_ok($drh->{Kids}, '==', 0, '... this Driver does not yet have any Kids');
}

# now the driver should be registered
%drivers = DBI->installed_drivers();
is(scalar keys %drivers, 1);
ok(exists $drivers{ExampleP});
ok($drivers{ExampleP}->isa('DBI::dr'));

my $using_dbd_gofer = ($ENV{DBI_AUTOPROXY}||'') =~ /^dbi:Gofer.*transport=/i;

## ----------------------------------------------------------------------------
# do database handle tests inside do BLOCK to capture scope

do {
    my $dbh = DBI->connect("dbi:$driver:", '', '');
    isa_ok($dbh, 'DBI::db');

    my $drh = $dbh->{Driver}; # (re)get drh here so tests can work using_dbd_gofer
    
    SKIP: {
        skip "Kids and ActiveKids attributes not supported under DBI::PurePerl", 2 if $DBI::PurePerl;
    
        cmp_ok($drh->{Kids}, '==', 1, '... our Driver has one Kid');
        cmp_ok($drh->{ActiveKids}, '==', 1, '... our Driver has one ActiveKid');  
    }

    my $sql = "select name from ?";

    my $sth1 = $dbh->prepare_cached($sql);
    isa_ok($sth1, 'DBI::st');    
    ok($sth1->execute("."), '... execute ran successfully');

    my $ck = $dbh->{CachedKids};
    is(ref($ck), "HASH", '... we got the CachedKids hash');
    
    cmp_ok(scalar(keys(%{$ck})), '==', 1, '... there is one CachedKid');
    ok(eq_set(
        [ values %{$ck} ],
        [ $sth1 ]
        ), 
    '... our statment handle should be in the CachedKids');

    ok($sth1->{Active}, '... our first statment is Active');
    
    {
	my $warn = 0; # use this to check that we are warned
	local $SIG{__WARN__} = sub { ++$warn if $_[0] =~ /still active/i };
	
	my $sth2 = $dbh->prepare_cached($sql);
	isa_ok($sth2, 'DBI::st');
	
	is($sth1, $sth2, '... prepare_cached returned the same statement handle');
	cmp_ok($warn,'==', 1, '... we got warned about our first statement handle being still active');
	
	ok(!$sth1->{Active}, '... our first statment is no longer Active since we re-prepared it');

	my $sth3 = $dbh->prepare_cached($sql, { foo => 1 });
	isa_ok($sth3, 'DBI::st');
	
	isnt($sth1, $sth3, '... prepare_cached returned a different statement handle now');
	cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');
	ok(eq_set(
	    [ values %{$ck} ],
	    [ $sth1, $sth3 ]
	    ), 
	'... both statment handles should be in the CachedKids');    

	ok($sth1->execute("."), '... executing first statement handle again');
	ok($sth1->{Active}, '... first statement handle is now active again');
	
	my $sth4 = $dbh->prepare_cached($sql, undef, 3);
	isa_ok($sth4, 'DBI::st');
	
	isnt($sth1, $sth4, '... our fourth statement handle is not the same as our first');
	ok($sth1->{Active}, '... first statement handle is still active');
	
	cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');    
	ok(eq_set(
	    [ values %{$ck} ],
	    [ $sth2, $sth4 ]
	    ), 
	'... second and fourth statment handles should be in the CachedKids');      
	
	$sth1->finish;
	ok(!$sth1->{Active}, '... first statement handle is no longer active');    

	ok($sth4->execute("."), '... fourth statement handle executed properly');
	ok($sth4->{Active}, '... fourth statement handle is Active');

	my $sth5 = $dbh->prepare_cached($sql, undef, 1);
	isa_ok($sth5, 'DBI::st');
	
	cmp_ok($warn, '==', 1, '... we still only got one warning');

	is($sth4, $sth5, '... fourth statement handle and fifth one match');
	ok(!$sth4->{Active}, '... fourth statement handle is not Active');
	ok(!$sth5->{Active}, '... fifth statement handle is not Active (shouldnt be its the same as fifth)');
	
	cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');    
	ok(eq_set(
	    [ values %{$ck} ],
	    [ $sth2, $sth5 ]
	    ), 
	'... second and fourth/fifth statment handles should be in the CachedKids');     
    }

    SKIP: {
	skip "swap_inner_handle() not supported under DBI::PurePerl", 23 if $DBI::PurePerl;
    
        my $sth6 = $dbh->prepare($sql);
        $sth6->execute(".");
        my $sth1_driver_name = $sth1->{Database}{Driver}{Name};

        ok( $sth6->{Active}, '... sixth statement handle is active');
        ok(!$sth1->{Active}, '... first statement handle is not active');

        ok($sth1->swap_inner_handle($sth6), '... first statement handle becomes the sixth');
        ok(!$sth6->{Active}, '... sixth statement handle is now not active');
        ok( $sth1->{Active}, '... first statement handle is now active again');

        ok($sth1->swap_inner_handle($sth6), '... first statement handle becomes the sixth');
        ok( $sth6->{Active}, '... sixth statement handle is active');
        ok(!$sth1->{Active}, '... first statement handle is not active');

        ok($sth1->swap_inner_handle($sth6), '... first statement handle becomes the sixth');
        ok(!$sth6->{Active}, '... sixth statement handle is now not active');
        ok( $sth1->{Active}, '... first statement handle is now active again');

	$sth1->{PrintError} = 0;
        ok(!$sth1->swap_inner_handle($dbh), '... can not swap a sth with a dbh');
	cmp_ok( $sth1->errstr, 'eq', "Can't swap_inner_handle between sth and dbh");

        ok($sth1->swap_inner_handle($sth6), '... first statement handle becomes the sixth');
        ok( $sth6->{Active}, '... sixth statement handle is active');
        ok(!$sth1->{Active}, '... first statement handle is not active');

        $sth6->finish;

	ok(my $dbh_nullp = DBI->connect("dbi:NullP:", undef, undef, { go_bypass => 1 }));
	ok(my $sth7 = $dbh_nullp->prepare(""));

	$sth1->{PrintError} = 0;
        ok(!$sth1->swap_inner_handle($sth7), "... can't swap_inner_handle with handle from different parent");
	cmp_ok( $sth1->errstr, 'eq', "Can't swap_inner_handle with handle from different parent");

	cmp_ok( $sth1->{Database}{Driver}{Name}, 'eq', $sth1_driver_name );
        ok( $sth1->swap_inner_handle($sth7,1), "... can swap to different parent if forced");
	cmp_ok( $sth1->{Database}{Driver}{Name}, 'eq', "NullP" );

	$dbh_nullp->disconnect;
    }

    ok(  $dbh->ping, 'ping should be true before disconnect');
    $dbh->disconnect;
    $dbh->{PrintError} = 0; # silence 'not connected' warning
    ok( !$dbh->ping, 'ping should be false after disconnect');

    SKIP: {
        skip "Kids and ActiveKids attributes not supported under DBI::PurePerl", 2 if $DBI::PurePerl;
    
        cmp_ok($drh->{Kids}, '==', 1, '... our Driver has one Kid after disconnect');
        cmp_ok($drh->{ActiveKids}, '==', 0, '... our Driver has no ActiveKids after disconnect');      
    }
    
};

if ($using_dbd_gofer) {
    $drh->{CachedKids} = {};
}

# make sure our driver has no more kids after this test
# NOTE:
# this also assures us that the next test has an empty slate as well
SKIP: {
    skip "Kids attribute not supported under DBI::PurePerl", 1 if $DBI::PurePerl;
    
    cmp_ok($drh->{Kids}, '==', 0, "... our $drh->{Name} driver should have 0 Kids after dbh was destoryed");
}

## ----------------------------------------------------------------------------
# handle reference leak tests

# NOTE: 
# this test checks for reference leaks by testing the Kids attribute
# which is not supported by DBI::PurePerl, so we just do not run this
# for DBI::PurePerl all together. Even though some of the tests would
# pass, it does not make sense becuase in the end, what is actually
# being tested for will give a false positive

sub work {
    my (%args) = @_;
    my $dbh = DBI->connect("dbi:$driver:", '', '');
    isa_ok( $dbh, 'DBI::db' );
    
    cmp_ok($drh->{Kids}, '==', 1, '... the Driver should have 1 Kid(s) now'); 
    
    if ( $args{Driver} ) {
        isa_ok( $dbh->{Driver}, 'DBI::dr' );
    } else {
        pass( "not testing Driver here" );
    }

    my $sth = $dbh->prepare_cached("select name from ?");
    isa_ok( $sth, 'DBI::st' );
    
    if ( $args{Database} ) {
        isa_ok( $sth->{Database}, 'DBI::db' );
    } else {
        pass( "not testing Database here" );
    }
    
    $dbh->disconnect;
    # both handles should be freed here
}

SKIP: {
    skip "Kids attribute not supported under DBI::PurePerl", 25 if $DBI::PurePerl;
    skip "drh Kids not testable under DBD::Gofer", 25 if $using_dbd_gofer;

    foreach my $args (
        {},
        { Driver   => 1 },
        { Database => 1 },
        { Driver   => 1, Database => 1 },
    ) {
        work( %{$args} );
        cmp_ok($drh->{Kids}, '==', 0, '... the Driver should have no Kids');
    }

    # make sure we have no kids when we end this
    cmp_ok($drh->{Kids}, '==', 0, '... the Driver should have no Kids at the end of this test');
}

## ----------------------------------------------------------------------------
# handle take_imp_data test

SKIP: {
    skip "take_imp_data test not supported under DBD::Gofer", 19 if $using_dbd_gofer;

    my $dbh = DBI->connect("dbi:$driver:", '', '');
    isa_ok($dbh, "DBI::db");
    my $drh = $dbh->{Driver}; # (re)get drh here so tests can work using_dbd_gofer

    cmp_ok($drh->{Kids}, '==', 1, '... our Driver should have 1 Kid(s) here')
        unless $DBI::PurePerl && pass();

    $dbh->prepare("select name from ?"); # destroyed at once
    my $sth2 = $dbh->prepare("select name from ?"); # inactive
    my $sth3 = $dbh->prepare("select name from ?"); # active:
    $sth3->execute(".");
    is $sth3->{Active}, 1;
    is $dbh->{ActiveKids}, 1
        unless $DBI::PurePerl && pass();

    my $ChildHandles = $dbh->{ChildHandles};

    skip "take_imp_data test needs weakrefs", 15 if not $ChildHandles;

    ok $ChildHandles, 'we need weakrefs for take_imp_data to work safely with child handles';
    is @$ChildHandles, 3, 'should have 3 entries (implementation detail)';
    is grep({ defined } @$ChildHandles), 2, 'should have 2 defined handles';

    my $imp_data = $dbh->take_imp_data;
    ok($imp_data, '... we got some imp_data to test');
    # generally length($imp_data) = 112 for 32bit, 116 for 64 bit
    # (as of DBI 1.37) but it can differ on some platforms
    # depending on structure packing by the compiler
    # so we just test that it's something reasonable:
    cmp_ok(length($imp_data), '>=', 80, '... test that our imp_data is greater than or equal to 80, this is reasonable');

    cmp_ok($drh->{Kids}, '==', 0, '... our Driver should have 0 Kid(s) after calling take_imp_data');

    is ref $sth3, 'DBI::zombie', 'sth should be reblessed';
    eval { $sth3->finish };
    like $@, qr/Can't locate object method/;

    {
        my @warn;
        local $SIG{__WARN__} = sub { push @warn, $_[0] if $_[0] =~ /after take_imp_data/; print "warn: @_\n"; };
        
        my $drh = $dbh->{Driver};
        ok(!defined $drh, '... our Driver should be undefined');
        
        my $trace_level = $dbh->{TraceLevel};
        ok(!defined $trace_level, '... our TraceLevel should be undefined');

        ok(!defined $dbh->disconnect, '... disconnect should return undef');

        ok(!defined $dbh->quote(42), '... quote should return undefined');

        cmp_ok(scalar @warn, '==', 4, '... we should have gotten 4 warnings');
    }

    my $dbh2 = DBI->connect("dbi:$driver:", '', '', { dbi_imp_data => $imp_data });
    isa_ok($dbh2, "DBI::db");
    # need a way to test dbi_imp_data has been used
    
    cmp_ok($drh->{Kids}, '==', 1, '... our Driver should have 1 Kid(s) again')
        unless $DBI::PurePerl && pass();
    
}

# we need this SKIP block on its own since we are testing the 
# destruction of objects within the scope of the above SKIP 
# block
SKIP: {
    skip "Kids attribute not supported under DBI::PurePerl", 1 if $DBI::PurePerl;
    
    cmp_ok($drh->{Kids}, '==', 0, '... our Driver has no Kids after this test');
}

## ----------------------------------------------------------------------------
# NullP statement handle attributes without execute

my $driver2 = "NullP";

my $drh2 = DBI->install_driver($driver);
isa_ok( $drh2, 'DBI::dr' );

SKIP: {
    skip "Kids attribute not supported under DBI::PurePerl", 1 if $DBI::PurePerl;
    
    cmp_ok($drh2->{Kids}, '==', 0, '... our Driver (2) has no Kids before this test');
}

do {
    my $dbh = DBI->connect("dbi:$driver2:", '', '');
    isa_ok($dbh, "DBI::db");

    my $sth = $dbh->prepare("foo bar");
    isa_ok($sth, "DBI::st");

    cmp_ok($sth->{NUM_OF_PARAMS}, '==', 0, '... NUM_OF_PARAMS is 0');
    is($sth->{NUM_OF_FIELDS}, undef, '... NUM_OF_FIELDS should be undef');
    is($sth->{Statement}, "foo bar", '... Statement is "foo bar"');

    ok(!defined $sth->{NAME},         '... NAME is undefined');
    ok(!defined $sth->{TYPE},         '... TYPE is undefined');
    ok(!defined $sth->{SCALE},        '... SCALE is undefined');
    ok(!defined $sth->{PRECISION},    '... PRECISION is undefined');
    ok(!defined $sth->{NULLABLE},     '... NULLABLE is undefined');
    ok(!defined $sth->{RowsInCache},  '... RowsInCache is undefined');
    ok(!defined $sth->{ParamValues},  '... ParamValues is undefined');
    # derived NAME attributes
    ok(!defined $sth->{NAME_uc},      '... NAME_uc is undefined');
    ok(!defined $sth->{NAME_lc},      '... NAME_lc is undefined');
    ok(!defined $sth->{NAME_hash},    '... NAME_hash is undefined');
    ok(!defined $sth->{NAME_uc_hash}, '... NAME_uc_hash is undefined');
    ok(!defined $sth->{NAME_lc_hash}, '... NAME_lc_hash is undefined');

    my $dbh_ref = ref($dbh);
    my $sth_ref = ref($sth);

    ok($dbh_ref->can("prepare"), '... $dbh can call "prepare"');
    ok(!$dbh_ref->can("nonesuch"), '... $dbh cannot call "nonesuch"');
    ok($sth_ref->can("execute"), '... $sth can call "execute"');

    # what is this test for??

    # I don't know why this warning has the "(perhaps ...)" suffix, it shouldn't:
    # Can't locate object method "nonesuch" via package "DBI::db" (perhaps you forgot to load "DBI::db"?)
    eval { ref($dbh)->nonesuch; };

    $dbh->disconnect;
};

SKIP: {
    skip "Kids attribute not supported under DBI::PurePerl", 1 if $DBI::PurePerl;
    
    cmp_ok($drh2->{Kids}, '==', 0, '... our Driver (2) has no Kids after this test');
}

## ----------------------------------------------------------------------------

1;