1#!/usr/bin/perl -w
2
3
4use strict;
5use warnings;
6use Test::More;
7BEGIN { require "t/utils.pl" }
8our (@AvailableDrivers);
9
10use constant TESTS_PER_DRIVER => 150;
11
12my $total = scalar(@AvailableDrivers) * TESTS_PER_DRIVER;
13plan tests => $total;
14
15foreach my $d ( @AvailableDrivers ) {
16SKIP: {
17	unless( has_schema( 'TestApp', $d ) ) {
18		skip "No schema for '$d' driver", TESTS_PER_DRIVER;
19	}
20	unless( should_test( $d ) ) {
21		skip "ENV is not defined for driver '$d'", TESTS_PER_DRIVER;
22	}
23
24	my $handle = get_handle( $d );
25	connect_handle( $handle );
26	isa_ok($handle->dbh, 'DBI::db');
27
28	my $ret = init_schema( 'TestApp', $handle );
29	isa_ok($ret,'DBI::st', "Inserted the schema. got a statement handle back");
30
31	my $count_all = init_data( 'TestApp::User', $handle );
32	ok( $count_all,  "init users data" );
33
34	my $users_obj = TestApp::Users->new( $handle );
35	isa_ok( $users_obj, 'DBIx::SearchBuilder' );
36	is( $users_obj->_Handle, $handle, "same handle as we used in constructor");
37
38# check that new object returns 0 records in any case
39	is( $users_obj->_RecordCount, 0, '_RecordCount returns 0 on not limited obj' );
40	is( $users_obj->Count, 0, 'Count returns 0 on not limited obj' );
41	is( $users_obj->IsLast, undef, 'IsLast returns undef on not limited obj after Count' );
42	is( $users_obj->First, undef, 'First returns undef on not limited obj' );
43	is( $users_obj->IsLast, undef, 'IsLast returns undef on not limited obj after First' );
44	is( $users_obj->Last, undef, 'Last returns undef on not limited obj' );
45	is( $users_obj->IsLast, undef, 'IsLast returns undef on not limited obj after Last' );
46	$users_obj->GotoFirstItem;
47	is( $users_obj->Next, undef, 'Next returns undef on not limited obj' );
48	is( $users_obj->IsLast, undef, 'IsLast returns undef on not limited obj after Next' );
49	# XXX TODO FIXME: may be this methods should be implemented
50	# $users_obj->GotoLastItem;
51	# is( $users_obj->Prev, undef, 'Prev returns undef on not limited obj' );
52	my $items_ref = $users_obj->ItemsArrayRef;
53	isa_ok( $items_ref, 'ARRAY', 'ItemsArrayRef always returns array reference' );
54	is_deeply( $items_ref, [], 'ItemsArrayRef returns [] on not limited obj' );
55
56# unlimit new object and check
57	$users_obj->UnLimit;
58	is( $users_obj->Count, $count_all, 'Count returns same number of records as was inserted' );
59	isa_ok( $users_obj->First, 'DBIx::SearchBuilder::Record', 'First returns record object' );
60	isa_ok( $users_obj->Last, 'DBIx::SearchBuilder::Record', 'Last returns record object' );
61	$users_obj->GotoFirstItem;
62	isa_ok( $users_obj->Next, 'DBIx::SearchBuilder::Record', 'Next returns record object' );
63	$items_ref = $users_obj->ItemsArrayRef;
64	isa_ok( $items_ref, 'ARRAY', 'ItemsArrayRef always returns array reference' );
65	is( scalar @{$items_ref}, $count_all, 'ItemsArrayRef returns same number of records as was inserted' );
66	$users_obj->RedoSearch;
67	$items_ref = $users_obj->ItemsArrayRef;
68	isa_ok( $items_ref, 'ARRAY', 'ItemsArrayRef always returns array reference' );
69	is( scalar @{$items_ref}, $count_all, 'ItemsArrayRef returns same number of records as was inserted' );
70
71# try to use $users_obj for all tests, after each call to CleanSlate it should look like new obj.
72# and test $obj->new syntax
73	my $clean_obj = $users_obj->new( $handle );
74	isa_ok( $clean_obj, 'DBIx::SearchBuilder' );
75
76# basic limits
77	$users_obj->CleanSlate;
78	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
79	$users_obj->Limit( FIELD => 'Login', VALUE => 'obra' );
80	is( $users_obj->Count, 1, 'found one user with login obra' );
81	TODO: {
82		local $TODO = 'require discussion';
83		is( $users_obj->IsLast, undef, 'IsLast returns undef before we fetch any record' );
84	}
85	my $first_rec = $users_obj->First;
86	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
87	is( $users_obj->IsLast, 1, '1 record in the collection then first rec is last');
88	is( $first_rec->Login, 'obra', 'login is correct' );
89	my $last_rec = $users_obj->Last;
90	is( $last_rec, $first_rec, 'Last returns same object as First' );
91	is( $users_obj->IsLast, 1, 'IsLast always returns 1 after Last call');
92	$users_obj->GotoFirstItem;
93	my $next_rec = $users_obj->Next;
94	is( $next_rec, $first_rec, 'Next returns same object as First' );
95	is( $users_obj->IsLast, 1, 'IsLast returns 1 after fetch first record with Next method');
96	is( $users_obj->Next, undef, 'only one record in the collection' );
97	TODO: {
98		local $TODO = 'require discussion';
99		is( $users_obj->IsLast, undef, 'Next returns undef, IsLast returns undef too');
100	}
101	$items_ref = $users_obj->ItemsArrayRef;
102	isa_ok( $items_ref, 'ARRAY', 'ItemsArrayRef always returns array reference' );
103	is( scalar @{$items_ref}, 1, 'ItemsArrayRef has only 1 record' );
104
105# similar basic limit, but with different OPERATORS and less First/Next/Last tests
106	# LIKE
107	$users_obj->CleanSlate;
108	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
109	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'LIKE', VALUE => 'Glass' );
110	is( $users_obj->Count, 1, "found one user with 'Glass' in the name" );
111	$first_rec = $users_obj->First;
112	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
113	is( $first_rec->Login, 'glasser', 'login is correct' );
114
115	# MATCHES
116	$users_obj->CleanSlate;
117	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
118	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'MATCHES', VALUE => 'lass' );
119	is( $users_obj->Count, 0, "found no user matching 'lass' in the name" );
120
121	$users_obj->CleanSlate;
122	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
123	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'MATCHES', VALUE => '%lass' );
124	is( $users_obj->Count, 0, "found no user matching '%lass' in the name" );
125
126	$users_obj->CleanSlate;
127	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
128	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'MATCHES', VALUE => 'lass%' );
129	is( $users_obj->Count, 0, "found no user matching 'lass%' in the name" );
130
131	$users_obj->CleanSlate;
132	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
133	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'MATCHES', VALUE => '%lass%' );
134	is( $users_obj->Count, 1, "found one user matching '%lass%' in the name" );
135	$first_rec = $users_obj->First;
136	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
137	is( $first_rec->Login, 'glasser', 'login is correct' );
138
139	# STARTSWITH
140	$users_obj->CleanSlate;
141	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
142	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'STARTSWITH', VALUE => 'Ruslan' );
143	is( $users_obj->Count, 1, "found one user who name starts with 'Ruslan'" );
144	$first_rec = $users_obj->First;
145	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
146	is( $first_rec->Login, 'cubic', 'login is correct' );
147
148	# ENDSWITH
149	$users_obj->CleanSlate;
150	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
151	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'ENDSWITH', VALUE => 'Tang' );
152	is( $users_obj->Count, 1, "found one user who name ends with 'Tang'" );
153	$first_rec = $users_obj->First;
154	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
155	is( $first_rec->Login, 'autrijus', 'login is correct' );
156
157	# IS NULL
158	# XXX TODO FIXME: FIELD => undef should be handled as NULL
159	$users_obj->CleanSlate;
160	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
161	$users_obj->Limit( FIELD => 'Phone', OPERATOR => 'IS', VALUE => 'NULL' );
162	is( $users_obj->Count, 2, "found 2 users who has unknown phone number" );
163
164	# IS NOT NULL
165	$users_obj->CleanSlate;
166	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
167	$users_obj->Limit( FIELD => 'Phone', OPERATOR => 'IS NOT', VALUE => 'NULL', QOUTEVALUE => 0 );
168	is( $users_obj->Count, $count_all - 2, "found users who has phone number filled" );
169
170	# IN [...] operator
171	$users_obj->CleanSlate;
172	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
173	$users_obj->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => ['obra', 'cubic'] );
174	is( $users_obj->Count, 2, "found two users using IN operator" );
175	is_deeply(
176        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
177        [ 'cubic', 'obra' ],
178        'found correct records',
179    );
180	$users_obj->CleanSlate;
181	$users_obj->Limit( FIELD => 'Login', OPERATOR => 'NOT IN', VALUE => ['obra', 'cubic'] );
182	is( $users_obj->Count, 2, "found two users using NOT IN operator" );
183	is_deeply(
184        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
185        [ 'autrijus', 'glasser' ],
186        'found correct records',
187    );
188
189	# IN $collection operator
190	$users_obj->CleanSlate;
191	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
192    {
193        my $tmp = $users_obj->Clone;
194        $tmp->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => ['obra', 'cubic'] );
195        $users_obj->Limit( FIELD => 'id', OPERATOR => 'IN', VALUE => $tmp );
196    }
197	is( $users_obj->Count, 2, "found two users using IN operator" );
198	is_deeply(
199        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
200        [ 'cubic', 'obra' ],
201        'found correct records',
202    );
203	$users_obj->CleanSlate;
204    {
205        my $tmp = $users_obj->Clone;
206        $tmp->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => ['obra', 'cubic'] );
207        $users_obj->Limit( FIELD => 'id', OPERATOR => 'NOT IN', VALUE => $tmp );
208    }
209	is( $users_obj->Count, 2, "found two users using IN operator" );
210	is_deeply(
211        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
212        [ 'autrijus', 'glasser' ],
213        'found correct records',
214    );
215	# IN with object and Column preselected
216	$users_obj->CleanSlate;
217	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
218    {
219        my $tmp = $users_obj->Clone;
220        $tmp->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => ['obra', 'cubic'] );
221        $tmp->Column(FIELD => 'Login');
222        $users_obj->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => $tmp );
223    }
224	is( $users_obj->Count, 2, "found two users using IN operator" );
225	is_deeply(
226        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
227        [ 'cubic', 'obra' ],
228        'found correct records',
229    );
230
231	# ORDER BY / GROUP BY
232	$users_obj->CleanSlate;
233	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
234	$users_obj->UnLimit;
235	$users_obj->GroupByCols({FIELD => 'Login'});
236	$users_obj->OrderBy(FIELD => 'Login', ORDER => 'desc');
237	$users_obj->Column(FIELD => 'Login');
238	is( $users_obj->Count, $count_all, "group by / order by finds right amount");
239	$first_rec = $users_obj->First;
240	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
241	is( $first_rec->Login, 'obra', 'login is correct' );
242
243	$users_obj->CleanSlate;
244	TODO: {
245        local $TODO = 'we leave order_by after clean slate, fixing this results in many RT failures';
246        is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
247    }
248
249    {
250	    $users_obj = TestApp::Users->new( $handle );
251        $users_obj->UnLimit;
252        $users_obj->GroupBy({FUNCTION => 'Login'});
253        $users_obj->OrderBy(FIELD => 'Login', ORDER => 'desc');
254        $users_obj->Column(FIELD => 'Login');
255        is( $users_obj->Count, $count_all, "group by / order by finds right amount");
256        $first_rec = $users_obj->First;
257        isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
258        is( $first_rec->Login, 'obra', 'login is correct' );
259    }
260    {
261	    $users_obj = TestApp::Users->new( $handle );
262        $users_obj->UnLimit;
263        $users_obj->GroupBy({FUNCTION => 'SUBSTR(Login, 1, 1)', });
264        $users_obj->Column(FIELD => 'Login', FUNCTION => 'SUBSTR(Login, 1, 1)');
265        my @list = sort map $_->Login, @{ $users_obj->ItemsArrayRef };
266        is_deeply( \@list, [qw(a c g o)], 'correct values' );
267    }
268    {
269	    $users_obj = TestApp::Users->new( $handle );
270        $users_obj->UnLimit;
271        $users_obj->GroupBy({FUNCTION => 'SUBSTR(?, 1, 1)', FIELD => 'Login'});
272        $users_obj->Column(FIELD => 'Login', FUNCTION => 'SUBSTR(?, 1, 1)');
273        my @list = sort map $_->Login, @{ $users_obj->ItemsArrayRef };
274        is_deeply( \@list, [qw(a c g o)], 'correct values' );
275    }
276    $users_obj = TestApp::Users->new( $handle );
277
278# Let's play a little with ENTRYAGGREGATOR
279    # EA defaults to OR for the same field
280	$users_obj->Limit( FIELD => 'Phone', OPERATOR => 'IS', VALUE => 'NULL', QOUTEVALUE => 0 );
281	$users_obj->Limit( FIELD => 'Phone', OPERATOR => 'LIKE', VALUE => 'X' );
282	is( $users_obj->Count, 4, "found users who has no phone or it has X char" );
283
284    # set AND for the same field
285	$users_obj->CleanSlate;
286	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
287	$users_obj->Limit( FIELD => 'Login', OPERATOR => 'NOT LIKE', VALUE => 'c' );
288	$users_obj->Limit(
289        ENTRYAGGREGATOR => 'AND', FIELD => 'Login', OPERATOR => 'LIKE', VALUE => 'u'
290    );
291	is( $users_obj->Count, 1, "found users who has no phone or it has X char" );
292
293    # default is AND for different fields
294	$users_obj->CleanSlate;
295	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
296	$users_obj->Limit( FIELD => 'Phone', OPERATOR => 'IS', VALUE => 'NULL', QOUTEVALUE => 0 );
297	$users_obj->Limit( FIELD => 'Login', OPERATOR => 'LIKE', VALUE => 'r' );
298	is( $users_obj->Count, 2, "found users who has no phone number or login has 'r' char" );
299
300# Let's play with RowsPerPage
301    # RowsPerPage(0)
302    # https://rt.cpan.org/Ticket/Display.html?id=42988
303	$users_obj->CleanSlate;
304	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
305    $users_obj->UnLimit;
306    $users_obj->RowsPerPage(0);
307	is( $users_obj->Count, $count_all, "found all users" );
308    ok( $users_obj->First, "fetched first user" );
309
310    # walk all pages
311	$users_obj->CleanSlate;
312	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
313    $users_obj->UnLimit;
314	$users_obj->OrderBy(FIELD => 'Login');
315    $users_obj->RowsPerPage(2);
316    {
317        my %seen;
318        my $saw_on_page = 0;
319        my $pages = 0;
320        my $prev_login = '';
321        do {
322            $saw_on_page = 0;
323            while ( my $user = $users_obj->Next ) {
324                $saw_on_page++;
325                $seen{ $user->id }++;
326                ok( $prev_login lt $user->Login, "order is correct" );
327            }
328            last unless $saw_on_page;
329
330            $pages++;
331            if ( $pages * 2 <= $count_all ) {
332                is( $saw_on_page, 2, "saw only two on the page" );
333            } else {
334                is( $saw_on_page, $count_all - ($pages * 2), "saw slightly less users on the last page");
335            }
336            $users_obj->NextPage;
337        } while ( $saw_on_page );
338
339        ok( !grep( $_ != 1, values %seen ), "saw each user only once") or do { use Data::Dumper; diag Dumper(\%seen) };
340        is( scalar keys %seen, $count_all, "saw all users" )
341    }
342
343    # two steps forward, on step back
344    $users_obj = TestApp::Users->new( $handle );
345    $users_obj->UnLimit;
346	$users_obj->OrderBy(FIELD => 'Login');
347    $users_obj->RowsPerPage(1);
348    for ( 1 .. $count_all-1) {
349        my $u = $users_obj->Next;
350        ok( $u, "got a user");
351        ok(!$users_obj->Next, "only on the page");
352
353        $users_obj->NextPage;
354        isnt( $users_obj->Next->id, $u->id, "got a user and he is different");
355        ok(!$users_obj->Next, "only on the page");
356
357        $users_obj->PrevPage;
358        is( $users_obj->Next->id, $u->id, "got a user and he is the same");
359        ok(!$users_obj->Next, "only on the page");
360
361        $users_obj->NextPage;
362    }
363
364    # tricky variant: skip 1, but show 2
365    $users_obj = TestApp::Users->new( $handle );
366    $users_obj->UnLimit;
367	$users_obj->OrderBy(FIELD => 'Login');
368    $users_obj->RowsPerPage(2);
369    $users_obj->FirstRow(2);
370    {
371        my $u = $users_obj->Next;
372        is( $u->Login, 'cubic', "cubic is second in the list");
373    }
374    {
375        my $u = $users_obj->Next;
376        is( $u->Login, 'glasser', "glasser is third in the list");
377    }
378
379# Let's play with Column
380    $users_obj = TestApp::Users->new( $handle );
381    $users_obj->UnLimit;
382    {
383        is( $users_obj->Column(FIELD => 'id'), 'id' );
384        isnt( my $id_alias = $users_obj->Column(FIELD => 'id'), 'id' );
385        my $u = $users_obj->Next;
386        is ( $u->_Value($id_alias), $u->id, "fetched id twice" );
387    }
388
389    $users_obj = TestApp::Users->new( $handle );
390    $users_obj->UnLimit;
391    {
392        is( $users_obj->Column(FIELD => 'id'), 'id' );
393        isnt( my $id_alias = $users_obj->Column(FIELD => 'id', FUNCTION => '? + 1'), 'id' );
394        my $u = $users_obj->Next;
395        is ( $u->_Value($id_alias), $u->id + 1, "fetched id and function based on id" )
396            or diag "wrong SQL: ". $users_obj->BuildSelectQuery;
397    }
398
399    $users_obj = TestApp::Users->new( $handle );
400    $users_obj->UnLimit;
401    {
402        is( $users_obj->Column(FIELD => 'id'), 'id' );
403        isnt( my $id_alias = $users_obj->Column(FUNCTION => 'id + 1'), 'id' );
404        my $u = $users_obj->Next;
405        is ( $u->_Value($id_alias), $u->id + 1, "fetched id and function based on id" );
406    }
407
408    $users_obj = TestApp::Users->new( $handle );
409    $users_obj->UnLimit;
410    {
411        is( $users_obj->Column(FIELD => 'id'), 'id' );
412        isnt( my $id_alias = $users_obj->Column(FUNCTION => '?', FIELD => 'id'), 'id' );
413        my $u = $users_obj->Next;
414        is ( $u->_Value($id_alias), $u->id, "fetched with '?' function" );
415    }
416
417    $users_obj = TestApp::Users->new( $handle );
418    $users_obj->UnLimit;
419    {
420        is( $users_obj->Column(FIELD => 'id'), "id" );
421        is( my $id_alias = $users_obj->Column(FIELD => 'id', AS => 'foo'), "foo" );
422        my $u = $users_obj->Next;
423        is( $u->_Value($id_alias), $u->id, "fetched id with custom alias" );
424    }
425
426    $users_obj = TestApp::Users->new( $handle );
427    $users_obj->UnLimit;
428    {
429        is( $users_obj->Column(FUNCTION => "main.*", AS => undef), undef );
430        my $u = $users_obj->Next;
431        ok $u->{fetched}{"\L$_"}, "fetched field $_" for keys %{$u->_ClassAccessible};
432    }
433
434    $users_obj = TestApp::Users->new( $handle );
435    $users_obj->UnLimit;
436    {
437        is( my $id_alias = $users_obj->AdditionalColumn(FIELD => 'id', AS => 'foo'), "foo" );
438        my $u = $users_obj->Next;
439        is( $u->_Value($id_alias), $u->id, "fetched id with custom alias" );
440        ok $u->{fetched}{"\L$_"}, "fetched normal field $_" for keys %{$u->_ClassAccessible};
441    }
442
443    # Last without running the search first
444    $users_obj = TestApp::Users->new( $handle );
445    $users_obj->UnLimit;
446    $users_obj->OrderBy( FIELD => "Login", ORDER => "ASC" );
447    is $users_obj->Last->Login, "obra", "Found last record correctly before search was run";
448
449	cleanup_schema( 'TestApp', $handle );
450}} # SKIP, foreach blocks
451
4521;
453
454package TestApp;
455
456sub schema_mysql {[
457	"DROP TABLE IF EXISTS Users",
458<<EOF
459CREATE TABLE Users (
460        id integer AUTO_INCREMENT,
461        Login varchar(18) NOT NULL,
462        Name varchar(36),
463	Phone varchar(18),
464  	PRIMARY KEY (id))
465EOF
466]}
467sub cleanup_schema_mysql { [
468    "DROP TABLE Users",
469] }
470
471sub schema_pg {
472<<EOF;
473CREATE TEMPORARY TABLE Users (
474        id serial PRIMARY KEY,
475        Login varchar(18) NOT NULL,
476        Name varchar(36),
477        Phone varchar(18)
478)
479EOF
480
481}
482
483sub schema_sqlite {
484
485<<EOF;
486CREATE TABLE Users (
487	id integer primary key,
488	Login varchar(18) NOT NULL,
489	Name varchar(36),
490	Phone varchar(18))
491EOF
492
493}
494
495sub schema_oracle { [
496    "CREATE SEQUENCE Users_seq",
497    "CREATE TABLE Users (
498        id integer CONSTRAINT Users_Key PRIMARY KEY,
499        Login varchar(18) NOT NULL,
500        Name varchar(36),
501        Phone varchar(18)
502    )",
503] }
504
505sub cleanup_schema_oracle { [
506    "DROP SEQUENCE Users_seq",
507    "DROP TABLE Users",
508] }
509
510
5111;
512
513package TestApp::User;
514
515use base $ENV{SB_TEST_CACHABLE}?
516    qw/DBIx::SearchBuilder::Record::Cachable/:
517    qw/DBIx::SearchBuilder::Record/;
518
519sub _Init {
520    my $self = shift;
521    my $handle = shift;
522    $self->Table('Users');
523    $self->_Handle($handle);
524}
525
526sub _ClassAccessible {
527    {
528        id =>
529        {read => 1, type => 'int(11)' },
530        Login =>
531        {read => 1, write => 1, type => 'varchar(18)' },
532        Name =>
533        {read => 1, write => 1, type => 'varchar(36)' },
534        Phone =>
535        {read => 1, write => 1, type => 'varchar(18)', default => ''},
536    }
537}
538
539sub init_data {
540    return (
541	[ 'Login',	'Name',			'Phone' ],
542	[ 'cubic',	'Ruslan U. Zakirov',	'+7-903-264-XX-XX' ],
543	[ 'obra',	'Jesse Vincent',	undef ],
544	[ 'glasser',	'David Glasser',	undef ],
545	[ 'autrijus',	'Autrijus Tang',	'+X-XXX-XXX-XX-XX' ],
546    );
547}
548
5491;
550
551package TestApp::Users;
552
553# use TestApp::User;
554use base qw/DBIx::SearchBuilder/;
555
556sub _Init {
557    my $self = shift;
558    $self->SUPER::_Init( Handle => shift );
559    $self->Table('Users');
560}
561
562sub NewItem
563{
564	my $self = shift;
565	return TestApp::User->new( $self->_Handle );
566}
567
5681;
569
570