]> granicus.if.org Git - postgresql/blob - src/test/regress/pg_regress.sh
$Header: -> $PostgreSQL Changes ...
[postgresql] / src / test / regress / pg_regress.sh
1 #! /bin/sh
2 # $PostgreSQL: pgsql/src/test/regress/pg_regress.sh,v 1.37 2003/11/29 19:52:14 pgsql Exp $
3
4 me=`basename $0`
5 : ${TMPDIR=/tmp}
6 TMPFILE=$TMPDIR/pg_regress.$$
7
8 help="\
9 PostgreSQL regression test driver
10
11 Usage: $me [options...] [extra tests...]
12
13 Options:
14   --debug                   turn on debug mode in programs that are run
15   --inputdir=DIR            take input files from DIR (default \`.')
16   --max-connections=N       maximum number of concurrent connections
17                             (default is 0 meaning unlimited)
18   --multibyte=ENCODING      use ENCODING as the multibyte encoding, and
19                             also run a test by the same name
20   --outputdir=DIR           place output files in DIR (default \`.')
21   --schedule=FILE           use test ordering schedule from FILE
22                             (may be used multiple times to concatenate)
23   --temp-install[=DIR]      create a temporary installation (in DIR)
24
25 Options for \`temp-install' mode:
26   --top-builddir=DIR        (relative) path to top level build directory
27
28 Options for using an existing installation:
29   --host=HOST               use postmaster running on HOST
30   --port=PORT               use postmaster running at PORT
31   --user=USER               connect as USER
32
33 The exit status is 0 if all tests passed, 1 if some tests failed, and 2
34 if the tests could not be run for some reason.
35
36 Report bugs to <pgsql-bugs@postgresql.org>."
37
38
39 message(){
40     _dashes='==============' # 14
41     _spaces='                                      ' # 38
42     _msg=`echo "$1$_spaces" | cut -c 1-38`
43     echo "$_dashes $_msg $_dashes"
44 }
45
46
47 # ----------
48 # Unset locale settings
49 # ----------
50
51 unset LC_COLLATE LC_CTYPE LC_MONETARY LC_MESSAGES LC_NUMERIC LC_TIME LC_ALL LANG LANGUAGE
52
53
54 # ----------
55 # Check for echo -n vs echo \c
56 # ----------
57
58 if echo '\c' | grep c >/dev/null 2>&1; then
59     ECHO_N='echo -n'
60     ECHO_C=''
61 else
62     ECHO_N='echo'
63     ECHO_C='\c'
64 fi
65
66
67 # ----------
68 # Initialize default settings
69 # ----------
70
71 : ${inputdir=.}
72 : ${outputdir=.}
73
74 libdir='@libdir@'
75 pkglibdir='@pkglibdir@'
76 bindir='@bindir@'
77 datadir='@datadir@'
78 host_platform='@host_tuple@'
79 enable_shared='@enable_shared@'
80 GCC=@GCC@
81
82 if [ "$GCC" = yes ]; then
83     compiler=gcc
84 else
85     compiler=cc
86 fi
87
88 unset mode
89 unset schedule
90 unset debug
91 unset top_builddir
92 unset temp_install
93 unset multibyte
94
95 dbname=regression
96 hostname=localhost
97 maxconnections=0
98
99 : ${GMAKE='@GMAKE@'}
100
101
102 # ----------
103 # Parse command line options
104 # ----------
105
106 while [ "$#" -gt 0 ]
107 do
108     case $1 in
109         --help|-\?)
110                 echo "$help"
111                 exit 0;;
112         --version)
113                 echo "pg_regress (PostgreSQL @VERSION@)"
114                 exit 0;;
115         --debug)
116                 debug=yes
117                 shift;;
118         --inputdir=*)
119                 inputdir=`expr "x$1" : "x--inputdir=\(.*\)"`
120                 shift;;
121         --multibyte=*)
122                 multibyte=`expr "x$1" : "x--multibyte=\(.*\)"`
123                 shift;;
124         --temp-install)
125                 temp_install=./tmp_check
126                 shift;;
127         --temp-install=*)
128                 temp_install=`expr "x$1" : "x--temp-install=\(.*\)"`
129                 shift;;
130         --max-connections=*)
131                 maxconnections=`expr "x$1" : "x--max-connections=\(.*\)"`
132                 shift;;
133         --outputdir=*)
134                 outputdir=`expr "x$1" : "x--outputdir=\(.*\)"`
135                 shift;;
136         --schedule=*)
137                 foo=`expr "x$1" : "x--schedule=\(.*\)"`
138                 schedule="$schedule $foo"
139                 shift;;
140         --top-builddir=*)
141                 top_builddir=`expr "x$1" : "x--top-builddir=\(.*\)"`
142                 shift;;
143         --host=*)
144                 PGHOST=`expr "x$1" : "x--host=\(.*\)"`
145                 export PGHOST
146                 unset PGHOSTADDR
147                 shift;;
148         --port=*)
149                 PGPORT=`expr "x$1" : "x--port=\(.*\)"`
150                 export PGPORT
151                 shift;;
152         --user=*)
153                 PGUSER=`expr "x$1" : "x--user=\(.*\)"`
154                 export PGUSER
155                 shift;;
156         -*)
157                 echo "$me: invalid argument $1" 1>&2
158                 exit 2;;
159         *)
160                 extra_tests="$extra_tests $1"
161                 shift;;
162     esac
163 done
164
165 # ----------
166 # warn of Cygwin likely failure if maxconnections = 0
167 # and we are running parallel tests
168 # ----------
169
170 case $host_platform in
171     *-*-cygwin*)
172         case "$schedule" in
173             *parallel_schedule*)
174                 if [ $maxconnections -eq 0 ] ; then
175                     echo Using unlimited parallel connections is likely to fail or hang on Cygwin.
176                     echo Try \"$me --max-connections=n\" or \"gmake MAX_CONNECTIONS=n check\"
177                     echo with n = 5 or 10 if this happens.
178                     echo
179                 fi
180                 ;;
181         esac
182         ;;
183 esac
184
185
186 # ----------
187 # When on QNX or BeOS, don't use Unix sockets.
188 # ----------
189
190 case $host_platform in
191     *-*-qnx* | *beos*)
192         unix_sockets=no;;
193     *)
194         unix_sockets=yes;;
195 esac
196
197
198 # ----------
199 # Set up diff to ignore horizontal white space differences.
200 # ----------
201
202 case $host_platform in
203     *-*-qnx* | *-*-sco3.2v5*)
204         DIFFFLAGS=-b;;
205     *)
206         DIFFFLAGS=-w;;
207 esac
208
209
210 # ----------
211 # Set backend timezone and datestyle explicitly
212 #
213 # To pass the horology test in its current form, the postmaster must be
214 # started with PGDATESTYLE=ISO, while the frontend must be started with
215 # PGDATESTYLE=Postgres.  We set the postmaster values here and change
216 # to the frontend settings after the postmaster has been started.
217 # ----------
218
219 PGTZ='PST8PDT'; export PGTZ
220 PGDATESTYLE='ISO, MDY'; export PGDATESTYLE
221
222
223 # ----------
224 # Exit trap to remove temp file and shut down postmaster
225 # ----------
226
227 # Note:  There are some stupid shells (even among recent ones) that
228 # ignore the argument to exit (as in `exit 1') if there is an exit
229 # trap.  The trap (and thus the shell script) will then always exit
230 # with the result of the last shell command before the `exit'.  Hence
231 # we have to write `(exit x); exit' below this point.
232
233 trap '
234     savestatus=$?
235     if [ -n "$postmaster_pid" ]; then
236         kill -2 "$postmaster_pid"
237         wait "$postmaster_pid"
238         unset postmaster_pid
239     fi
240     rm -f "$TMPFILE" && exit $savestatus
241 ' 0
242
243 trap '
244     savestatus=$?
245     echo; echo "caught signal"
246     if [ -n "$postmaster_pid" ]; then
247         echo "signalling fast shutdown to postmaster with pid $postmaster_pid"
248         kill -2 "$postmaster_pid"
249         wait "$postmaster_pid"
250         unset postmaster_pid
251     fi
252     (exit $savestatus); exit
253 ' 1 2 13 15
254
255
256
257 # ----------
258 # Scan resultmap file to find which platform-specific expected files to use.
259 # The format of each line of the file is
260 #         testname/hostplatformpattern=substitutefile
261 # where the hostplatformpattern is evaluated per the rules of expr(1),
262 # namely, it is a standard regular expression with an implicit ^ at the start.
263 # What hostplatformpattern will be matched against is the config.guess output
264 # followed by either ':gcc' or ':cc' (independent of the actual name of the
265 # compiler executable).
266 #
267 # The tempfile hackery is needed because some shells will run the loop
268 # inside a subshell, whereupon shell variables set therein aren't seen
269 # outside the loop :-(
270 # ----------
271
272 cat /dev/null >$TMPFILE
273 if [ -f "$inputdir/resultmap" ]
274 then
275     while read LINE
276     do
277         HOSTPAT=`expr "$LINE" : '.*/\(.*\)='`
278         if [ `expr "$host_platform:$compiler" : "$HOSTPAT"` -ne 0 ]
279         then
280             # remove hostnamepattern from line so that there are no shell
281             # wildcards in SUBSTLIST; else later 'for' could expand them!
282             TESTNAME=`expr "$LINE" : '\(.*\)/'`
283             SUBST=`echo "$LINE" | sed 's/^.*=//'`
284             echo "$TESTNAME=$SUBST" >> $TMPFILE
285         fi
286     done <"$inputdir/resultmap"
287 fi
288 SUBSTLIST=`cat $TMPFILE`
289 rm -f $TMPFILE
290
291
292 LOGDIR=$outputdir/log
293
294 if [ x"$temp_install" != x"" ]
295 then
296     if echo x"$temp_install" | grep -v '^x/' >/dev/null 2>&1; then
297         temp_install="`pwd`/$temp_install"
298     fi
299
300     bindir=$temp_install/install/$bindir
301     libdir=$temp_install/install/$libdir
302     pkglibdir=$temp_install/install/$pkglibdir
303     datadir=$temp_install/install/$datadir
304     PGDATA=$temp_install/data
305
306     if [ "$unix_sockets" = no ]; then
307         PGHOST=$hostname
308         export PGHOST
309         unset PGHOSTADDR
310     else
311         unset PGHOST
312         unset PGHOSTADDR
313     fi
314     PGPORT=65432
315     export PGPORT
316
317     # Get rid of environment stuff that might cause psql to misbehave
318     # while contacting our temp installation
319     unset PGDATABASE PGUSER PGSERVICE PGSSLMODE PGREQUIRESSL PGCONNECT_TIMEOUT
320
321     # ----------
322     # Set up shared library paths, needed by psql and pg_encoding
323     # (if you run multibyte).  LD_LIBRARY_PATH covers many platforms,
324     # feel free to account for others as well.
325     # ----------
326
327     if [ -n "$LD_LIBRARY_PATH" ]; then
328         LD_LIBRARY_PATH="$libdir:$LD_LIBRARY_PATH"
329     else
330         LD_LIBRARY_PATH=$libdir
331     fi
332     export LD_LIBRARY_PATH
333
334     # ----------
335     # Windows needs shared libraries in PATH. (Only those linked into
336     # executables, not dlopen'ed ones)
337     # ----------
338     case $host_platform in
339         *-*-cygwin*)
340             PATH=$libdir:$PATH
341             export PATH
342             ;;
343     esac
344
345     if [ -d "$temp_install" ]; then
346         message "removing existing temp installation"
347         rm -rf "$temp_install"
348     fi
349
350     message "creating temporary installation"
351     if [ ! -d "$LOGDIR" ]; then
352         mkdir -p "$LOGDIR" || { (exit 2); exit; }
353     fi
354     $GMAKE -C "$top_builddir" DESTDIR="$temp_install/install" install with_perl=no with_python=no >"$LOGDIR/install.log" 2>&1
355
356     if [ $? -ne 0 ]
357     then
358         echo
359         echo "$me: installation failed"
360         echo "Examine $LOGDIR/install.log for the reason."
361         echo
362         (exit 2); exit
363     fi
364
365     # fix conversion shared objs path
366     conv=$datadir/conversion_create.sql
367     backup=$conv.bak
368     mv $conv $backup
369     sed -e "s@\$libdir@$pkglibdir@g" $backup > $conv
370     rm $backup
371
372     message "initializing database system"
373     [ "$debug" = yes ] && initdb_options='--debug'
374     "$bindir/initdb" -D "$PGDATA" -L "$datadir" --noclean $initdb_options >"$LOGDIR/initdb.log" 2>&1
375
376     if [ $? -ne 0 ]
377     then
378         echo
379         echo "$me: initdb failed"
380         echo "Examine $LOGDIR/initdb.log for the reason."
381         echo
382         (exit 2); exit
383     fi
384
385
386     # ----------
387     # Start postmaster
388     # ----------
389
390     message "starting postmaster"
391     [ "$debug" = yes ] && postmaster_options="$postmaster_options -d 5"
392     [ "$unix_sockets" = no ] && postmaster_options="$postmaster_options -i"
393     "$bindir/postmaster" -D "$PGDATA" -F $postmaster_options >"$LOGDIR/postmaster.log" 2>&1 &
394     postmaster_pid=$!
395
396     # Wait till postmaster is able to accept connections (normally only
397     # a second or so, but Cygwin is reportedly *much* slower).  Don't
398     # wait forever, however.
399     i=0
400     max=60
401     until "$bindir/psql" $psql_options template1 </dev/null 2>/dev/null
402     do
403         i=`expr $i + 1`
404         if [ $i -ge $max ]
405         then
406             break
407         fi
408         if kill -0 $postmaster_pid >/dev/null 2>&1
409         then
410             : still starting up
411         else
412             break
413         fi
414         sleep 1
415     done
416
417     if kill -0 $postmaster_pid >/dev/null 2>&1
418     then
419         echo "running on port $PGPORT with pid $postmaster_pid"
420     else
421         echo
422         echo "$me: postmaster did not start"
423         echo "Examine $LOGDIR/postmaster.log for the reason."
424         echo
425         (exit 2); exit
426     fi
427
428 else # not temp-install
429
430     # If Unix sockets are not available, use the local host by default.
431     if [ "$unix_sockets" = no ]; then
432         PGHOST=$hostname
433         export PGHOST
434         unset PGHOSTADDR
435     fi
436
437     if [ -n "$PGPORT" ]; then
438         port_info="port $PGPORT"
439     else
440         port_info="default port"
441     fi
442
443     if [ -n "$PGHOST" ]; then
444         echo "(using postmaster on $PGHOST, $port_info)"
445     else
446         echo "(using postmaster on Unix socket, $port_info)"
447     fi
448     message "dropping database \"$dbname\""
449     "$bindir/dropdb" $psql_options "$dbname"
450     # errors can be ignored
451 fi
452
453
454 # ----------
455 # Set up SQL shell for the test.
456 # ----------
457
458 PSQL="$bindir/psql -a -q -X $psql_options"
459
460
461 # ----------
462 # Set frontend timezone and datestyle explicitly
463 # ----------
464
465 PGTZ='PST8PDT'; export PGTZ
466 PGDATESTYLE='Postgres, MDY'; export PGDATESTYLE
467
468
469 # ----------
470 # Set up multibyte environment
471 # ----------
472
473 if [ -n "$multibyte" ]; then
474     PGCLIENTENCODING=$multibyte
475     export PGCLIENTENCODING
476     encoding_opt="-E $multibyte"
477 else
478     unset PGCLIENTENCODING
479 fi
480
481
482 # ----------
483 # Create the regression database
484 # We use template0 so that any installation-local cruft in template1
485 # will not mess up the tests.
486 # ----------
487
488 message "creating database \"$dbname\""
489 "$bindir/createdb" $encoding_opt $psql_options --template template0 "$dbname"
490 if [ $? -ne 0 ]; then
491     echo "$me: createdb failed"
492     (exit 2); exit
493 fi
494
495 "$bindir/psql" $psql_options -c "\
496 alter database \"$dbname\" set lc_messages to 'C';
497 alter database \"$dbname\" set lc_monetary to 'C';
498 alter database \"$dbname\" set lc_numeric to 'C';
499 alter database \"$dbname\" set lc_time to 'C';" "$dbname" 2>/dev/null
500 if [ $? -ne 0 ]; then
501     echo "$me: could not set database default locales"
502     (exit 2); exit
503 fi
504
505
506 # ----------
507 # Remove regressuser* and regressgroup* user accounts.
508 # ----------
509
510 message "dropping regression test user accounts"
511 "$bindir/psql" $psql_options -c 'DROP GROUP regressgroup1; DROP GROUP regressgroup2; DROP USER regressuser1, regressuser2, regressuser3, regressuser4;' $dbname 2>/dev/null
512 if [ $? -eq 2 ]; then
513     echo "$me: could not drop user accounts"
514     (exit 2); exit
515 fi
516
517
518 # ----------
519 # Install the PL/pgSQL language in it
520 # ----------
521
522 if [ "$enable_shared" = yes ]; then
523         message "installing PL/pgSQL"
524         "$bindir/createlang" -L "$pkglibdir" $psql_options plpgsql $dbname
525         if [ $? -ne 0 ] && [ $? -ne 2 ]; then
526             echo "$me: createlang failed"
527             (exit 2); exit
528         fi
529 fi
530
531
532 # ----------
533 # Let's go
534 # ----------
535
536 message "running regression test queries"
537
538 if [ ! -d "$outputdir/results" ]; then
539     mkdir -p "$outputdir/results" || { (exit 2); exit; }
540 fi
541 result_summary_file=$outputdir/regression.out
542 diff_file=$outputdir/regression.diffs
543
544 cat /dev/null >"$result_summary_file"
545 cat /dev/null >"$diff_file"
546
547 lno=0
548 (
549     [ "$enable_shared" != yes ] && echo "ignore: plpgsql"
550     cat $schedule </dev/null
551     for x in $extra_tests; do
552         echo "test: $x"
553     done
554 ) | sed 's/[    ]*#.*//g' | \
555 while read line
556 do
557     # Count line numbers
558     lno=`expr $lno + 1`
559     [ -z "$line" ] && continue
560
561     set X $line; shift
562
563     if [ x"$1" = x"ignore:" ]; then
564         shift
565         ignore_list="$ignore_list $@"
566         continue
567     elif [ x"$1" != x"test:" ]; then
568         echo "$me:$schedule:$lno: syntax error"
569         (exit 2); exit
570     fi
571
572     shift
573
574     # ----------
575     # Start tests
576     # ----------
577
578     if [ $# -eq 1 ]; then
579         # Run a single test
580         formatted=`echo $1 | awk '{printf "%-20.20s", $1;}'`
581         $ECHO_N "test $formatted ... $ECHO_C"
582         $PSQL -d "$dbname" <"$inputdir/sql/$1.sql" >"$outputdir/results/$1.out" 2>&1
583     else
584         # Start a parallel group
585         $ECHO_N "parallel group ($# tests): $ECHO_C"
586         if [ $maxconnections -gt 0 ] ; then
587             connnum=0
588             test $# -gt $maxconnections && $ECHO_N "(in groups of $maxconnections) $ECHO_C"
589         fi
590         for name do
591             ( 
592               $PSQL -d "$dbname" <"$inputdir/sql/$name.sql" >"$outputdir/results/$name.out" 2>&1
593               $ECHO_N " $name$ECHO_C"
594             ) &
595             if [ $maxconnections -gt 0 ] ; then
596                 connnum=`expr \( $connnum + 1 \) % $maxconnections`
597                 test $connnum -eq 0 && wait
598             fi
599         done
600         wait
601         echo
602     fi
603
604     # ----------
605     # Run diff
606     # (We do not want to run the diffs immediately after each test,
607     # because they would certainly get corrupted if run in parallel
608     # subshells.)
609     # ----------
610
611     for name do
612         if [ $# -ne 1 ]; then
613             formatted=`echo "$name" | awk '{printf "%-20.20s", $1;}'`
614             $ECHO_N "     $formatted ... $ECHO_C"
615         fi
616
617         # Check list extracted from resultmap to see if we should compare
618         # to a system-specific expected file.
619         # There shouldn't be multiple matches, but take the last if there are.
620
621         EXPECTED="$inputdir/expected/${name}"
622         for LINE in $SUBSTLIST
623         do
624             if [ `expr "$LINE" : "$name="` -ne 0 ]
625             then
626                 SUBST=`echo "$LINE" | sed 's/^.*=//'`
627                 EXPECTED="$inputdir/expected/${SUBST}"
628             fi
629         done
630
631         # If there are multiple equally valid result files, loop to get the right one.
632         # If none match, diff against the closest one.
633
634         bestfile=
635         bestdiff=
636         result=2
637         for thisfile in $EXPECTED.out ${EXPECTED}_[0-9].out; do
638             [ ! -r "$thisfile" ] && continue
639             diff $DIFFFLAGS $thisfile $outputdir/results/${name}.out >/dev/null 2>&1
640             result=$?
641             case $result in
642                 0) break;;
643                 1) thisdiff=`diff $DIFFFLAGS $thisfile $outputdir/results/${name}.out | wc -l`
644                    if [ -z "$bestdiff" ] || [ "$thisdiff" -lt "$bestdiff" ]; then
645                        bestdiff=$thisdiff; bestfile=$thisfile
646                    fi
647                    continue;;
648                 2) break;;
649             esac
650         done
651
652         # Now print the result.
653
654         case $result in
655             0)
656                 echo "ok";;
657             1)
658                 ( diff $DIFFFLAGS -C3 $bestfile $outputdir/results/${name}.out
659                   echo
660                   echo "======================================================================"
661                   echo ) >> "$diff_file"
662                 if echo " $ignore_list " | grep " $name " >/dev/null 2>&1 ; then
663                     echo "failed (ignored)"
664                 else
665                     echo "FAILED"
666                 fi
667                 ;;
668             2)
669                 # desaster struck
670                 echo "trouble" 1>&2
671                 (exit 2); exit;;
672         esac
673     done
674 done | tee "$result_summary_file" 2>&1
675
676 [ $? -ne 0 ] && exit
677
678 # ----------
679 # Server shutdown
680 # ----------
681
682 if [ -n "$postmaster_pid" ]; then
683     message "shutting down postmaster"
684     kill -15 "$postmaster_pid"
685     wait "$postmaster_pid"
686     unset postmaster_pid
687 fi
688
689 rm -f "$TMPFILE"
690
691
692 # ----------
693 # Evaluation
694 # ----------
695
696 count_total=`cat "$result_summary_file" | grep '\.\.\.' | wc -l | sed 's/ //g'`
697 count_ok=`cat "$result_summary_file" | grep '\.\.\. ok' | wc -l | sed 's/ //g'`
698 count_failed=`cat "$result_summary_file" | grep '\.\.\. FAILED' | wc -l | sed 's/ //g'`
699 count_ignored=`cat "$result_summary_file" | grep '\.\.\. failed (ignored)' | wc -l | sed 's/ //g'`
700
701 echo
702 if [ $count_total -eq $count_ok ]; then
703     msg="All $count_total tests passed."
704     result=0
705 elif [ $count_failed -eq 0 ]; then
706     msg="$count_ok of $count_total tests passed, $count_ignored failed test(s) ignored."
707     result=0
708 elif [ $count_ignored -eq 0 ]; then
709     msg="$count_failed of $count_total tests failed."
710     result=1
711 else
712     msg="`expr $count_failed + $count_ignored` of $count_total tests failed, $count_ignored of these failures ignored."
713     result=1
714 fi
715
716 dashes=`echo " $msg " | sed 's/./=/g'`
717 echo "$dashes"
718 echo " $msg "
719 echo "$dashes"
720 echo
721
722 if [ -s "$diff_file" ]; then
723     echo "The differences that caused some tests to fail can be viewed in the"
724     echo "file \`$diff_file'.  A copy of the test summary that you see"
725     echo "above is saved in the file \`$result_summary_file'."
726     echo
727 else
728     rm -f "$diff_file" "$result_summary_file"
729 fi
730
731
732 (exit $result); exit