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