1-- 2-- Tests for PL/pgSQL control structures 3-- 4 5-- integer FOR loop 6 7do $$ 8begin 9 -- basic case 10 for i in 1..3 loop 11 raise notice '1..3: i = %', i; 12 end loop; 13 -- with BY, end matches exactly 14 for i in 1..10 by 3 loop 15 raise notice '1..10 by 3: i = %', i; 16 end loop; 17 -- with BY, end does not match 18 for i in 1..11 by 3 loop 19 raise notice '1..11 by 3: i = %', i; 20 end loop; 21 -- zero iterations 22 for i in 1..0 by 3 loop 23 raise notice '1..0 by 3: i = %', i; 24 end loop; 25 -- REVERSE 26 for i in reverse 10..0 by 3 loop 27 raise notice 'reverse 10..0 by 3: i = %', i; 28 end loop; 29 -- potential overflow 30 for i in 2147483620..2147483647 by 10 loop 31 raise notice '2147483620..2147483647 by 10: i = %', i; 32 end loop; 33 -- potential overflow, reverse direction 34 for i in reverse -2147483620..-2147483647 by 10 loop 35 raise notice 'reverse -2147483620..-2147483647 by 10: i = %', i; 36 end loop; 37end$$; 38 39-- BY can't be zero or negative 40do $$ 41begin 42 for i in 1..3 by 0 loop 43 raise notice '1..3 by 0: i = %', i; 44 end loop; 45end$$; 46 47do $$ 48begin 49 for i in 1..3 by -1 loop 50 raise notice '1..3 by -1: i = %', i; 51 end loop; 52end$$; 53 54do $$ 55begin 56 for i in reverse 1..3 by -1 loop 57 raise notice 'reverse 1..3 by -1: i = %', i; 58 end loop; 59end$$; 60 61 62-- CONTINUE statement 63 64create table conttesttbl(idx serial, v integer); 65insert into conttesttbl(v) values(10); 66insert into conttesttbl(v) values(20); 67insert into conttesttbl(v) values(30); 68insert into conttesttbl(v) values(40); 69 70create function continue_test1() returns void as $$ 71declare _i integer = 0; _r record; 72begin 73 raise notice '---1---'; 74 loop 75 _i := _i + 1; 76 raise notice '%', _i; 77 continue when _i < 10; 78 exit; 79 end loop; 80 81 raise notice '---2---'; 82 <<lbl>> 83 loop 84 _i := _i - 1; 85 loop 86 raise notice '%', _i; 87 continue lbl when _i > 0; 88 exit lbl; 89 end loop; 90 end loop; 91 92 raise notice '---3---'; 93 <<the_loop>> 94 while _i < 10 loop 95 _i := _i + 1; 96 continue the_loop when _i % 2 = 0; 97 raise notice '%', _i; 98 end loop; 99 100 raise notice '---4---'; 101 for _i in 1..10 loop 102 begin 103 -- applies to outer loop, not the nested begin block 104 continue when _i < 5; 105 raise notice '%', _i; 106 end; 107 end loop; 108 109 raise notice '---5---'; 110 for _r in select * from conttesttbl loop 111 continue when _r.v <= 20; 112 raise notice '%', _r.v; 113 end loop; 114 115 raise notice '---6---'; 116 for _r in execute 'select * from conttesttbl' loop 117 continue when _r.v <= 20; 118 raise notice '%', _r.v; 119 end loop; 120 121 raise notice '---7---'; 122 <<looplabel>> 123 for _i in 1..3 loop 124 continue looplabel when _i = 2; 125 raise notice '%', _i; 126 end loop; 127 128 raise notice '---8---'; 129 _i := 1; 130 while _i <= 3 loop 131 raise notice '%', _i; 132 _i := _i + 1; 133 continue when _i = 3; 134 end loop; 135 136 raise notice '---9---'; 137 for _r in select * from conttesttbl order by v limit 1 loop 138 raise notice '%', _r.v; 139 continue; 140 end loop; 141 142 raise notice '---10---'; 143 for _r in execute 'select * from conttesttbl order by v limit 1' loop 144 raise notice '%', _r.v; 145 continue; 146 end loop; 147 148 raise notice '---11---'; 149 <<outerlooplabel>> 150 for _i in 1..2 loop 151 raise notice 'outer %', _i; 152 <<innerlooplabel>> 153 for _j in 1..3 loop 154 continue outerlooplabel when _j = 2; 155 raise notice 'inner %', _j; 156 end loop; 157 end loop; 158end; $$ language plpgsql; 159 160select continue_test1(); 161 162-- should fail: CONTINUE is only legal inside a loop 163create function continue_error1() returns void as $$ 164begin 165 begin 166 continue; 167 end; 168end; 169$$ language plpgsql; 170 171-- should fail: unlabeled EXIT is only legal inside a loop 172create function exit_error1() returns void as $$ 173begin 174 begin 175 exit; 176 end; 177end; 178$$ language plpgsql; 179 180-- should fail: no such label 181create function continue_error2() returns void as $$ 182begin 183 begin 184 loop 185 continue no_such_label; 186 end loop; 187 end; 188end; 189$$ language plpgsql; 190 191-- should fail: no such label 192create function exit_error2() returns void as $$ 193begin 194 begin 195 loop 196 exit no_such_label; 197 end loop; 198 end; 199end; 200$$ language plpgsql; 201 202-- should fail: CONTINUE can't reference the label of a named block 203create function continue_error3() returns void as $$ 204begin 205 <<begin_block1>> 206 begin 207 loop 208 continue begin_block1; 209 end loop; 210 end; 211end; 212$$ language plpgsql; 213 214-- On the other hand, EXIT *can* reference the label of a named block 215create function exit_block1() returns void as $$ 216begin 217 <<begin_block1>> 218 begin 219 loop 220 exit begin_block1; 221 raise exception 'should not get here'; 222 end loop; 223 end; 224end; 225$$ language plpgsql; 226 227select exit_block1(); 228 229-- verbose end block and end loop 230create function end_label1() returns void as $$ 231<<blbl>> 232begin 233 <<flbl1>> 234 for i in 1 .. 10 loop 235 raise notice 'i = %', i; 236 exit flbl1; 237 end loop flbl1; 238 <<flbl2>> 239 for j in 1 .. 10 loop 240 raise notice 'j = %', j; 241 exit flbl2; 242 end loop; 243end blbl; 244$$ language plpgsql; 245 246select end_label1(); 247 248-- should fail: undefined end label 249create function end_label2() returns void as $$ 250begin 251 for _i in 1 .. 10 loop 252 exit; 253 end loop flbl1; 254end; 255$$ language plpgsql; 256 257-- should fail: end label does not match start label 258create function end_label3() returns void as $$ 259<<outer_label>> 260begin 261 <<inner_label>> 262 for _i in 1 .. 10 loop 263 exit; 264 end loop outer_label; 265end; 266$$ language plpgsql; 267 268-- should fail: end label on a block without a start label 269create function end_label4() returns void as $$ 270<<outer_label>> 271begin 272 for _i in 1 .. 10 loop 273 exit; 274 end loop outer_label; 275end; 276$$ language plpgsql; 277 278-- unlabeled exit matches no blocks 279do $$ 280begin 281for i in 1..10 loop 282 <<innerblock>> 283 begin 284 begin -- unlabeled block 285 exit; 286 raise notice 'should not get here'; 287 end; 288 raise notice 'should not get here, either'; 289 end; 290 raise notice 'nor here'; 291end loop; 292raise notice 'should get here'; 293end$$; 294 295-- check exit out of an unlabeled block to a labeled one 296do $$ 297<<outerblock>> 298begin 299 <<innerblock>> 300 begin 301 <<moreinnerblock>> 302 begin 303 begin -- unlabeled block 304 exit innerblock; 305 raise notice 'should not get here'; 306 end; 307 raise notice 'should not get here, either'; 308 end; 309 raise notice 'nor here'; 310 end; 311 raise notice 'should get here'; 312end$$; 313 314-- check exit out of outermost block 315do $$ 316<<outerblock>> 317begin 318 <<innerblock>> 319 begin 320 exit outerblock; 321 raise notice 'should not get here'; 322 end; 323 raise notice 'should not get here, either'; 324end$$; 325 326-- unlabeled exit does match a while loop 327do $$ 328begin 329 <<outermostwhile>> 330 while 1 > 0 loop 331 <<outerwhile>> 332 while 1 > 0 loop 333 <<innerwhile>> 334 while 1 > 0 loop 335 exit; 336 raise notice 'should not get here'; 337 end loop; 338 raise notice 'should get here'; 339 exit outermostwhile; 340 raise notice 'should not get here, either'; 341 end loop; 342 raise notice 'nor here'; 343 end loop; 344 raise notice 'should get here, too'; 345end$$; 346 347-- check exit out of an unlabeled while to a labeled one 348do $$ 349begin 350 <<outerwhile>> 351 while 1 > 0 loop 352 while 1 > 0 loop 353 exit outerwhile; 354 raise notice 'should not get here'; 355 end loop; 356 raise notice 'should not get here, either'; 357 end loop; 358 raise notice 'should get here'; 359end$$; 360 361-- continue to an outer while 362do $$ 363declare i int := 0; 364begin 365 <<outermostwhile>> 366 while i < 2 loop 367 raise notice 'outermostwhile, i = %', i; 368 i := i + 1; 369 <<outerwhile>> 370 while 1 > 0 loop 371 <<innerwhile>> 372 while 1 > 0 loop 373 continue outermostwhile; 374 raise notice 'should not get here'; 375 end loop; 376 raise notice 'should not get here, either'; 377 end loop; 378 raise notice 'nor here'; 379 end loop; 380 raise notice 'out of outermostwhile, i = %', i; 381end$$; 382 383-- return out of a while 384create function return_from_while() returns int language plpgsql as $$ 385declare i int := 0; 386begin 387 while i < 10 loop 388 if i > 2 then 389 return i; 390 end if; 391 i := i + 1; 392 end loop; 393 return null; 394end$$; 395 396select return_from_while(); 397 398-- using list of scalars in fori and fore stmts 399create function for_vect() returns void as $proc$ 400<<lbl>>declare a integer; b varchar; c varchar; r record; 401begin 402 -- fori 403 for i in 1 .. 3 loop 404 raise notice '%', i; 405 end loop; 406 -- fore with record var 407 for r in select gs as aa, 'BB' as bb, 'CC' as cc from generate_series(1,4) gs loop 408 raise notice '% % %', r.aa, r.bb, r.cc; 409 end loop; 410 -- fore with single scalar 411 for a in select gs from generate_series(1,4) gs loop 412 raise notice '%', a; 413 end loop; 414 -- fore with multiple scalars 415 for a,b,c in select gs, 'BB','CC' from generate_series(1,4) gs loop 416 raise notice '% % %', a, b, c; 417 end loop; 418 -- using qualified names in fors, fore is enabled, disabled only for fori 419 for lbl.a, lbl.b, lbl.c in execute $$select gs, 'bb','cc' from generate_series(1,4) gs$$ loop 420 raise notice '% % %', a, b, c; 421 end loop; 422end; 423$proc$ language plpgsql; 424 425select for_vect(); 426 427-- CASE statement 428 429create or replace function case_test(bigint) returns text as $$ 430declare a int = 10; 431 b int = 1; 432begin 433 case $1 434 when 1 then 435 return 'one'; 436 when 2 then 437 return 'two'; 438 when 3,4,3+5 then 439 return 'three, four or eight'; 440 when a then 441 return 'ten'; 442 when a+b, a+b+1 then 443 return 'eleven, twelve'; 444 end case; 445end; 446$$ language plpgsql immutable; 447 448select case_test(1); 449select case_test(2); 450select case_test(3); 451select case_test(4); 452select case_test(5); -- fails 453select case_test(8); 454select case_test(10); 455select case_test(11); 456select case_test(12); 457select case_test(13); -- fails 458 459create or replace function catch() returns void as $$ 460begin 461 raise notice '%', case_test(6); 462exception 463 when case_not_found then 464 raise notice 'caught case_not_found % %', SQLSTATE, SQLERRM; 465end 466$$ language plpgsql; 467 468select catch(); 469 470-- test the searched variant too, as well as ELSE 471create or replace function case_test(bigint) returns text as $$ 472declare a int = 10; 473begin 474 case 475 when $1 = 1 then 476 return 'one'; 477 when $1 = a + 2 then 478 return 'twelve'; 479 else 480 return 'other'; 481 end case; 482end; 483$$ language plpgsql immutable; 484 485select case_test(1); 486select case_test(2); 487select case_test(12); 488select case_test(13); 489