diff --git a/GNUmakefile.in b/GNUmakefile.in index af2f119ef7ac..24486f858089 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -119,7 +119,7 @@ distdir = postgresql-$(VERSION) dummy = =install= garbage = =* "#"* ."#"* *~* *.orig *.rej core postgresql-* -dist: $(distdir).tar.gz +dist: $(distdir).tar.gz $(distdir).tar.bz2 ifeq ($(split-dist), yes) dist: postgresql-base-$(VERSION).tar.gz postgresql-docs-$(VERSION).tar.gz postgresql-opt-$(VERSION).tar.gz postgresql-test-$(VERSION).tar.gz endif @@ -129,6 +129,8 @@ dist: $(distdir).tar: distdir $(TAR) chf $@ $(distdir) +.INTERMEDIATE: $(distdir).tar + opt_files = \ src/tools src/tutorial \ $(addprefix src/pl/, plperl plpython tcl) @@ -150,7 +152,7 @@ postgresql-test-$(VERSION).tar: distdir distdir: -rm -rf $(distdir)* $(dummy) - for x in `cd $(top_srcdir) && find . -name CVS -prune -o -print`; do \ + for x in `cd $(top_srcdir) && find . \( -name CVS -prune \) -o \( -name .git -prune \) -o -print`; do \ file=`expr X$$x : 'X\./\(.*\)'`; \ if test -d "$(top_srcdir)/$$file" ; then \ mkdir "$(distdir)/$$file" && chmod 777 "$(distdir)/$$file"; \ @@ -165,12 +167,12 @@ distdir: #cp $(distdir)/doc/src/sgml/INSTALL $(distdir)/ #cp $(distdir)/doc/src/sgml/regress_README $(distdir)/src/test/regress/README $(MAKE) -C $(distdir) distclean - -rm -f $(distdir)/README.CVS + #rm -f $(distdir)/README.git -distcheck: $(distdir).tar.gz +distcheck: dist -rm -rf $(dummy) mkdir $(dummy) - $(GZIP) -d -c $< | $(TAR) xf - + $(GZIP) -d -c $(distdir).tar.gz | $(TAR) xf - install_prefix=`cd $(dummy) && pwd`; \ cd $(distdir) \ && ./configure --prefix="$$install_prefix" diff --git a/config/c-library.m4 b/config/c-library.m4 index 337629b97f6c..cddeafaec232 100644 --- a/config/c-library.m4 +++ b/config/c-library.m4 @@ -1,5 +1,5 @@ # Macros that test various C library quirks -# $PostgreSQL: pgsql/config/c-library.m4,v 1.33 2008/08/21 13:53:28 petere Exp $ +# config/c-library.m4 # PGAC_VAR_INT_TIMEZONE @@ -298,6 +298,7 @@ int main() AC_MSG_RESULT([$pgac_cv_printf_arg_control]) ])# PGAC_FUNC_PRINTF_ARG_CONTROL + # PGAC_TYPE_LOCALE_T # ------------------ # Check for the locale_t type and find the right header file. Mac OS diff --git a/config/docbook.m4 b/config/docbook.m4 index 0ed08343107e..54498ee91b71 100644 --- a/config/docbook.m4 +++ b/config/docbook.m4 @@ -1,4 +1,4 @@ -# config/docbook.m4 +# $PostgreSQL: pgsql/config/docbook.m4,v 1.8 2007/08/09 02:33:58 tgl Exp $ # PGAC_PROG_JADE # -------------- @@ -55,7 +55,7 @@ AC_CACHE_VAL([pgac_cv_path_stylesheets], [if test -n "$DOCBOOKSTYLE"; then pgac_cv_path_stylesheets=$DOCBOOKSTYLE else - for pgac_prefix in /usr /usr/local /opt /sw; do + for pgac_prefix in /usr /usr/local /opt; do for pgac_infix in share lib; do for pgac_postfix in \ sgml/stylesheets/nwalsh-modular \ @@ -64,8 +64,7 @@ else sgml/docbook-dsssl \ sgml/docbook/dsssl/modular \ sgml/docbook/stylesheet/dsssl/modular \ - sgml/docbook/dsssl-stylesheets \ - sgml/dsssl/docbook-dsssl-nwalsh + sgml/docbook/dsssl-stylesheets do pgac_candidate=$pgac_prefix/$pgac_infix/$pgac_postfix if test -r "$pgac_candidate/html/docbook.dsl" \ @@ -97,32 +96,3 @@ if test -n "$DOCBOOKSTYLE"; then else AC_PATH_PROGS(COLLATEINDEX, collateindex.pl) fi])# PGAC_PATH_COLLATEINDEX - - -# PGAC_PATH_DOCBOOK2MAN -# --------------------- -# Find docbook2man program from the docbook2X package. Upstream calls -# this program docbook2man, but there is also a different docbook2man -# out there from the docbook-utils package. Thus, the program we want -# is called docbook2x-man on Debian and db2x_docbook2man on Fedora. -# -# (Consider rewriting this macro using AC_PATH_PROGS_FEATURE_CHECK -# when switching to Autoconf 2.62+.) -AC_DEFUN([PGAC_PATH_DOCBOOK2MAN], -[AC_CACHE_CHECK([for docbook2man], [ac_cv_path_DOCBOOK2MAN], -[if test -z "$DOCBOOK2MAN"; then - _AS_PATH_WALK([], - [for ac_prog in docbook2x-man db2x_docbook2man docbook2man; do - ac_path="$as_dir/$ac_prog" - AS_EXECUTABLE_P(["$ac_path"]) || continue - if "$ac_path" --version 2>/dev/null | $GREP docbook2x >/dev/null 2>&1; then - ac_cv_path_DOCBOOK2MAN=$ac_path - break - fi - done]) -else - ac_cv_path_DOCBOOK2MAN=$DOCBOOK2MAN -fi]) -DOCBOOK2MAN=$ac_cv_path_DOCBOOK2MAN -AC_SUBST(DOCBOOK2MAN) -])# PGAC_PATH_DOCBOOK2MAN diff --git a/config/mkinstalldirs b/config/mkinstalldirs index 070b418022b7..d7b533bccefa 100755 --- a/config/mkinstalldirs +++ b/config/mkinstalldirs @@ -105,13 +105,15 @@ esac for file do - set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + # pathcomp starts with leading slash if present, else empty + pathcomp=`echo "$file" | sed -ne 's|^\(.\).*$|\1|;s|[^/]||;p'` + # swap spaces and slashes, then split on spaces + set fnord `echo "$file" | tr ' /' '/ '` shift - pathcomp= for d do - pathcomp="$pathcomp$d" + pathcomp="$pathcomp"`echo "$d" | tr '/' ' '` case $pathcomp in -*) pathcomp=./$pathcomp ;; esac diff --git a/config/programs.m4 b/config/programs.m4 index 8a85bde21249..504062bbeddd 100644 --- a/config/programs.m4 +++ b/config/programs.m4 @@ -1,4 +1,4 @@ -# config/programs.m4 +# $PostgreSQL: pgsql/config/programs.m4,v 1.24 2008/08/29 13:02:32 petere Exp $ # PGAC_PATH_BISON @@ -10,7 +10,7 @@ AC_DEFUN([PGAC_PATH_BISON], [# Let the user override the search if test -z "$BISON"; then - AC_PATH_PROGS(BISON, bison) + AC_CHECK_PROGS(BISON, bison) fi if test "$BISON"; then @@ -19,15 +19,15 @@ if test "$BISON"; then if echo "$pgac_bison_version" | $AWK '{ if ([$]4 < 1.875) exit 0; else exit 1;}' then AC_MSG_WARN([ -*** The installed version of Bison, $BISON, is too old to use with PostgreSQL. -*** Bison version 1.875 or later is required, but this is $pgac_bison_version.]) +*** The installed version of Bison is too old to use with PostgreSQL. +*** Bison version 1.875 or later is required.]) BISON="" fi fi if test -z "$BISON"; then AC_MSG_WARN([ -*** Without Bison you will not be able to build PostgreSQL from Git nor +*** Without Bison you will not be able to build PostgreSQL from CVS nor *** change any of the parser definition files. You can obtain Bison from *** a GNU mirror site. (If you are using the official distribution of *** PostgreSQL then you do not need to worry about this, because the Bison @@ -42,11 +42,8 @@ AC_SUBST(BISONFLAGS) # PGAC_PATH_FLEX # -------------- # Look for Flex, set the output variable FLEX to its path if found. -# Reject versions before 2.5.31, as we need a reasonably non-buggy reentrant -# scanner. (Note: the well-publicized security problem in 2.5.31 does not -# affect Postgres, and there are still distros shipping patched 2.5.31, -# so allow it.) Also find Flex if its installed under `lex', but do not -# accept other Lex programs. +# Avoid the buggy version 2.5.3. Also find Flex if its installed +# under `lex', but do not accept other Lex programs. AC_DEFUN([PGAC_PATH_FLEX], [AC_CACHE_CHECK([for flex], pgac_cv_path_flex, @@ -78,6 +75,9 @@ else *** The installed version of Flex, $pgac_candidate, is too old to use with Greenplum DB. *** Flex version 2.5.4 or later is required, but this is $pgac_flex_version.]) fi + + pgac_cv_path_flex=$pgac_candidate + break 2 fi fi done @@ -88,8 +88,14 @@ fi ])[]dnl AC_CACHE_CHECK if test x"$pgac_cv_path_flex" = x"no"; then + if test -n "$pgac_broken_flex"; then + AC_MSG_WARN([ +*** The Flex version 2.5.3 you have at $pgac_broken_flex contains a bug. You +*** should get version 2.5.4 or later.]) + fi + AC_MSG_WARN([ -*** Without Flex you will not be able to build PostgreSQL from Git nor +*** Without Flex you will not be able to build PostgreSQL from CVS or *** change any of the scanner definition files. You can obtain Flex from *** a GNU mirror site. (If you are using the official distribution of *** PostgreSQL then you do not need to worry about this because the Flex @@ -98,7 +104,7 @@ if test x"$pgac_cv_path_flex" = x"no"; then FLEX= else FLEX=$pgac_cv_path_flex - pgac_flex_version=`$FLEX --version 2>/dev/null` + pgac_flex_version=`$FLEX -V 2>/dev/null` AC_MSG_NOTICE([using $pgac_flex_version]) fi @@ -117,7 +123,7 @@ AC_SUBST(FLEXFLAGS) AC_DEFUN([PGAC_CHECK_READLINE], [AC_REQUIRE([AC_CANONICAL_HOST]) -AC_CACHE_CHECK([for library containing readline], [pgac_cv_check_readline], +AC_CACHE_VAL([pgac_cv_check_readline], [pgac_cv_check_readline=no pgac_save_LIBS=$LIBS if test x"$with_libedit_preferred" != x"yes" @@ -125,6 +131,7 @@ then READLINE_ORDER="-lreadline -ledit" else READLINE_ORDER="-ledit -lreadline" fi for pgac_rllib in $READLINE_ORDER ; do + AC_MSG_CHECKING([for ${pgac_rllib}]) for pgac_lib in "" " -ltermcap" " -lncurses" " -lcurses" ; do LIBS="${pgac_rllib}${pgac_lib} $pgac_save_LIBS" AC_TRY_LINK_FUNC([readline], [[ @@ -143,11 +150,14 @@ for pgac_rllib in $READLINE_ORDER ; do ]]) done if test "$pgac_cv_check_readline" != no ; then + AC_MSG_RESULT([yes ($pgac_cv_check_readline)]) break + else + AC_MSG_RESULT(no) fi done LIBS=$pgac_save_LIBS -])[]dnl AC_CACHE_CHECK +])[]dnl AC_CACHE_VAL if test "$pgac_cv_check_readline" != no ; then LIBS="$pgac_cv_check_readline $LIBS" @@ -163,8 +173,8 @@ fi # Readline versions < 2.1 don't have rl_completion_append_character AC_DEFUN([PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER], -[AC_CACHE_CHECK([for rl_completion_append_character], pgac_cv_var_rl_completion_append_character, -[AC_TRY_LINK([#include +[AC_MSG_CHECKING([for rl_completion_append_character]) +AC_TRY_LINK([#include #ifdef HAVE_READLINE_READLINE_H # include #elif defined(HAVE_READLINE_H) @@ -172,12 +182,10 @@ AC_DEFUN([PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER], #endif ], [rl_completion_append_character = 'x';], -[pgac_cv_var_rl_completion_append_character=yes], -[pgac_cv_var_rl_completion_append_character=no])]) -if test x"$pgac_cv_var_rl_completion_append_character" = x"yes"; then +[AC_MSG_RESULT(yes) AC_DEFINE(HAVE_RL_COMPLETION_APPEND_CHARACTER, 1, - [Define to 1 if you have the global variable 'rl_completion_append_character'.]) -fi])# PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER + [Define to 1 if you have the global variable 'rl_completion_append_character'.])], +[AC_MSG_RESULT(no)])])# PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER diff --git a/config/python.m4 b/config/python.m4 index cd3acec64fbb..3eeb4d3f76c8 100644 --- a/config/python.m4 +++ b/config/python.m4 @@ -85,13 +85,13 @@ AC_SUBST(python_libdir)[]dnl AC_SUBST(python_libspec)[]dnl AC_SUBST(python_additional_libs)[]dnl -# threaded python is not supported on bsd's +# threaded python is not supported on OpenBSD AC_MSG_CHECKING(whether Python is compiled with thread support) pythreads=`${PYTHON} -c "import sys; print(int('thread' in sys.builtin_module_names))"` if test "$pythreads" = "1"; then AC_MSG_RESULT(yes) case $host_os in - openbsd*|freebsd*) + openbsd*) AC_MSG_ERROR([threaded Python not supported on this platform]) ;; esac diff --git a/configure b/configure index eb85eaea0ea9..9f4cf286e073 100755 --- a/configure +++ b/configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for Greenplum Database 8.3devel. +# Generated by GNU Autoconf 2.69 for PostgreSQL 8.3.23. # -# Report bugs to . +# Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -269,10 +269,10 @@ fi $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and -$0: support@greenplum.com about your system, including any -$0: error possibly output before this message. Then install -$0: a modern shell, or manually run the script under such a -$0: shell if you do have one." +$0: pgsql-bugs@postgresql.org about your system, including +$0: any error possibly output before this message. Then +$0: install a modern shell, or manually run the script +$0: under such a shell if you do have one." fi exit 1 fi @@ -580,11 +580,11 @@ MFLAGS= MAKEFLAGS= # Identity of this package. -PACKAGE_NAME='Greenplum Database' -PACKAGE_TARNAME='greenplum-database' -PACKAGE_VERSION='8.3devel' -PACKAGE_STRING='Greenplum Database 8.3devel' -PACKAGE_BUGREPORT='support@greenplum.com' +PACKAGE_NAME='PostgreSQL' +PACKAGE_TARNAME='postgresql' +PACKAGE_VERSION='8.3.23' +PACKAGE_STRING='PostgreSQL 8.3.23' +PACKAGE_BUGREPORT='pgsql-bugs@postgresql.org' PACKAGE_URL='' ac_unique_file="src/backend/access/common/heaptuple.c" @@ -654,6 +654,7 @@ PTHREAD_CC acx_pthread_config LIBOBJS HAVE_IPV6 +OSSP_UUID_LIBS CURL_LIBS CURL_CFLAGS CURL_CONFIG @@ -707,8 +708,10 @@ with_libcurl with_rt with_zlib with_system_tzdata +with_libxslt with_libxml XML2_CONFIG +with_ossp_uuid with_openssl with_bonjour with_ldap @@ -737,7 +740,6 @@ GCC CPP PG_CRC32C_OBJS CFLAGS_SSE42 -SUN_STUDIO_CC OBJEXT EXEEXT ac_ct_CC @@ -855,7 +857,9 @@ with_bonjour with_openssl with_readline with_libedit_preferred +with_ossp_uuid with_libxml +with_libxslt with_system_tzdata with_zlib with_rt @@ -1416,7 +1420,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures Greenplum Database 8.3devel to adapt to many kinds of systems. +\`configure' configures PostgreSQL 8.3.23 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1464,8 +1468,7 @@ Fine tuning of the installation directories: --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root - [DATAROOTDIR/doc/greenplum-database] + --docdir=DIR documentation root [DATAROOTDIR/doc/postgresql] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] @@ -1482,7 +1485,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Greenplum Database 8.3devel:";; + short | recursive ) echo "Configuration of PostgreSQL 8.3.23:";; esac cat <<\_ACEOF @@ -1540,7 +1543,9 @@ Optional Packages: --with-openssl build with OpenSSL support --without-readline do not use GNU Readline nor BSD Libedit for editing --without-libedit-preferred Don't prefer BSD Libedit over GNU Readline + --with-ossp-uuid use OSSP UUID library when building contrib/uuid-ossp --with-libxml build with XML support + --with-libxslt use XSLT support when building contrib/xml2 --with-system-tzdata=DIR use system time zone data in DIR --without-zlib do not use Zlib --without-rt do not use Realtime Library @@ -1565,7 +1570,7 @@ Some influential environment variables: Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. -Report bugs to . +Report bugs to . _ACEOF ac_status=$? fi @@ -1628,7 +1633,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Greenplum Database configure 8.3devel +PostgreSQL configure 8.3.23 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1881,9 +1886,9 @@ $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## ------------------------------------ ## -## Report this to support@greenplum.com ## -## ------------------------------------ ##" +( $as_echo "## ---------------------------------------- ## +## Report this to pgsql-bugs@postgresql.org ## +## ---------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac @@ -2339,7 +2344,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Greenplum Database $as_me 8.3devel, which was +It was created by PostgreSQL $as_me 8.3.23, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2690,7 +2695,6 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu - ac_aux_dir= for ac_dir in config "$srcdir"/config; do if test -f "$ac_dir/install-sh"; then @@ -4220,30 +4224,6 @@ else fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# Check if it's Sun Studio compiler. We assume that -# __SUNPRO_C will be defined for Sun Studio compilers -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __SUNPRO_C -choke me -#endif - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - SUN_STUDIO_CC=yes -else - SUN_STUDIO_CC=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - - unset CFLAGS # @@ -4520,12 +4500,13 @@ fi # Some versions of GCC support some additional useful warning flags. # Check whether they are supported, and add them to CFLAGS if so. -# ICC pretends to be GCC but it's lying; it doesn't support these flags, -# but has its own. Also check other compiler-specific flags here. +# ICC pretends to be GCC but it's lying; it doesn't support these options. if test "$GCC" = yes -a "$ICC" = no; then - CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -Wpointer-arith" + CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -Wpointer-arith -Winline" # These work in some but not all gcc versions + # GPDB code is full of declarations after statement. + #PGAC_PROG_CC_CFLAGS_OPT([-Wdeclaration-after-statement]) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC supports -Wendif-labels" >&5 $as_echo_n "checking if $CC supports -Wendif-labels... " >&6; } pgac_save_CFLAGS=$CFLAGS @@ -4554,7 +4535,6 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag - #PGAC_PROG_CC_CFLAGS_OPT([-Wmissing-format-attribute]) # This was included in -Wall/-Wformat in older GCC versions { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC supports -Wformat-security" >&5 $as_echo_n "checking if $CC supports -Wformat-security... " >&6; } @@ -4825,8 +4805,8 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag -elif test "$PORTNAME" = "aix"; then - # AIX's xlc has to have strict aliasing turned off too +elif test x"${CC}" = x"xlc"; then + # AIX xlc has to have strict aliasing turned off too { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC supports -qnoansialias" >&5 $as_echo_n "checking if $CC supports -qnoansialias... " >&6; } pgac_save_CFLAGS=$CFLAGS @@ -6193,6 +6173,37 @@ fi +# +# OSSP UUID library +# + +pgac_args="$pgac_args with_ossp_uuid" + + +# Check whether --with-ossp-uuid was given. +if test "${with_ossp_uuid+set}" = set; then : + withval=$with_ossp_uuid; + case $withval in + yes) + : + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-ossp-uuid option" "$LINENO" 5 + ;; + esac + +else + with_ossp_uuid=no + +fi + + + + + # # XML # @@ -6283,6 +6294,39 @@ fi +# +# XSLT +# + +pgac_args="$pgac_args with_libxslt" + + +# Check whether --with-libxslt was given. +if test "${with_libxslt+set}" = set; then : + withval=$with_libxslt; + case $withval in + yes) + +$as_echo "#define USE_LIBXSLT 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libxslt option" "$LINENO" 5 + ;; + esac + +else + with_libxslt=no + +fi + + + + + # # tzdata @@ -6607,6 +6651,8 @@ $as_echo "$as_me: using CPPFLAGS=$CPPFLAGS" >&6;} $as_echo "$as_me: using LDFLAGS=$LDFLAGS" >&6;} + + # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes @@ -7463,22 +7509,20 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_BISON+:} false; then : +if ${ac_cv_prog_BISON+:} false; then : $as_echo_n "(cached) " >&6 else - case $BISON in - [\\/]* | ?:[\\/]*) - ac_cv_path_BISON="$BISON" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR + if test -n "$BISON"; then + ac_cv_prog_BISON="$BISON" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_BISON="$as_dir/$ac_word$ac_exec_ext" + ac_cv_prog_BISON="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi @@ -7486,10 +7530,9 @@ done done IFS=$as_save_IFS - ;; -esac fi -BISON=$ac_cv_path_BISON +fi +BISON=$ac_cv_prog_BISON if test -n "$BISON"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BISON" >&5 $as_echo "$BISON" >&6; } @@ -7511,24 +7554,24 @@ $as_echo "$as_me: using $pgac_bison_version" >&6;} if echo "$pgac_bison_version" | $AWK '{ if ($4 < 1.875) exit 0; else exit 1;}' then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: -*** The installed version of Bison, $BISON, is too old to use with PostgreSQL. -*** Bison version 1.875 or later is required, but this is $pgac_bison_version." >&5 +*** The installed version of Bison is too old to use with PostgreSQL. +*** Bison version 1.875 or later is required." >&5 $as_echo "$as_me: WARNING: -*** The installed version of Bison, $BISON, is too old to use with PostgreSQL. -*** Bison version 1.875 or later is required, but this is $pgac_bison_version." >&2;} +*** The installed version of Bison is too old to use with PostgreSQL. +*** Bison version 1.875 or later is required." >&2;} BISON="" fi fi if test -z "$BISON"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: -*** Without Bison you will not be able to build PostgreSQL from Git nor +*** Without Bison you will not be able to build PostgreSQL from CVS nor *** change any of the parser definition files. You can obtain Bison from *** a GNU mirror site. (If you are using the official distribution of *** PostgreSQL then you do not need to worry about this, because the Bison *** output is pre-generated.)" >&5 $as_echo "$as_me: WARNING: -*** Without Bison you will not be able to build PostgreSQL from Git nor +*** Without Bison you will not be able to build PostgreSQL from CVS nor *** change any of the parser definition files. You can obtain Bison from *** a GNU mirror site. (If you are using the official distribution of *** PostgreSQL then you do not need to worry about this, because the Bison @@ -7573,6 +7616,9 @@ $as_echo "$as_me: WARNING: *** The installed version of Flex, $pgac_candidate, is too old to use with Greenplum DB. *** Flex version 2.5.4 or later is required, but this is $pgac_flex_version." >&2;} fi + + pgac_cv_path_flex=$pgac_candidate + break 2 fi fi done @@ -7585,14 +7631,23 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_path_flex" >&5 $as_echo "$pgac_cv_path_flex" >&6; } if test x"$pgac_cv_path_flex" = x"no"; then + if test -n "$pgac_broken_flex"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** The Flex version 2.5.3 you have at $pgac_broken_flex contains a bug. You +*** should get version 2.5.4 or later." >&5 +$as_echo "$as_me: WARNING: +*** The Flex version 2.5.3 you have at $pgac_broken_flex contains a bug. You +*** should get version 2.5.4 or later." >&2;} + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: -*** Without Flex you will not be able to build PostgreSQL from Git nor +*** Without Flex you will not be able to build PostgreSQL from CVS or *** change any of the scanner definition files. You can obtain Flex from *** a GNU mirror site. (If you are using the official distribution of *** PostgreSQL then you do not need to worry about this because the Flex *** output is pre-generated.)" >&5 $as_echo "$as_me: WARNING: -*** Without Flex you will not be able to build PostgreSQL from Git nor +*** Without Flex you will not be able to build PostgreSQL from CVS or *** change any of the scanner definition files. You can obtain Flex from *** a GNU mirror site. (If you are using the official distribution of *** PostgreSQL then you do not need to worry about this because the Flex @@ -7601,7 +7656,7 @@ $as_echo "$as_me: WARNING: FLEX= else FLEX=$pgac_cv_path_flex - pgac_flex_version=`$FLEX --version 2>/dev/null` + pgac_flex_version=`$FLEX -V 2>/dev/null` { $as_echo "$as_me:${as_lineno-$LINENO}: using $pgac_flex_version" >&5 $as_echo "$as_me: using $pgac_flex_version" >&6;} fi @@ -7822,7 +7877,7 @@ python_additional_libs=`${PYTHON} -c "import distutils.sysconfig,string; print(' $as_echo "${python_libspec} ${python_additional_libs}" >&6; } -# threaded python is not supported on bsd's +# threaded python is not supported on OpenBSD { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Python is compiled with thread support" >&5 $as_echo_n "checking whether Python is compiled with thread support... " >&6; } pythreads=`${PYTHON} -c "import sys; print(int('thread' in sys.builtin_module_names))"` @@ -7830,7 +7885,7 @@ if test "$pythreads" = "1"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } case $host_os in - openbsd*|freebsd*) + openbsd*) as_fn_error $? "threaded Python not supported on this platform" "$LINENO" 5 ;; esac @@ -8536,8 +8591,6 @@ fi if test "$with_readline" = yes; then -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing readline" >&5 -$as_echo_n "checking for library containing readline... " >&6; } if ${pgac_cv_check_readline+:} false; then : $as_echo_n "(cached) " >&6 else @@ -8548,6 +8601,8 @@ then READLINE_ORDER="-lreadline -ledit" else READLINE_ORDER="-ledit -lreadline" fi for pgac_rllib in $READLINE_ORDER ; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pgac_rllib}" >&5 +$as_echo_n "checking for ${pgac_rllib}... " >&6; } for pgac_lib in "" " -ltermcap" " -lncurses" " -lcurses" ; do LIBS="${pgac_rllib}${pgac_lib} $pgac_save_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -8588,14 +8643,18 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done if test "$pgac_cv_check_readline" != no ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($pgac_cv_check_readline)" >&5 +$as_echo "yes ($pgac_cv_check_readline)" >&6; } break + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi done LIBS=$pgac_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_check_readline" >&5 -$as_echo "$pgac_cv_check_readline" >&6; } + if test "$pgac_cv_check_readline" != no ; then LIBS="$pgac_cv_check_readline $LIBS" @@ -9756,6 +9815,144 @@ fi fi +if test "$with_libxslt" = yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for xsltCleanupGlobals in -lxslt" >&5 +$as_echo_n "checking for xsltCleanupGlobals in -lxslt... " >&6; } +if ${ac_cv_lib_xslt_xsltCleanupGlobals+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lxslt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char xsltCleanupGlobals (); +int +main () +{ +return xsltCleanupGlobals (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_xslt_xsltCleanupGlobals=yes +else + ac_cv_lib_xslt_xsltCleanupGlobals=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xslt_xsltCleanupGlobals" >&5 +$as_echo "$ac_cv_lib_xslt_xsltCleanupGlobals" >&6; } +if test "x$ac_cv_lib_xslt_xsltCleanupGlobals" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBXSLT 1 +_ACEOF + + LIBS="-lxslt $LIBS" + +else + as_fn_error $? "library 'xslt' is required for XSLT support" "$LINENO" 5 +fi + +fi + +# for contrib/uuid-ossp +if test "$with_ossp_uuid" = yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid_export in -lossp-uuid" >&5 +$as_echo_n "checking for uuid_export in -lossp-uuid... " >&6; } +if ${ac_cv_lib_ossp_uuid_uuid_export+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lossp-uuid $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_export (); +int +main () +{ +return uuid_export (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ossp_uuid_uuid_export=yes +else + ac_cv_lib_ossp_uuid_uuid_export=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ossp_uuid_uuid_export" >&5 +$as_echo "$ac_cv_lib_ossp_uuid_uuid_export" >&6; } +if test "x$ac_cv_lib_ossp_uuid_uuid_export" = xyes; then : + OSSP_UUID_LIBS="-lossp-uuid" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid_export in -luuid" >&5 +$as_echo_n "checking for uuid_export in -luuid... " >&6; } +if ${ac_cv_lib_uuid_uuid_export+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-luuid $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_export (); +int +main () +{ +return uuid_export (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_uuid_uuid_export=yes +else + ac_cv_lib_uuid_uuid_export=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_export" >&5 +$as_echo "$ac_cv_lib_uuid_uuid_export" >&6; } +if test "x$ac_cv_lib_uuid_uuid_export" = xyes; then : + OSSP_UUID_LIBS="-luuid" +else + as_fn_error $? "library 'ossp-uuid' or 'uuid' is required for OSSP-UUID" "$LINENO" 5 +fi + +fi + +fi + + # Check for EMC Connect if test "$enable_connectemc" = yes ; then _LIBS="$LIBS" @@ -10441,6 +10638,17 @@ else fi +fi + +if test "$with_libxslt" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libxslt/xslt.h" "ac_cv_header_libxslt_xslt_h" "$ac_includes_default" +if test "x$ac_cv_header_libxslt_xslt_h" = xyes; then : + +else + as_fn_error $? "header file is required for XSLT support" "$LINENO" 5 +fi + + fi if test "$with_ldap" = yes ; then @@ -10622,7 +10830,6 @@ done fi - ## ## Types, structures, compiler characteristics ## @@ -11884,6 +12091,13 @@ $as_echo "#define GETTIMEOFDAY_1ARG /**/" >>confdefs.h fi +# Some versions of libedit contain strlcpy(), setproctitle(), and other +# symbols that that library has no business exposing to the world. Pending +# acquisition of a clue by those developers, ignore libedit (including its +# possible alias of libreadline) while checking for everything else. +LIBS_including_readline="$LIBS" +LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` + for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` @@ -12413,8 +12627,6 @@ fi ;; esac -LIBS="$pgac_save_LIBS" - # System's version of getaddrinfo(), if any, may be used only if we found # a definition for struct addrinfo; see notes in src/include/getaddrinfo.h. # (Note: the AC_REPLACE_FUNCS probe fails on Windows, where the available @@ -12445,7 +12657,22 @@ esac fi # Similarly, use system's getopt_long() only if system provides struct option. -if test x"$ac_cv_type_struct_option" = xyes ; then +# Solaris' getopt() doesn't do what we want for long options, so always use +# our versions on that platform. +if test "$PORTNAME" = "solaris"; then + case " $LIBOBJS " in + *" getopt.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getopt.$ac_objext" + ;; +esac + + case " $LIBOBJS " in + *" getopt_long.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getopt_long.$ac_objext" + ;; +esac + +elif test x"$ac_cv_type_struct_option" = xyes ; then ac_fn_c_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long" if test "x$ac_cv_func_getopt_long" = xyes; then : $as_echo "#define HAVE_GETOPT_LONG 1" >>confdefs.h @@ -12480,6 +12707,23 @@ esac fi +# mingw has adopted a GNU-centric interpretation of optind/optreset, +# so always use our version on Windows. +if test "$PORTNAME" = "win32"; then + case " $LIBOBJS " in + *" getopt.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getopt.$ac_objext" + ;; +esac + + case " $LIBOBJS " in + *" getopt_long.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getopt_long.$ac_objext" + ;; +esac + +fi + # Win32 support if test "$PORTNAME" = "win32"; then ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" @@ -12531,70 +12775,6 @@ $as_echo "#define HAVE_SYMLINK 1" >>confdefs.h fi -if test "$with_readline" = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for rl_completion_append_character" >&5 -$as_echo_n "checking for rl_completion_append_character... " >&6; } -if ${pgac_cv_var_rl_completion_append_character+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#ifdef HAVE_READLINE_READLINE_H -# include -#elif defined(HAVE_READLINE_H) -# include -#endif - -int -main () -{ -rl_completion_append_character = 'x'; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - pgac_cv_var_rl_completion_append_character=yes -else - pgac_cv_var_rl_completion_append_character=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_var_rl_completion_append_character" >&5 -$as_echo "$pgac_cv_var_rl_completion_append_character" >&6; } -if test x"$pgac_cv_var_rl_completion_append_character" = x"yes"; then - -$as_echo "#define HAVE_RL_COMPLETION_APPEND_CHARACTER 1" >>confdefs.h - -fi - for ac_func in rl_completion_matches rl_filename_completion_function -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - - for ac_func in replace_history_entry -do : - ac_fn_c_check_func "$LINENO" "replace_history_entry" "ac_cv_func_replace_history_entry" -if test "x$ac_cv_func_replace_history_entry" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_REPLACE_HISTORY_ENTRY 1 -_ACEOF - -fi -done - -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sigsetjmp" >&5 $as_echo_n "checking for sigsetjmp... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -12863,6 +13043,101 @@ fi esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin locking functions" >&5 +$as_echo_n "checking for builtin locking functions... " >&6; } +if ${pgac_cv_gcc_int_atomics+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +int lock = 0; + __sync_lock_test_and_set(&lock, 1); + __sync_lock_release(&lock); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + pgac_cv_gcc_int_atomics="yes" +else + pgac_cv_gcc_int_atomics="no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_gcc_int_atomics" >&5 +$as_echo "$pgac_cv_gcc_int_atomics" >&6; } +if test x"$pgac_cv_gcc_int_atomics" = x"yes"; then + +$as_echo "#define HAVE_GCC_INT_ATOMICS 1" >>confdefs.h + +fi + +# Lastly, restore full LIBS list and check for readline/libedit symbols +LIBS="$LIBS_including_readline" + +if test "$with_readline" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for rl_completion_append_character" >&5 +$as_echo_n "checking for rl_completion_append_character... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#ifdef HAVE_READLINE_READLINE_H +# include +#elif defined(HAVE_READLINE_H) +# include +#endif + +int +main () +{ +rl_completion_append_character = 'x'; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_RL_COMPLETION_APPEND_CHARACTER 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + for ac_func in rl_completion_matches rl_filename_completion_function +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + for ac_func in replace_history_entry +do : + ac_fn_c_check_func "$LINENO" "replace_history_entry" "ac_cv_func_replace_history_entry" +if test "x$ac_cv_func_replace_history_entry" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_REPLACE_HISTORY_ENTRY 1 +_ACEOF + +fi +done + +fi + + # # Pthreads # @@ -14900,7 +15175,7 @@ if test "$PORTNAME" != "win32"; then $as_echo "#define USE_SYSV_SHARED_MEMORY 1" >>confdefs.h -SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c" + SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c" else $as_echo "#define USE_WIN32_SHARED_MEMORY 1" >>confdefs.h @@ -15361,7 +15636,7 @@ else if test -n "$DOCBOOKSTYLE"; then pgac_cv_path_stylesheets=$DOCBOOKSTYLE else - for pgac_prefix in /usr /usr/local /opt /sw; do + for pgac_prefix in /usr /usr/local /opt; do for pgac_infix in share lib; do for pgac_postfix in \ sgml/stylesheets/nwalsh-modular \ @@ -15370,8 +15645,7 @@ else sgml/docbook-dsssl \ sgml/docbook/dsssl/modular \ sgml/docbook/stylesheet/dsssl/modular \ - sgml/docbook/dsssl-stylesheets \ - sgml/dsssl/docbook-dsssl-nwalsh + sgml/docbook/dsssl-stylesheets do pgac_candidate=$pgac_prefix/$pgac_infix/$pgac_postfix if test -r "$pgac_candidate/html/docbook.dsl" \ @@ -16245,7 +16519,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by Greenplum Database $as_me 8.3devel, which was +This file was extended by PostgreSQL $as_me 8.3.23, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -16309,13 +16583,13 @@ $config_links Configuration commands: $config_commands -Report bugs to ." +Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -Greenplum Database config.status 8.3devel +PostgreSQL config.status 8.3.23 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.in b/configure.in index 2abc9d22e1a9..a2c7d9dffe2a 100644 --- a/configure.in +++ b/configure.in @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -dnl $PostgreSQL: pgsql/configure.in,v 1.501 2007/02/07 00:28:54 petere Exp $ +dnl $PostgreSQL: pgsql/configure.in,v 1.546.2.16 2010/05/14 03:27:07 scrappy Exp $ dnl dnl Developers, please strive to achieve this order: dnl @@ -17,9 +17,8 @@ dnl Read the Autoconf manual for details. dnl m4_pattern_forbid(^PGAC_)dnl to catch undefined macros -AC_INIT([Greenplum Database], [8.3devel], [support@greenplum.com]) +AC_INIT([PostgreSQL], [8.3.23], [pgsql-bugs@postgresql.org]) -AC_PREREQ(2.63) dnl m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.63], [], [m4_fatal([Autoconf version 2.63 is required. dnl Untested combinations of 'autoconf' and PostgreSQL versions are not dnl recommended. You can remove the check from 'configure.in' but it is then @@ -298,14 +297,6 @@ AC_TRY_COMPILE([], [@%:@ifndef __INTEL_COMPILER choke me @%:@endif], [ICC=[yes]], [ICC=[no]]) -# Check if it's Sun Studio compiler. We assume that -# __SUNPRO_C will be defined for Sun Studio compilers -AC_TRY_COMPILE([], [@%:@ifndef __SUNPRO_C -choke me -@%:@endif], [SUN_STUDIO_CC=yes], [SUN_STUDIO_CC=no]) - -AC_SUBST(SUN_STUDIO_CC) - unset CFLAGS # @@ -438,14 +429,14 @@ AC_SUBST(PG_CRC32C_OBJS) # Some versions of GCC support some additional useful warning flags. # Check whether they are supported, and add them to CFLAGS if so. -# ICC pretends to be GCC but it's lying; it doesn't support these flags, -# but has its own. Also check other compiler-specific flags here. +# ICC pretends to be GCC but it's lying; it doesn't support these options. if test "$GCC" = yes -a "$ICC" = no; then - CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -Wpointer-arith" + CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -Wpointer-arith -Winline" # These work in some but not all gcc versions + # GPDB code is full of declarations after statement. + #PGAC_PROG_CC_CFLAGS_OPT([-Wdeclaration-after-statement]) PGAC_PROG_CC_CFLAGS_OPT([-Wendif-labels]) - #PGAC_PROG_CC_CFLAGS_OPT([-Wmissing-format-attribute]) # This was included in -Wall/-Wformat in older GCC versions PGAC_PROG_CC_CFLAGS_OPT([-Wformat-security]) # Disable strict-aliasing rules; needed for gcc 3.3+ @@ -473,8 +464,8 @@ elif test "$ICC" = yes; then PGAC_PROG_CC_CFLAGS_OPT([-mp1]) # Make sure strict aliasing is off (though this is said to be the default) PGAC_PROG_CC_CFLAGS_OPT([-fno-strict-aliasing]) -elif test "$PORTNAME" = "aix"; then - # AIX's xlc has to have strict aliasing turned off too +elif test x"${CC}" = x"xlc"; then + # AIX xlc has to have strict aliasing turned off too PGAC_PROG_CC_CFLAGS_OPT([-qnoansialias]) fi @@ -499,7 +490,7 @@ fi # enable profiling if --enable-profiling if test "$enable_profiling" = yes && test "$ac_cv_prog_cc_g" = yes; then if test "$GCC" = yes; then - AC_DEFINE([PROFILE_PID_DIR], 1, + AC_DEFINE([PROFILE_PID_DIR], 1, [Define to 1 to allow profiling output to be saved separately for each process.]) CFLAGS="$CFLAGS -pg $PLATFORM_PROFILE_FLAGS" else @@ -884,6 +875,13 @@ PGAC_ARG_BOOL(with, libedit-preferred, yes, [ --without-libedit-preferred Don't prefer BSD Libedit over GNU Readline]) +# +# OSSP UUID library +# +PGAC_ARG_BOOL(with, ossp-uuid, no, [ --with-ossp-uuid use OSSP UUID library when building contrib/uuid-ossp]) +AC_SUBST(with_ossp_uuid) + + # # XML # @@ -908,6 +906,14 @@ fi AC_SUBST(with_libxml) +# +# XSLT +# +PGAC_ARG_BOOL(with, libxslt, no, [ --with-libxslt use XSLT support when building contrib/xml2], + [AC_DEFINE([USE_LIBXSLT], 1, [Define to 1 to use XSLT support when building contrib/xml2. (--with-libxslt)])]) + + +AC_SUBST(with_libxslt) # # tzdata @@ -981,6 +987,8 @@ AC_ARG_VAR(LDFLAGS_SL, [extra linker flags for linking shared libraries only]) AC_MSG_NOTICE([using CPPFLAGS=$CPPFLAGS]) AC_MSG_NOTICE([using LDFLAGS=$LDFLAGS]) +AC_ARG_VAR(LDFLAGS_SL) + PGAC_PROG_LD AC_SUBST(LD) AC_SUBST(with_gnu_ld) @@ -1199,6 +1207,20 @@ if test "$enable_snmp" = yes ; then AC_CHECK_LIB(netsnmp, netsnmp_ds_set_string, [], [AC_MSG_ERROR([library 'netsnmp' is required for snmp support])]) fi +if test "$with_libxslt" = yes ; then + AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])]) +fi + +# for contrib/uuid-ossp +if test "$with_ossp_uuid" = yes ; then + AC_CHECK_LIB(ossp-uuid, uuid_export, + [OSSP_UUID_LIBS="-lossp-uuid"], + [AC_CHECK_LIB(uuid, uuid_export, + [OSSP_UUID_LIBS="-luuid"], + [AC_MSG_ERROR([library 'ossp-uuid' or 'uuid' is required for OSSP-UUID])])]) +fi +AC_SUBST(OSSP_UUID_LIBS) + # Check for EMC Connect if test "$enable_connectemc" = yes ; then _LIBS="$LIBS" @@ -1334,6 +1356,10 @@ if test "$with_libxml" = yes ; then AC_CHECK_HEADER(libxml/parser.h, [], [AC_MSG_ERROR([header file is required for XML support])]) fi +if test "$with_libxslt" = yes ; then + AC_CHECK_HEADER(libxslt/xslt.h, [], [AC_MSG_ERROR([header file is required for XSLT support])]) +fi + if test "$with_ldap" = yes ; then if test "$PORTNAME" != "win32"; then AC_CHECK_HEADERS(ldap.h, [], @@ -1383,7 +1409,6 @@ if test "$enable_mapreduce" = yes; then AC_CHECK_HEADERS(yaml.h, [], [AC_MSG_ERROR([YAML includes required for Greenplum Mapreduce])]) fi - ## ## Types, structures, compiler characteristics ## @@ -1444,7 +1469,7 @@ if test "$with_krb5" = yes; then AC_MSG_CHECKING(for krb5_free_unparsed_name) AC_TRY_LINK([#include ], [krb5_free_unparsed_name(NULL,NULL);], - [AC_DEFINE(HAVE_KRB5_FREE_UNPARSED_NAME, 1, [Define to 1 if you have krb5_free_unparsed_name]) + [AC_DEFINE(HAVE_KRB5_FREE_UNPARSED_NAME, 1, [Define to 1 if you have krb5_free_unparsed_name]) AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)]) fi @@ -1458,6 +1483,13 @@ PGAC_VAR_INT_TIMEZONE AC_FUNC_ACCEPT_ARGTYPES PGAC_FUNC_GETTIMEOFDAY_1ARG +# Some versions of libedit contain strlcpy(), setproctitle(), and other +# symbols that that library has no business exposing to the world. Pending +# acquisition of a clue by those developers, ignore libedit (including its +# possible alias of libreadline) while checking for everything else. +LIBS_including_readline="$LIBS" +LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` + AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs]) # posix_fadvise() is a no-op on Solaris, so don't incur function overhead @@ -1582,8 +1614,6 @@ case $host_os in ;; esac -LIBS="$pgac_save_LIBS" - # System's version of getaddrinfo(), if any, may be used only if we found # a definition for struct addrinfo; see notes in src/include/getaddrinfo.h. # (Note: the AC_REPLACE_FUNCS probe fails on Windows, where the available @@ -1596,7 +1626,12 @@ else fi # Similarly, use system's getopt_long() only if system provides struct option. -if test x"$ac_cv_type_struct_option" = xyes ; then +# Solaris' getopt() doesn't do what we want for long options, so always use +# our versions on that platform. +if test "$PORTNAME" = "solaris"; then + AC_LIBOBJ(getopt) + AC_LIBOBJ(getopt_long) +elif test x"$ac_cv_type_struct_option" = xyes ; then AC_REPLACE_FUNCS([getopt_long]) else AC_LIBOBJ(getopt_long) @@ -1608,6 +1643,13 @@ if test "$PORTNAME" = "solaris"; then AC_LIBOBJ(getopt) fi +# mingw has adopted a GNU-centric interpretation of optind/optreset, +# so always use our version on Windows. +if test "$PORTNAME" = "win32"; then + AC_LIBOBJ(getopt) + AC_LIBOBJ(getopt_long) +fi + # Win32 support if test "$PORTNAME" = "win32"; then AC_REPLACE_FUNCS(gettimeofday) @@ -1620,13 +1662,6 @@ AC_DEFINE([HAVE_SYMLINK], 1, [Define to 1 if you have the `symlink' function.]) fi -if test "$with_readline" = yes; then - PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER - AC_CHECK_FUNCS([rl_completion_matches rl_filename_completion_function]) - AC_CHECK_FUNCS([replace_history_entry]) -fi - - dnl Cannot use AC_CHECK_FUNC because sigsetjmp may be a macro dnl (especially on GNU libc) dnl See also comments in c.h. @@ -1681,6 +1716,27 @@ case $host_os in esac +AC_CACHE_CHECK([for builtin locking functions], pgac_cv_gcc_int_atomics, +[AC_TRY_LINK([], + [int lock = 0; + __sync_lock_test_and_set(&lock, 1); + __sync_lock_release(&lock);], + [pgac_cv_gcc_int_atomics="yes"], + [pgac_cv_gcc_int_atomics="no"])]) +if test x"$pgac_cv_gcc_int_atomics" = x"yes"; then + AC_DEFINE(HAVE_GCC_INT_ATOMICS, 1, [Define to 1 if you have __sync_lock_test_and_set(int *) and friends.]) +fi + +# Lastly, restore full LIBS list and check for readline/libedit symbols +LIBS="$LIBS_including_readline" + +if test "$with_readline" = yes; then + PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER + AC_CHECK_FUNCS([rl_completion_matches rl_filename_completion_function]) + AC_CHECK_FUNCS([replace_history_entry]) +fi + + # # Pthreads # @@ -2001,8 +2057,8 @@ fi # Select shared-memory implementation type. if test "$PORTNAME" != "win32"; then -AC_DEFINE(USE_SYSV_SHARED_MEMORY, 1, [Define to select SysV-style shared memory.]) -SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c" + AC_DEFINE(USE_SYSV_SHARED_MEMORY, 1, [Define to select SysV-style shared memory.]) + SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c" else AC_DEFINE(USE_WIN32_SHARED_MEMORY, 1, [Define to select Win32-style shared memory.]) SHMEM_IMPLEMENTATION="src/backend/port/win32_shmem.c" diff --git a/contrib/Makefile b/contrib/Makefile index 16a90d0d5c0e..15296c9bd3f0 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/contrib/Makefile,v 1.71 2007/02/08 15:09:47 momjian Exp $ +# $PostgreSQL: pgsql/contrib/Makefile,v 1.82.2.1 2008/05/08 16:49:47 tgl Exp $ subdir = contrib top_builddir = .. @@ -15,8 +15,11 @@ WANTED_DIRS = \ gp_internal_tools \ gp_cancel_query \ indexscan \ - intarray - + dict_int \ + dict_xsyn \ + intarray \ + pageinspect \ + test_parser ifeq ($(with_openssl),yes) WANTED_DIRS += sslinfo @@ -32,7 +35,6 @@ endif # Missing: # start-scripts \ (does not have a makefile) -# xml2 \ (requires libxml installed) all install installdirs uninstall distprep clean distclean maintainer-clean: diff --git a/contrib/README b/contrib/README index 1ae49adc7045..fd6d7ff92b8f 100644 --- a/contrib/README +++ b/contrib/README @@ -116,10 +116,6 @@ pg_standby - Sample archive_command for warm standby operation by Simon Riggs -pg_stat_statements - - Track statement execution times across a whole database cluster - by Takahiro Itagaki - pg_trgm - Functions for determining the similarity of text based on trigram matching. diff --git a/contrib/adminpack/.gitignore b/contrib/adminpack/.gitignore new file mode 100644 index 000000000000..ea9a442f3a15 --- /dev/null +++ b/contrib/adminpack/.gitignore @@ -0,0 +1 @@ +/adminpack.sql diff --git a/contrib/btree_gist/.gitignore b/contrib/btree_gist/.gitignore new file mode 100644 index 000000000000..46318eaa7b06 --- /dev/null +++ b/contrib/btree_gist/.gitignore @@ -0,0 +1,3 @@ +/btree_gist.sql +# Generated subdirectories +/results/ diff --git a/contrib/chkpass/.gitignore b/contrib/chkpass/.gitignore new file mode 100644 index 000000000000..9029d666aa5e --- /dev/null +++ b/contrib/chkpass/.gitignore @@ -0,0 +1 @@ +/chkpass.sql diff --git a/contrib/chkpass/uninstall_chkpass.sql b/contrib/chkpass/uninstall_chkpass.sql index 2e0878bacb96..386fc95c5fa2 100644 --- a/contrib/chkpass/uninstall_chkpass.sql +++ b/contrib/chkpass/uninstall_chkpass.sql @@ -7,8 +7,6 @@ DROP OPERATOR <>(chkpass, text); DROP OPERATOR =(chkpass, text); -DROP OPERATOR =(text, chkpass); - DROP FUNCTION ne(chkpass, text); DROP FUNCTION eq(chkpass, text); diff --git a/contrib/cube/.gitignore b/contrib/cube/.gitignore new file mode 100644 index 000000000000..862c18f653c6 --- /dev/null +++ b/contrib/cube/.gitignore @@ -0,0 +1,6 @@ +/cubeparse.c +/cubeparse.h +/cubescan.c +/cube.sql +# Generated subdirectories +/results/ diff --git a/contrib/cube/uninstall_cube.sql b/contrib/cube/uninstall_cube.sql index fb4880bab53b..ddbbaec4201e 100644 --- a/contrib/cube/uninstall_cube.sql +++ b/contrib/cube/uninstall_cube.sql @@ -95,8 +95,4 @@ DROP FUNCTION cube_ne(cube, cube); DROP FUNCTION cube_eq(cube, cube); -DROP CAST (text AS cube); - -DROP FUNCTION cube(text); - DROP TYPE cube CASCADE; diff --git a/contrib/dblink/.gitignore b/contrib/dblink/.gitignore index c5f67742afc4..fb7e8728bcff 100644 --- a/contrib/dblink/.gitignore +++ b/contrib/dblink/.gitignore @@ -1 +1,3 @@ -dblink.sql +/dblink.sql +# Generated subdirectories +/results/ diff --git a/contrib/dblink/README.dblink b/contrib/dblink/README.dblink deleted file mode 100755 index 5b6ffa8ae789..000000000000 --- a/contrib/dblink/README.dblink +++ /dev/null @@ -1,109 +0,0 @@ -/* - * dblink - * - * Functions returning results from a remote database - * - * Joe Conway - * And contributors: - * Darko Prenosil - * Shridhar Daithankar - * Kai Londenberg (K.Londenberg@librics.de) - * - * Copyright (c) 2001-2007, PostgreSQL Global Development Group - * ALL RIGHTS RESERVED; - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without a written agreement - * is hereby granted, provided that the above copyright notice and this - * paragraph and the following two paragraphs appear in all copies. - * - * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING - * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS - * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - */ - -Release Notes: - 27 August 2006 - - Added async query capability. Original patch by - Kai Londenberg (K.Londenberg@librics.de), modified by Joe Conway - Version 0.7 (as of 25 Feb, 2004) - - Added new version of dblink, dblink_exec, dblink_open, dblink_close, - and, dblink_fetch -- allows ERROR on remote side of connection to - throw NOTICE locally instead of ERROR - Version 0.6 - - functions deprecated in 0.5 have been removed - - added ability to create "named" persistent connections - Version 0.5 - - dblink now supports use directly as a table function; this is the new - preferred usage going forward - - Use of dblink_tok is now deprecated; original form of dblink is also - deprecated. They _will_ be removed in the next version. - - dblink_last_oid is also deprecated; use dblink_exec() which returns - the command status as a single row, single column result. - - Original dblink, dblink_tok, and dblink_last_oid are commented out in - dblink.sql; remove the comments to use the deprecated functions. - - dblink_strtok() and dblink_replace() functions were removed. Use - split() and replace() respectively (new backend functions in - PostgreSQL 7.3) instead. - - New functions: dblink_exec() for non-SELECT queries; dblink_connect() - opens connection that persists for duration of a backend; - dblink_disconnect() closes a persistent connection; dblink_open() - opens a cursor; dblink_fetch() fetches results from an open cursor. - dblink_close() closes a cursor. - - New test suite: dblink_check.sh, dblink.test.sql, - dblink.test.expected.out. Execute dblink_check.sh from the same - directory as the other two files. Output is dblink.test.out and - dblink.test.diff. Note that dblink.test.sql is a good source - of example usage. - - Version 0.4 - - removed cursor wrap around input sql to allow for remote - execution of INSERT/UPDATE/DELETE - - dblink now returns a resource id instead of a real pointer - - added several utility functions -- see below - - Version 0.3 - - fixed dblink invalid pointer causing corrupt elog message - - fixed dblink_tok improper handling of null results - - fixed examples in README.dblink - - Version 0.2 - - initial release - -Installation: - Place these files in a directory called 'dblink' under 'contrib' in the PostgreSQL source tree. Then run: - - make - make install - - You can use dblink.sql to create the functions in your database of choice, e.g. - - psql template1 < dblink.sql - - installs dblink functions into database template1 - -Documentation: - - Note: Parameters representing relation names must include double - quotes if the names are mixed-case or contain special characters. They - must also be appropriately qualified with schema name if applicable. - - See the following files: - doc/connection - doc/cursor - doc/query - doc/execute - doc/misc - -================================================================== --- Joe Conway - diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c index 840de268bf48..983f7d6f0b7c 100644 --- a/contrib/dblink/dblink.c +++ b/contrib/dblink/dblink.c @@ -8,7 +8,7 @@ * Darko Prenosil * Shridhar Daithankar * - * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.69.2.2 2009/01/03 19:57:54 joe Exp $ + * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.69.2.8 2010/06/15 19:04:28 tgl Exp $ * Copyright (c) 2001-2008, PostgreSQL Global Development Group * ALL RIGHTS RESERVED; * @@ -37,6 +37,7 @@ #include "libpq-fe.h" #include "fmgr.h" #include "funcapi.h" +#include "miscadmin.h" #include "access/genam.h" #include "access/heapam.h" #include "access/tupdesc.h" @@ -47,11 +48,11 @@ #include "executor/executor.h" #include "executor/spi.h" #include "lib/stringinfo.h" -#include "miscadmin.h" #include "nodes/execnodes.h" #include "nodes/nodes.h" #include "nodes/pg_list.h" #include "parser/parse_type.h" +#include "parser/scansup.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/array.h" @@ -82,20 +83,24 @@ static remoteConn *getConnectionByName(const char *name); static HTAB *createConnHash(void); static void createNewConnection(const char *name, remoteConn * rconn); static void deleteConnection(const char *name); -static char **get_pkey_attnames(Oid relid, int16 *numatts); +static char **get_pkey_attnames(Relation rel, int16 *numatts); static char **get_text_array_contents(ArrayType *array, int *numitems); -static char *get_sql_insert(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals); -static char *get_sql_delete(Oid relid, int2vector *pkattnums, int16 pknumatts, char **tgt_pkattvals); -static char *get_sql_update(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals); +static char *get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals); +static char *get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **tgt_pkattvals); +static char *get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals); static char *quote_literal_cstr(char *rawstr); static char *quote_ident_cstr(char *rawstr); -static int16 get_attnum_pk_pos(int2vector *pkattnums, int16 pknumatts, int16 key); -static HeapTuple get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals); -static Oid get_relid_from_relname(text *relname_text); -static char *generate_relation_name(Oid relid); +static int get_attnum_pk_pos(int *pkattnums, int pknumatts, int key); +static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals); +static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode); +static char *generate_relation_name(Relation rel); static void dblink_connstr_check(const char *connstr); static void dblink_security_check(PGconn *conn, remoteConn *rconn); static void dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail); +static void dblink_security_check(PGconn *conn, remoteConn *rconn); +static void validate_pkattnums(Relation rel, + int2vector *pkattnums_arg, int32 pknumatts_arg, + int **pkattnums, int *pknumatts); /* Global */ static remoteConn *pconn = NULL; @@ -129,6 +134,14 @@ typedef struct remoteConnHashEnt } \ } while (0) +#define xpstrdup(tgtvar_, srcvar_) \ + do { \ + if (srcvar_) \ + tgtvar_ = pstrdup(srcvar_); \ + else \ + tgtvar_ = NULL; \ + } while (0) + #define DBLINK_RES_INTERNALERROR(p2) \ do { \ msg = pstrdup(PQerrorMessage(conn)); \ @@ -863,7 +876,6 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async, bool do_get) PQfinish(conn); MemoryContextSwitchTo(oldcontext); SRF_RETURN_DONE(funcctx); - } } if (PQresultStatus(res) == PGRES_COMMAND_OK) @@ -1136,20 +1148,23 @@ PG_FUNCTION_INFO_V1(dblink_exec); Datum dblink_exec(PG_FUNCTION_ARGS) { + text *volatile sql_cmd_status = NULL; + PGconn *volatile conn = NULL; + volatile bool freeconn = false; + + DBLINK_INIT; + + PG_TRY(); + { char *msg; PGresult *res = NULL; - text *sql_cmd_status = NULL; TupleDesc tupdesc = NULL; - PGconn *conn = NULL; char *connstr = NULL; char *sql = NULL; char *conname = NULL; remoteConn *rconn = NULL; - bool freeconn = false; bool fail = true; /* default to backward compatible behavior */ - DBLINK_INIT; - if (PG_NARGS() == 3) { /* must be text,text,bool */ @@ -1228,8 +1243,17 @@ dblink_exec(PG_FUNCTION_ARGS) (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), errmsg("statement returning results not allowed"))); } + } + PG_CATCH(); + { + /* if needed, close the connection to the database */ + if (freeconn) + PQfinish(conn); + PG_RE_THROW(); + } + PG_END_TRY(); - /* if needed, close the connection to the database and cleanup */ + /* if needed, close the connection to the database */ if (freeconn) PQfinish(conn); @@ -1248,7 +1272,6 @@ Datum dblink_get_pkey(PG_FUNCTION_ARGS) { int16 numatts; - Oid relid; char **results; FuncCallContext *funcctx; int32 call_cntr; @@ -1259,7 +1282,8 @@ dblink_get_pkey(PG_FUNCTION_ARGS) /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { - TupleDesc tupdesc = NULL; + Relation rel; + TupleDesc tupdesc; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); @@ -1269,13 +1293,13 @@ dblink_get_pkey(PG_FUNCTION_ARGS) */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - /* convert relname to rel Oid */ - relid = get_relid_from_relname(PG_GETARG_TEXT_P(0)); - if (!OidIsValid(relid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" does not exist", - GET_STR(PG_GETARG_TEXT_P(0))))); + /* open target relation */ + rel = get_rel_from_relname(PG_GETARG_TEXT_P(0), AccessShareLock, ACL_SELECT); + + /* get the array of attnums */ + results = get_pkey_attnames(rel, &numatts); + + relation_close(rel, AccessShareLock); /* * need a tuple descriptor representing one INT and one TEXT column @@ -1293,9 +1317,6 @@ dblink_get_pkey(PG_FUNCTION_ARGS) attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; - /* get an array of attnums */ - results = get_pkey_attnames(relid, &numatts); - if ((results != NULL) && (numatts > 0)) { funcctx->max_calls = numatts; @@ -1378,12 +1399,13 @@ Datum dblink_build_sql_insert(PG_FUNCTION_ARGS) { text *relname_text = PG_GETARG_TEXT_P(0); - int2vector *pkattnums = (int2vector *) PG_GETARG_POINTER(1); - int32 pknumatts_tmp = PG_GETARG_INT32(2); + int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1); + int32 pknumatts_arg = PG_GETARG_INT32(2); ArrayType *src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); - Oid relid; - int16 pknumatts = 0; + Relation rel; + int *pkattnums; + int pknumatts; char **src_pkattvals; char **tgt_pkattvals; int src_nitems; @@ -1391,30 +1413,15 @@ dblink_build_sql_insert(PG_FUNCTION_ARGS) char *sql; /* - * Convert relname to rel OID. + * Open target relation. */ - relid = get_relid_from_relname(relname_text); - if (!OidIsValid(relid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" does not exist", - GET_STR(relname_text)))); + rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT); /* - * There should be at least one key attribute + * Process pkattnums argument. */ - if (pknumatts_tmp <= 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("number of key attributes must be > 0"))); - - if (pknumatts_tmp <= SHRT_MAX) - pknumatts = pknumatts_tmp; - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("input for number of primary key " \ - "attributes too large"))); + validate_pkattnums(rel, pkattnums_arg, pknumatts_arg, + &pkattnums, &pknumatts); /* * Source array is made up of key values that will be used to locate the @@ -1449,7 +1456,12 @@ dblink_build_sql_insert(PG_FUNCTION_ARGS) /* * Prep work is finally done. Go get the SQL string. */ - sql = get_sql_insert(relid, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals); + sql = get_sql_insert(rel, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals); + + /* + * Now we can close the relation. + */ + relation_close(rel, AccessShareLock); /* * And send it @@ -1478,40 +1490,26 @@ Datum dblink_build_sql_delete(PG_FUNCTION_ARGS) { text *relname_text = PG_GETARG_TEXT_P(0); - int2vector *pkattnums = (int2vector *) PG_GETARG_POINTER(1); - int32 pknumatts_tmp = PG_GETARG_INT32(2); + int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1); + int32 pknumatts_arg = PG_GETARG_INT32(2); ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); - Oid relid; - int16 pknumatts = 0; + Relation rel; + int *pkattnums; + int pknumatts; char **tgt_pkattvals; int tgt_nitems; char *sql; /* - * Convert relname to rel OID. + * Open target relation. */ - relid = get_relid_from_relname(relname_text); - if (!OidIsValid(relid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" does not exist", - GET_STR(relname_text)))); + rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT); /* - * There should be at least one key attribute + * Process pkattnums argument. */ - if (pknumatts_tmp <= 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("number of key attributes must be > 0"))); - - if (pknumatts_tmp <= SHRT_MAX) - pknumatts = pknumatts_tmp; - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("input for number of primary key " \ - "attributes too large"))); + validate_pkattnums(rel, pkattnums_arg, pknumatts_arg, + &pkattnums, &pknumatts); /* * Target array is made up of key values that will be used to build the @@ -1531,7 +1529,12 @@ dblink_build_sql_delete(PG_FUNCTION_ARGS) /* * Prep work is finally done. Go get the SQL string. */ - sql = get_sql_delete(relid, pkattnums, pknumatts, tgt_pkattvals); + sql = get_sql_delete(rel, pkattnums, pknumatts, tgt_pkattvals); + + /* + * Now we can close the relation. + */ + relation_close(rel, AccessShareLock); /* * And send it @@ -1564,12 +1567,13 @@ Datum dblink_build_sql_update(PG_FUNCTION_ARGS) { text *relname_text = PG_GETARG_TEXT_P(0); - int2vector *pkattnums = (int2vector *) PG_GETARG_POINTER(1); - int32 pknumatts_tmp = PG_GETARG_INT32(2); + int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1); + int32 pknumatts_arg = PG_GETARG_INT32(2); ArrayType *src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); - Oid relid; - int16 pknumatts = 0; + Relation rel; + int *pkattnums; + int pknumatts; char **src_pkattvals; char **tgt_pkattvals; int src_nitems; @@ -1577,30 +1581,15 @@ dblink_build_sql_update(PG_FUNCTION_ARGS) char *sql; /* - * Convert relname to rel OID. + * Open target relation. */ - relid = get_relid_from_relname(relname_text); - if (!OidIsValid(relid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" does not exist", - GET_STR(relname_text)))); + rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT); /* - * There should be one source array key values for each key attnum + * Process pkattnums argument. */ - if (pknumatts_tmp <= 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("number of key attributes must be > 0"))); - - if (pknumatts_tmp <= SHRT_MAX) - pknumatts = pknumatts_tmp; - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("input for number of primary key " \ - "attributes too large"))); + validate_pkattnums(rel, pkattnums_arg, pknumatts_arg, + &pkattnums, &pknumatts); /* * Source array is made up of key values that will be used to locate the @@ -1635,7 +1624,12 @@ dblink_build_sql_update(PG_FUNCTION_ARGS) /* * Prep work is finally done. Go get the SQL string. */ - sql = get_sql_update(relid, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals); + sql = get_sql_update(rel, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals); + + /* + * Now we can close the relation. + */ + relation_close(rel, AccessShareLock); /* * And send it @@ -1655,7 +1649,7 @@ dblink_build_sql_update(PG_FUNCTION_ARGS) * Return NULL, and set numatts = 0, if no primary key exists. */ static char ** -get_pkey_attnames(Oid relid, int16 *numatts) +get_pkey_attnames(Relation rel, int16 *numatts) { Relation indexRelation; ScanKeyData skey; @@ -1663,22 +1657,11 @@ get_pkey_attnames(Oid relid, int16 *numatts) HeapTuple indexTuple; int i; char **result = NULL; - Relation rel; TupleDesc tupdesc; - AclResult aclresult; /* initialize numatts to 0 in case no primary key exists */ *numatts = 0; - /* open relation using relid, check permissions, get tupdesc */ - rel = relation_open(relid, AccessShareLock); - - aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), - ACL_SELECT); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - tupdesc = rel->rd_att; /* Prepare to scan pg_index for entries having indrelid = this rel. */ @@ -1686,7 +1669,7 @@ get_pkey_attnames(Oid relid, int16 *numatts) ScanKeyInit(&skey, Anum_pg_index_indrelid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); + ObjectIdGetDatum(RelationGetRelid(rel))); scan = systable_beginscan(indexRelation, IndexIndrelidIndexId, true, SnapshotNow, 1, &skey); @@ -1712,7 +1695,6 @@ get_pkey_attnames(Oid relid, int16 *numatts) systable_endscan(scan); heap_close(indexRelation, AccessShareLock); - relation_close(rel, AccessShareLock); return result; } @@ -1779,32 +1761,27 @@ get_text_array_contents(ArrayType *array, int *numitems) } static char * -get_sql_insert(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals) +get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals) { - Relation rel; char *relname; HeapTuple tuple; TupleDesc tupdesc; int natts; StringInfoData buf; char *val; - int16 key; + int key; int i; bool needComma; initStringInfo(&buf); /* get relation name including any needed schema prefix and quoting */ - relname = generate_relation_name(relid); + relname = generate_relation_name(rel); - /* - * Open relation using relid - */ - rel = relation_open(relid, AccessShareLock); tupdesc = rel->rd_att; natts = tupdesc->natts; - tuple = get_tuple_of_interest(relid, pkattnums, pknumatts, src_pkattvals); + tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals); if (!tuple) ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), @@ -1829,7 +1806,7 @@ get_sql_insert(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka appendStringInfo(&buf, ") VALUES("); /* - * remember attvals are 1 based + * Note: i is physical column number (counting from 0). */ needComma = false; for (i = 0; i < natts; i++) @@ -1840,12 +1817,9 @@ get_sql_insert(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka if (needComma) appendStringInfo(&buf, ","); - if (tgt_pkattvals != NULL) - key = get_attnum_pk_pos(pkattnums, pknumatts, i + 1); - else - key = -1; + key = get_attnum_pk_pos(pkattnums, pknumatts, i); - if (key > -1) + if (key >= 0) val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL; else val = SPI_getvalue(tuple, tupdesc, i + 1); @@ -1861,46 +1835,34 @@ get_sql_insert(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka } appendStringInfo(&buf, ")"); - relation_close(rel, AccessShareLock); return (buf.data); } static char * -get_sql_delete(Oid relid, int2vector *pkattnums, int16 pknumatts, char **tgt_pkattvals) +get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **tgt_pkattvals) { - Relation rel; char *relname; TupleDesc tupdesc; - int natts; StringInfoData buf; int i; initStringInfo(&buf); /* get relation name including any needed schema prefix and quoting */ - relname = generate_relation_name(relid); + relname = generate_relation_name(rel); - /* - * Open relation using relid - */ - rel = relation_open(relid, AccessShareLock); tupdesc = rel->rd_att; - natts = tupdesc->natts; appendStringInfo(&buf, "DELETE FROM %s WHERE ", relname); for (i = 0; i < pknumatts; i++) { - int16 pkattnum = pkattnums->values[i]; + int pkattnum = pkattnums[i]; if (i > 0) appendStringInfo(&buf, " AND "); appendStringInfoString(&buf, - quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); - - if (tgt_pkattvals == NULL) - /* internal error */ - elog(ERROR, "target key array must not be NULL"); + quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum]->attname))); if (tgt_pkattvals[i] != NULL) appendStringInfo(&buf, " = %s", @@ -1909,37 +1871,31 @@ get_sql_delete(Oid relid, int2vector *pkattnums, int16 pknumatts, char **tgt_pka appendStringInfo(&buf, " IS NULL"); } - relation_close(rel, AccessShareLock); return (buf.data); } static char * -get_sql_update(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals) +get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals) { - Relation rel; char *relname; HeapTuple tuple; TupleDesc tupdesc; int natts; StringInfoData buf; char *val; - int16 key; + int key; int i; bool needComma; initStringInfo(&buf); /* get relation name including any needed schema prefix and quoting */ - relname = generate_relation_name(relid); + relname = generate_relation_name(rel); - /* - * Open relation using relid - */ - rel = relation_open(relid, AccessShareLock); tupdesc = rel->rd_att; natts = tupdesc->natts; - tuple = get_tuple_of_interest(relid, pkattnums, pknumatts, src_pkattvals); + tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals); if (!tuple) ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), @@ -1947,6 +1903,9 @@ get_sql_update(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka appendStringInfo(&buf, "UPDATE %s SET ", relname); + /* + * Note: i is physical column number (counting from 0). + */ needComma = false; for (i = 0; i < natts; i++) { @@ -1959,12 +1918,9 @@ get_sql_update(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka appendStringInfo(&buf, "%s = ", quote_ident_cstr(NameStr(tupdesc->attrs[i]->attname))); - if (tgt_pkattvals != NULL) - key = get_attnum_pk_pos(pkattnums, pknumatts, i + 1); - else - key = -1; + key = get_attnum_pk_pos(pkattnums, pknumatts, i); - if (key > -1) + if (key >= 0) val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL; else val = SPI_getvalue(tuple, tupdesc, i + 1); @@ -1983,29 +1939,22 @@ get_sql_update(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka for (i = 0; i < pknumatts; i++) { - int16 pkattnum = pkattnums->values[i]; + int pkattnum = pkattnums[i]; if (i > 0) appendStringInfo(&buf, " AND "); appendStringInfo(&buf, "%s", - quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); + quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum]->attname))); - if (tgt_pkattvals != NULL) - val = tgt_pkattvals[i] ? pstrdup(tgt_pkattvals[i]) : NULL; - else - val = SPI_getvalue(tuple, tupdesc, pkattnum); + val = tgt_pkattvals[i]; if (val != NULL) - { appendStringInfo(&buf, " = %s", quote_literal_cstr(val)); - pfree(val); - } else appendStringInfo(&buf, " IS NULL"); } - relation_close(rel, AccessShareLock); return (buf.data); } @@ -2045,8 +1994,8 @@ quote_ident_cstr(char *rawstr) return result; } -static int16 -get_attnum_pk_pos(int2vector *pkattnums, int16 pknumatts, int16 key) +static int +get_attnum_pk_pos(int *pkattnums, int pknumatts, int key) { int i; @@ -2054,35 +2003,23 @@ get_attnum_pk_pos(int2vector *pkattnums, int16 pknumatts, int16 key) * Not likely a long list anyway, so just scan for the value */ for (i = 0; i < pknumatts; i++) - if (key == pkattnums->values[i]) + if (key == pkattnums[i]) return i; return -1; } static HeapTuple -get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals) +get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals) { - Relation rel; char *relname; TupleDesc tupdesc; + int natts; StringInfoData buf; int ret; HeapTuple tuple; int i; - initStringInfo(&buf); - - /* get relation name including any needed schema prefix and quoting */ - relname = generate_relation_name(relid); - - /* - * Open relation using relid - */ - rel = relation_open(relid, AccessShareLock); - tupdesc = CreateTupleDescCopy(rel->rd_att); - relation_close(rel, AccessShareLock); - /* * Connect to SPI manager */ @@ -2090,21 +2027,46 @@ get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char ** /* internal error */ elog(ERROR, "SPI connect failure - returned %d", ret); + initStringInfo(&buf); + + /* get relation name including any needed schema prefix and quoting */ + relname = generate_relation_name(rel); + + tupdesc = rel->rd_att; + natts = tupdesc->natts; + /* - * Build sql statement to look up tuple of interest Use src_pkattvals as - * the criteria. + * Build sql statement to look up tuple of interest, ie, the one matching + * src_pkattvals. We used to use "SELECT *" here, but it's simpler to + * generate a result tuple that matches the table's physical structure, + * with NULLs for any dropped columns. Otherwise we have to deal with + * two different tupdescs and everything's very confusing. */ - appendStringInfo(&buf, "SELECT * FROM %s WHERE ", relname); + appendStringInfoString(&buf, "SELECT "); + + for (i = 0; i < natts; i++) + { + if (i > 0) + appendStringInfoString(&buf, ", "); + + if (tupdesc->attrs[i]->attisdropped) + appendStringInfoString(&buf, "NULL"); + else + appendStringInfoString(&buf, + quote_ident_cstr(NameStr(tupdesc->attrs[i]->attname))); + } + + appendStringInfo(&buf, " FROM %s WHERE ", relname); for (i = 0; i < pknumatts; i++) { - int16 pkattnum = pkattnums->values[i]; + int pkattnum = pkattnums[i]; if (i > 0) appendStringInfo(&buf, " AND "); appendStringInfoString(&buf, - quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); + quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum]->attname))); if (src_pkattvals[i] != NULL) appendStringInfo(&buf, " = %s", @@ -2152,52 +2114,49 @@ get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char ** return NULL; } -static Oid -get_relid_from_relname(text *relname_text) +/* + * Open the relation named by relname_text, acquire specified type of lock, + * verify we have specified permissions. + * Caller must close rel when done with it. + */ +static Relation +get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode) { RangeVar *relvar; Relation rel; - Oid relid; + AclResult aclresult; relvar = makeRangeVarFromNameList(textToQualifiedNameList(relname_text)); - rel = heap_openrv(relvar, AccessShareLock); - relid = RelationGetRelid(rel); - relation_close(rel, AccessShareLock); + rel = heap_openrv(relvar, lockmode); + + aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), + aclmode); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); - return relid; + return rel; } /* * generate_relation_name - copied from ruleutils.c - * Compute the name to display for a relation specified by OID + * Compute the name to display for a relation * * The result includes all necessary quoting and schema-prefixing. */ static char * -generate_relation_name(Oid relid) +generate_relation_name(Relation rel) { - HeapTuple tp; - Form_pg_class reltup; char *nspname; char *result; - tp = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); - if (!HeapTupleIsValid(tp)) - elog(ERROR, "cache lookup failed for relation %u", relid); - - reltup = (Form_pg_class) GETSTRUCT(tp); - /* Qualify the name if not visible in search path */ - if (RelationIsVisible(relid)) + if (RelationIsVisible(RelationGetRelid(rel))) nspname = NULL; else - nspname = get_namespace_name(reltup->relnamespace); - - result = quote_qualified_identifier(nspname, NameStr(reltup->relname)); + nspname = get_namespace_name(rel->rd_rel->relnamespace); - ReleaseSysCache(tp); + result = quote_qualified_identifier(nspname, RelationGetRelationName(rel)); return result; } @@ -2207,13 +2166,13 @@ static remoteConn * getConnectionByName(const char *name) { remoteConnHashEnt *hentry; - char key[NAMEDATALEN]; + char *key; if (!remoteConnHash) remoteConnHash = createConnHash(); - MemSet(key, 0, NAMEDATALEN); - snprintf(key, NAMEDATALEN - 1, "%s", name); + key = pstrdup(name); + truncate_identifier(key, strlen(key), false); hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, key, HASH_FIND, NULL); @@ -2239,20 +2198,25 @@ createNewConnection(const char *name, remoteConn * rconn) { remoteConnHashEnt *hentry; bool found; - char key[NAMEDATALEN]; + char *key; if (!remoteConnHash) remoteConnHash = createConnHash(); - MemSet(key, 0, NAMEDATALEN); - snprintf(key, NAMEDATALEN - 1, "%s", name); + key = pstrdup(name); + truncate_identifier(key, strlen(key), true); hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, key, HASH_ENTER, &found); if (found) + { + PQfinish(rconn->conn); + pfree(rconn); + ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("duplicate connection name"))); + } hentry->rconn = rconn; strlcpy(hentry->name, name, sizeof(hentry->name)); @@ -2263,14 +2227,13 @@ deleteConnection(const char *name) { remoteConnHashEnt *hentry; bool found; - char key[NAMEDATALEN]; + char *key; if (!remoteConnHash) remoteConnHash = createConnHash(); - MemSet(key, 0, NAMEDATALEN); - snprintf(key, NAMEDATALEN - 1, "%s", name); - + key = pstrdup(name); + truncate_identifier(key, strlen(key), false); hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, key, HASH_REMOVE, &found); @@ -2300,6 +2263,7 @@ dblink_security_check(PGconn *conn, remoteConn *rconn) } } } + /* * For non-superusers, insist that the connstr specify a password. This * prevents a password from being picked up from .pgpass, a service file, @@ -2390,3 +2354,52 @@ dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_ errcontext("Error occurred on dblink connection named \"%s\": %s.", dblink_context_conname, dblink_context_msg))); } + +/* + * Validate the PK-attnums argument for dblink_build_sql_insert() and related + * functions, and translate to the internal representation. + * + * The user supplies an int2vector of 1-based physical attnums, plus a count + * argument (the need for the separate count argument is historical, but we + * still check it). We check that each attnum corresponds to a valid, + * non-dropped attribute of the rel. We do *not* prevent attnums from being + * listed twice, though the actual use-case for such things is dubious. + * + * The internal representation is a palloc'd int array of 0-based physical + * attnums. + */ +static void +validate_pkattnums(Relation rel, + int2vector *pkattnums_arg, int32 pknumatts_arg, + int **pkattnums, int *pknumatts) +{ + TupleDesc tupdesc = rel->rd_att; + int natts = tupdesc->natts; + int i; + + /* Don't take more array elements than there are */ + pknumatts_arg = Min(pknumatts_arg, pkattnums_arg->dim1); + + /* Must have at least one pk attnum selected */ + if (pknumatts_arg <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("number of key attributes must be > 0"))); + + /* Allocate output array */ + *pkattnums = (int *) palloc(pknumatts_arg * sizeof(int)); + *pknumatts = pknumatts_arg; + + /* Validate attnums and convert to internal form */ + for (i = 0; i < pknumatts_arg; i++) + { + int pkattnum = pkattnums_arg->values[i]; + + if (pkattnum <= 0 || pkattnum > natts || + tupdesc->attrs[pkattnum - 1]->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid attribute number %d", pkattnum))); + (*pkattnums)[i] = pkattnum - 1; + } +} diff --git a/contrib/dblink/doc/connection b/contrib/dblink/doc/connection deleted file mode 100755 index ba70ecd9f1b6..000000000000 --- a/contrib/dblink/doc/connection +++ /dev/null @@ -1,122 +0,0 @@ -$PostgreSQL: pgsql/contrib/dblink/doc/connection,v 1.4.2.1 2007/07/09 01:32:30 joe Exp $ -================================================================== -Name - -dblink_connect -- Opens a persistent connection to a remote database - -Synopsis - -dblink_connect(text connstr) -dblink_connect(text connname, text connstr) - -Inputs - - connname - if 2 arguments are given, the first is used as a name for a persistent - connection - - connstr - - standard libpq format connection string, - e.g. "hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd" - - if only one argument is given, the connection is unnamed; only one unnamed - connection can exist at a time - -Outputs - - Returns status = "OK" - -Notes - - Only superusers may use dblink_connect to create non-password - authenticated connections. If non-superusers need this capability, - use dblink_connect_u instead. - -Example usage - -select dblink_connect('dbname=postgres'); - dblink_connect ----------------- - OK -(1 row) - -select dblink_connect('myconn','dbname=postgres'); - dblink_connect ----------------- - OK -(1 row) - -================================================================== -Name - -dblink_connect_u -- Opens a persistent connection to a remote database - -Synopsis - -dblink_connect_u(text connstr) -dblink_connect_u(text connname, text connstr) - -Inputs - - connname - if 2 arguments are given, the first is used as a name for a persistent - connection - - connstr - - standard libpq format connection string, - e.g. "hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd" - - if only one argument is given, the connection is unnamed; only one unnamed - connection can exist at a time - -Outputs - - Returns status = "OK" - -Notes - - With dblink_connect_u, a non-superuser may connect to any database server - using any authentication method. If the authentication method specified - for a particular user does not require a password, impersonation and - therefore escalation of privileges may occur. For this reason, - dblink_connect_u is initially installed with all privileges revoked from - public. Privilege to these functions should be granted with care. - -Example usage - - -================================================================== -Name - -dblink_disconnect -- Closes a persistent connection to a remote database - -Synopsis - -dblink_disconnect() -dblink_disconnect(text connname) - -Inputs - - connname - if an argument is given, it is used as a name for a persistent - connection to close; otherwiase the unnamed connection is closed - -Outputs - - Returns status = "OK" - -Example usage - -test=# select dblink_disconnect(); - dblink_disconnect -------------------- - OK -(1 row) - -select dblink_disconnect('myconn'); - dblink_disconnect -------------------- - OK -(1 row) diff --git a/contrib/dblink/doc/cursor b/contrib/dblink/doc/cursor deleted file mode 100755 index 874599582885..000000000000 --- a/contrib/dblink/doc/cursor +++ /dev/null @@ -1,220 +0,0 @@ -$PostgreSQL: pgsql/contrib/dblink/doc/cursor,v 1.6 2006/03/11 04:38:29 momjian Exp $ -================================================================== -Name - -dblink_open -- Opens a cursor on a remote database - -Synopsis - -dblink_open(text cursorname, text sql [, bool fail_on_error]) -dblink_open(text connname, text cursorname, text sql [, bool fail_on_error]) - -Inputs - - connname - if three arguments are present, the first is taken as the specific - connection name to use; otherwise the unnamed connection is assumed - - cursorname - - a reference name for the cursor - - sql - - sql statement that you wish to execute on the remote host - e.g. "select * from pg_class" - - fail_on_error - - If true (default when not present) then an ERROR thrown on the remote side - of the connection causes an ERROR to also be thrown locally. If false, the - remote ERROR is locally treated as a NOTICE, and the return value is set - to 'ERROR'. - -Outputs - - Returns status = "OK" - -Note - 1) dblink_connect(text connstr) must be executed first - 2) dblink_open starts an explicit transaction. If, after using dblink_open, - you use dblink_exec to change data, and then an error occurs or you use - dblink_disconnect without a dblink_close first, your change *will* be - lost. Also, using dblink_close explicitly ends the transaction and thus - effectively closes *all* open cursors. - -Example usage - -test=# select dblink_connect('dbname=postgres'); - dblink_connect ----------------- - OK -(1 row) - -test=# select dblink_open('foo','select proname, prosrc from pg_proc'); - dblink_open -------------- - OK -(1 row) - -================================================================== -Name - -dblink_fetch -- Returns a set from an open cursor on a remote database - -Synopsis - -dblink_fetch(text cursorname, int32 howmany [, bool fail_on_error]) -dblink_fetch(text connname, text cursorname, int32 howmany [, bool fail_on_error]) - -Inputs - - connname - if three arguments are present, the first is taken as the specific - connection name to use; otherwise the unnamed connection is assumed - - cursorname - - The reference name for the cursor - - howmany - - Maximum number of rows to retrieve. The next howmany rows are fetched, - starting at the current cursor position, moving forward. Once the cursor - has positioned to the end, no more rows are produced. - - fail_on_error - - If true (default when not present) then an ERROR thrown on the remote side - of the connection causes an ERROR to also be thrown locally. If false, the - remote ERROR is locally treated as a NOTICE, and no rows are returned. - -Outputs - - Returns setof record - -Note - - On a mismatch between the number of return fields as specified in the FROM - clause, and the actual number of fields returned by the remote cursor, an - ERROR will be thrown. In this event, the remote cursor is still advanced - by as many rows as it would have been if the ERROR had not occurred. - -Example usage - -test=# select dblink_connect('dbname=postgres'); - dblink_connect ----------------- - OK -(1 row) - -test=# select dblink_open('foo','select proname, prosrc from pg_proc where proname like ''bytea%'''); - dblink_open -------------- - OK -(1 row) - -test=# select * from dblink_fetch('foo',5) as (funcname name, source text); - funcname | source -----------+---------- - byteacat | byteacat - byteacmp | byteacmp - byteaeq | byteaeq - byteage | byteage - byteagt | byteagt -(5 rows) - -test=# select * from dblink_fetch('foo',5) as (funcname name, source text); - funcname | source ------------+----------- - byteain | byteain - byteale | byteale - bytealike | bytealike - bytealt | bytealt - byteane | byteane -(5 rows) - -test=# select * from dblink_fetch('foo',5) as (funcname name, source text); - funcname | source -------------+------------ - byteanlike | byteanlike - byteaout | byteaout -(2 rows) - -test=# select * from dblink_fetch('foo',5) as (funcname name, source text); - funcname | source -----------+-------- -(0 rows) - -================================================================== -Name - -dblink_close -- Closes a cursor on a remote database - -Synopsis - -dblink_close(text cursorname [, bool fail_on_error]) -dblink_close(text connname, text cursorname [, bool fail_on_error]) - -Inputs - - connname - if two arguments are present, the first is taken as the specific - connection name to use; otherwise the unnamed connection is assumed - - cursorname - - a reference name for the cursor - - fail_on_error - - If true (default when not present) then an ERROR thrown on the remote side - of the connection causes an ERROR to also be thrown locally. If false, the - remote ERROR is locally treated as a NOTICE, and the return value is set - to 'ERROR'. - -Outputs - - Returns status = "OK" - -Note - dblink_connect(text connstr) or dblink_connect(text connname, text connstr) - must be executed first. - -Example usage - -test=# select dblink_connect('dbname=postgres'); - dblink_connect ----------------- - OK -(1 row) - -test=# select dblink_open('foo','select proname, prosrc from pg_proc'); - dblink_open -------------- - OK -(1 row) - -test=# select dblink_close('foo'); - dblink_close --------------- - OK -(1 row) - -select dblink_connect('myconn','dbname=regression'); - dblink_connect ----------------- - OK -(1 row) - -select dblink_open('myconn','foo','select proname, prosrc from pg_proc'); - dblink_open -------------- - OK -(1 row) - -select dblink_close('myconn','foo'); - dblink_close --------------- - OK -(1 row) diff --git a/contrib/dblink/doc/execute b/contrib/dblink/doc/execute deleted file mode 100755 index 021e01ef85a1..000000000000 --- a/contrib/dblink/doc/execute +++ /dev/null @@ -1,80 +0,0 @@ -$PostgreSQL: pgsql/contrib/dblink/doc/execute,v 1.4 2006/03/11 04:38:29 momjian Exp $ -================================================================== -Name - -dblink_exec -- Executes an UPDATE/INSERT/DELETE on a remote database - -Synopsis - -dblink_exec(text connstr, text sql [, bool fail_on_error]) -dblink_exec(text connname, text sql [, bool fail_on_error]) -dblink_exec(text sql [, bool fail_on_error]) - -Inputs - - connname - connstr - - If two arguments are present, the first is first assumed to be a specific - connection name to use. If the name is not found, the argument is then - assumed to be a valid connection string, of standard libpq format, - e.g.: "hostaddr=127.0.0.1 dbname=mydb user=postgres password=mypasswd" - - If only one argument is used, then the unnamed connection is used. - - sql - - sql statement that you wish to execute on the remote host, e.g.: - insert into foo values(0,'a','{"a0","b0","c0"}'); - - fail_on_error - - If true (default when not present) then an ERROR thrown on the remote side - of the connection causes an ERROR to also be thrown locally. If false, the - remote ERROR is locally treated as a NOTICE, and the return value is set - to 'ERROR'. - -Outputs - - Returns status of the command, or 'ERROR' if the command failed. - -Notes - 1) dblink_open starts an explicit transaction. If, after using dblink_open, - you use dblink_exec to change data, and then an error occurs or you use - dblink_disconnect without a dblink_close first, your change *will* be - lost. - -Example usage - -select dblink_connect('dbname=dblink_test_slave'); - dblink_connect ----------------- - OK -(1 row) - -select dblink_exec('insert into foo values(21,''z'',''{"a0","b0","c0"}'');'); - dblink_exec ------------------ - INSERT 943366 1 -(1 row) - -select dblink_connect('myconn','dbname=regression'); - dblink_connect ----------------- - OK -(1 row) - -select dblink_exec('myconn','insert into foo values(21,''z'',''{"a0","b0","c0"}'');'); - dblink_exec ------------------- - INSERT 6432584 1 -(1 row) - -select dblink_exec('myconn','insert into pg_class values (''foo'')',false); -NOTICE: sql error -DETAIL: ERROR: null value in column "relnamespace" violates not-null constraint - - dblink_exec -------------- - ERROR -(1 row) diff --git a/contrib/dblink/doc/misc b/contrib/dblink/doc/misc deleted file mode 100755 index 3834afd8724b..000000000000 --- a/contrib/dblink/doc/misc +++ /dev/null @@ -1,232 +0,0 @@ -$PostgreSQL: pgsql/contrib/dblink/doc/misc,v 1.4 2006/09/02 21:11:15 joe Exp $ -================================================================== -Name - -dblink_current_query -- returns the current query string - -Synopsis - -dblink_current_query () RETURNS text - -Inputs - - None - -Outputs - - Returns text -- a copy of the currently executing query - -Example usage - -test=# select dblink_current_query() from (select dblink('dbname=postgres','select oid, proname from pg_proc where proname = ''byteacat''') as f1) as t1; - dblink_current_query ------------------------------------------------------------------------------------------------------------------------------------------------------ - select dblink_current_query() from (select dblink('dbname=postgres','select oid, proname from pg_proc where proname = ''byteacat''') as f1) as t1; -(1 row) - -================================================================== -Name - -dblink_get_pkey -- returns the position and field names of a relation's - primary key fields - -Synopsis - -dblink_get_pkey(text relname) RETURNS setof dblink_pkey_results - -Inputs - - relname - - any relation name; - e.g. 'foobar' - -Outputs - - Returns setof dblink_pkey_results -- one row for each primary key field, - in order of position in the key. dblink_pkey_results is defined as follows: - CREATE TYPE dblink_pkey_results AS (position int4, colname text); - -Example usage - -test=# select * from dblink_get_pkey('foobar'); - position | colname -----------+--------- - 1 | f1 - 2 | f2 - 3 | f3 - 4 | f4 - 5 | f5 - -================================================================== -Name - -dblink_build_sql_insert -- builds an insert statement using a local - tuple, replacing the selection key field - values with alternate supplied values -dblink_build_sql_delete -- builds a delete statement using supplied - values for selection key field values -dblink_build_sql_update -- builds an update statement using a local - tuple, replacing the selection key field - values with alternate supplied values - - -Synopsis - -dblink_build_sql_insert(text relname - ,int2vector primary_key_attnums - ,int2 num_primary_key_atts - ,_text src_pk_att_vals_array - ,_text tgt_pk_att_vals_array) RETURNS text -dblink_build_sql_delete(text relname - ,int2vector primary_key_attnums - ,int2 num_primary_key_atts - ,_text tgt_pk_att_vals_array) RETURNS text -dblink_build_sql_update(text relname - ,int2vector primary_key_attnums - ,int2 num_primary_key_atts - ,_text src_pk_att_vals_array - ,_text tgt_pk_att_vals_array) RETURNS text - -Inputs - - relname - - any relation name; - e.g. 'foobar' - - primary_key_attnums - - vector of primary key attnums (1 based, see pg_index.indkey); - e.g. '1 2' - - num_primary_key_atts - - number of primary key attnums in the vector; e.g. 2 - - src_pk_att_vals_array - - array of primary key values, used to look up the local matching - tuple, the values of which are then used to construct the SQL - statement - - tgt_pk_att_vals_array - - array of primary key values, used to replace the local tuple - values in the SQL statement - -Outputs - - Returns text -- requested SQL statement - -Example usage - -test=# select dblink_build_sql_insert('foo','1 2',2,'{"1", "a"}','{"1", "b''a"}'); - dblink_build_sql_insert --------------------------------------------------- - INSERT INTO foo(f1,f2,f3) VALUES('1','b''a','1') -(1 row) - -test=# select dblink_build_sql_delete('MyFoo','1 2',2,'{"1", "b"}'); - dblink_build_sql_delete ---------------------------------------------- - DELETE FROM "MyFoo" WHERE f1='1' AND f2='b' -(1 row) - -test=# select dblink_build_sql_update('foo','1 2',2,'{"1", "a"}','{"1", "b"}'); - dblink_build_sql_update -------------------------------------------------------------- - UPDATE foo SET f1='1',f2='b',f3='1' WHERE f1='1' AND f2='b' -(1 row) - - -================================================================== -Name - -dblink_get_connections -- returns a text array of all active named - dblink connections - -Synopsis - -dblink_get_connections() RETURNS text[] - -Inputs - - none - -Outputs - - Returns text array of all active named dblink connections - -Example usage - - SELECT dblink_get_connections(); - -================================================================== -Name - -dblink_is_busy -- checks to see if named connection is busy - with an async query - -Synopsis - -dblink_is_busy(text connname) RETURNS int - -Inputs - - connname - The specific connection name to use. - -Outputs - - Returns 1 if connection is busy, 0 if it is not busy. - If this function returns 0, it is guaranteed that dblink_get_result - will not block. - -Example usage - - SELECT dblink_is_busy('dtest1'); - -================================================================== -Name - -dblink_cancel_query -- cancels any active query on the named connection - -Synopsis - -dblink_cancel_query(text connname) RETURNS text - -Inputs - - connname - The specific connection name to use. - -Outputs - - Returns "OK" on success, or an error message on failure. - -Example usage - - SELECT dblink_cancel_query('dtest1'); - -================================================================== -Name - -dblink_error_message -- gets last error message on the named connection - -Synopsis - -dblink_error_message(text connname) RETURNS text - -Inputs - - connname - The specific connection name to use. - -Outputs - - Returns last error message. - -Example usage - - SELECT dblink_error_message('dtest1'); diff --git a/contrib/dblink/doc/query b/contrib/dblink/doc/query deleted file mode 100755 index 42427b5d5c36..000000000000 --- a/contrib/dblink/doc/query +++ /dev/null @@ -1,242 +0,0 @@ -================================================================== -Name - -dblink -- Returns a set from a remote database - -Synopsis - -dblink(text connstr, text sql [, bool fail_on_error]) -dblink(text connname, text sql [, bool fail_on_error]) -dblink(text sql [, bool fail_on_error]) - -Inputs - - connname - connstr - If two arguments are present, the first is first assumed to be a specific - connection name to use. If the name is not found, the argument is then - assumed to be a valid connection string, of standard libpq format, - e.g.: "hostaddr=127.0.0.1 dbname=mydb user=postgres password=mypasswd" - - If only one argument is used, then the unnamed connection is used. - - sql - - sql statement that you wish to execute on the remote host - e.g. "select * from pg_class" - - fail_on_error - - If true (default when not present) then an ERROR thrown on the remote side - of the connection causes an ERROR to also be thrown locally. If false, the - remote ERROR is locally treated as a NOTICE, and no rows are returned. - -Outputs - - Returns setof record - -Example usage - -select * from dblink('dbname=postgres','select proname, prosrc from pg_proc') - as t1(proname name, prosrc text) where proname like 'bytea%'; - proname | prosrc -------------+------------ - byteacat | byteacat - byteaeq | byteaeq - bytealt | bytealt - byteale | byteale - byteagt | byteagt - byteage | byteage - byteane | byteane - byteacmp | byteacmp - bytealike | bytealike - byteanlike | byteanlike - byteain | byteain - byteaout | byteaout -(12 rows) - -select dblink_connect('dbname=postgres'); - dblink_connect ----------------- - OK -(1 row) - -select * from dblink('select proname, prosrc from pg_proc') - as t1(proname name, prosrc text) where proname like 'bytea%'; - proname | prosrc -------------+------------ - byteacat | byteacat - byteaeq | byteaeq - bytealt | bytealt - byteale | byteale - byteagt | byteagt - byteage | byteage - byteane | byteane - byteacmp | byteacmp - bytealike | bytealike - byteanlike | byteanlike - byteain | byteain - byteaout | byteaout -(12 rows) - -select dblink_connect('myconn','dbname=regression'); - dblink_connect ----------------- - OK -(1 row) - -select * from dblink('myconn','select proname, prosrc from pg_proc') - as t1(proname name, prosrc text) where proname like 'bytea%'; - proname | prosrc -------------+------------ - bytearecv | bytearecv - byteasend | byteasend - byteale | byteale - byteagt | byteagt - byteage | byteage - byteane | byteane - byteacmp | byteacmp - bytealike | bytealike - byteanlike | byteanlike - byteacat | byteacat - byteaeq | byteaeq - bytealt | bytealt - byteain | byteain - byteaout | byteaout -(14 rows) - - -================================================================== -A more convenient way to use dblink may be to create a view: - - create view myremote_pg_proc as - select * - from dblink('dbname=postgres','select proname, prosrc from pg_proc') - as t1(proname name, prosrc text); - -Then you can simply write: - - select * from myremote_pg_proc where proname like 'bytea%'; - - -================================================================== -Name - -dblink_send_query -- Sends an async query to a remote database - -Synopsis - -dblink_send_query(text connname, text sql) - -Inputs - - connname - The specific connection name to use. - - sql - - sql statement that you wish to execute on the remote host - e.g. "select * from pg_class" - -Outputs - - Returns int. A return value of 1 if the query was successfully dispatched, - 0 otherwise. If 1, results must be fetched by dblink_get_result(connname). - A running query may be cancelled by dblink_cancel_query(connname). - -Example usage - - SELECT dblink_connect('dtest1', 'dbname=contrib_regression'); - SELECT * from - dblink_send_query('dtest1', 'select * from foo where f1 < 3') as t1; - -================================================================== -Name - -dblink_get_result -- Gets an async query result - -Synopsis - -dblink_get_result(text connname [, bool fail_on_error]) - -Inputs - - connname - The specific connection name to use. An asynchronous query must - have already been sent using dblink_send_query() - - fail_on_error - - If true (default when not present) then an ERROR thrown on the remote side - of the connection causes an ERROR to also be thrown locally. If false, the - remote ERROR is locally treated as a NOTICE, and no rows are returned. - -Outputs - - Returns setof record - -Notes - Blocks until a result gets available. - - This function *must* be called if dblink_send_query returned - a 1, even on cancelled queries - otherwise the connection - can't be used anymore. It must be called once for each query - sent, and one additional time to obtain an empty set result, - prior to using the connection again. - -Example usage - -contrib_regression=# SELECT dblink_connect('dtest1', 'dbname=contrib_regression'); - dblink_connect ----------------- - OK -(1 row) - -contrib_regression=# SELECT * from -contrib_regression-# dblink_send_query('dtest1', 'select * from foo where f1 < 3') as t1; - t1 ----- - 1 -(1 row) - -contrib_regression=# SELECT * from dblink_get_result('dtest1') as t1(f1 int, f2 text, f3 text[]); - f1 | f2 | f3 -----+----+------------ - 0 | a | {a0,b0,c0} - 1 | b | {a1,b1,c1} - 2 | c | {a2,b2,c2} -(3 rows) - -contrib_regression=# SELECT * from dblink_get_result('dtest1') as t1(f1 int, f2 text, f3 text[]); - f1 | f2 | f3 -----+----+---- -(0 rows) - -contrib_regression=# SELECT * from - dblink_send_query('dtest1', 'select * from foo where f1 < 3; select * from foo where f1 > 6') as t1; - t1 ----- - 1 -(1 row) - -contrib_regression=# SELECT * from dblink_get_result('dtest1') as t1(f1 int, f2 text, f3 text[]); - f1 | f2 | f3 -----+----+------------ - 0 | a | {a0,b0,c0} - 1 | b | {a1,b1,c1} - 2 | c | {a2,b2,c2} -(3 rows) - -contrib_regression=# SELECT * from dblink_get_result('dtest1') as t1(f1 int, f2 text, f3 text[]); - f1 | f2 | f3 -----+----+--------------- - 7 | h | {a7,b7,c7} - 8 | i | {a8,b8,c8} - 9 | j | {a9,b9,c9} - 10 | k | {a10,b10,c10} -(4 rows) - -contrib_regression=# SELECT * from dblink_get_result('dtest1') as t1(f1 int, f2 text, f3 text[]); - f1 | f2 | f3 -----+----+---- -(0 rows) diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out index fd35d76af967..f5503ce692c3 100755 --- a/contrib/dblink/expected/dblink.out +++ b/contrib/dblink/expected/dblink.out @@ -46,6 +46,9 @@ SELECT dblink_build_sql_insert('foo','1 2',2,'{"0", "a"}','{"99", "xyz"}'); INSERT INTO foo(f1,f2,f3) VALUES('99','xyz','{a0,b0,c0}') (1 row) +-- too many pk fields, should fail +SELECT dblink_build_sql_insert('foo','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}','{"99", "xyz", "{za0,zb0,zc0}"}'); +ERROR: invalid attribute number 4 -- build an update statement based on a local tuple, -- replacing the primary key values with new ones SELECT dblink_build_sql_update('foo','1 2',2,'{"0", "a"}','{"99", "xyz"}'); @@ -54,6 +57,9 @@ SELECT dblink_build_sql_update('foo','1 2',2,'{"0", "a"}','{"99", "xyz"}'); UPDATE foo SET f1 = '99', f2 = 'xyz', f3 = '{a0,b0,c0}' WHERE f1 = '99' AND f2 = 'xyz' (1 row) +-- too many pk fields, should fail +SELECT dblink_build_sql_update('foo','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}','{"99", "xyz", "{za0,zb0,zc0}"}'); +ERROR: invalid attribute number 4 -- build a delete statement based on a local tuple, SELECT dblink_build_sql_delete('foo','1 2',2,'{"0", "a"}'); dblink_build_sql_delete @@ -61,6 +67,9 @@ SELECT dblink_build_sql_delete('foo','1 2',2,'{"0", "a"}'); DELETE FROM foo WHERE f1 = '0' AND f2 = 'a' (1 row) +-- too many pk fields, should fail +SELECT dblink_build_sql_delete('foo','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}'); +ERROR: invalid attribute number 4 -- retest using a quoted and schema qualified table CREATE SCHEMA "MySchema"; CREATE TABLE "MySchema"."Foo"(f1 int, f2 text, f3 text[], primary key (f1,f2)); @@ -795,3 +804,40 @@ SELECT dblink_disconnect('dtest1'); OK (1 row) +-- test dropped columns in dblink_build_sql_insert, dblink_build_sql_update +CREATE TEMP TABLE test_dropped +( + col1 INT NOT NULL DEFAULT 111, + id SERIAL PRIMARY KEY, + col2 INT NOT NULL DEFAULT 112, + col2b INT NOT NULL DEFAULT 113 +); +NOTICE: CREATE TABLE will create implicit sequence "test_dropped_id_seq" for serial column "test_dropped.id" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "test_dropped_pkey" for table "test_dropped" +INSERT INTO test_dropped VALUES(default); +ALTER TABLE test_dropped + DROP COLUMN col1, + DROP COLUMN col2, + ADD COLUMN col3 VARCHAR(10) NOT NULL DEFAULT 'foo', + ADD COLUMN col4 INT NOT NULL DEFAULT 42; +SELECT dblink_build_sql_insert('test_dropped', '2', 1, + ARRAY['1'::TEXT], ARRAY['2'::TEXT]); + dblink_build_sql_insert +--------------------------------------------------------------------------- + INSERT INTO test_dropped(id,col2b,col3,col4) VALUES('2','113','foo','42') +(1 row) + +SELECT dblink_build_sql_update('test_dropped', '2', 1, + ARRAY['1'::TEXT], ARRAY['2'::TEXT]); + dblink_build_sql_update +------------------------------------------------------------------------------------------- + UPDATE test_dropped SET id = '2', col2b = '113', col3 = 'foo', col4 = '42' WHERE id = '2' +(1 row) + +SELECT dblink_build_sql_delete('test_dropped', '2', 1, + ARRAY['2'::TEXT]); + dblink_build_sql_delete +----------------------------------------- + DELETE FROM test_dropped WHERE id = '2' +(1 row) + diff --git a/contrib/dblink/sql/dblink.sql b/contrib/dblink/sql/dblink.sql index 7e91e9c6db4e..5aa0c33202e6 100644 --- a/contrib/dblink/sql/dblink.sql +++ b/contrib/dblink/sql/dblink.sql @@ -34,13 +34,19 @@ FROM dblink_get_pkey('foo'); -- build an insert statement based on a local tuple, -- replacing the primary key values with new ones SELECT dblink_build_sql_insert('foo','1 2',2,'{"0", "a"}','{"99", "xyz"}'); +-- too many pk fields, should fail +SELECT dblink_build_sql_insert('foo','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}','{"99", "xyz", "{za0,zb0,zc0}"}'); -- build an update statement based on a local tuple, -- replacing the primary key values with new ones SELECT dblink_build_sql_update('foo','1 2',2,'{"0", "a"}','{"99", "xyz"}'); +-- too many pk fields, should fail +SELECT dblink_build_sql_update('foo','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}','{"99", "xyz", "{za0,zb0,zc0}"}'); -- build a delete statement based on a local tuple, SELECT dblink_build_sql_delete('foo','1 2',2,'{"0", "a"}'); +-- too many pk fields, should fail +SELECT dblink_build_sql_delete('foo','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}'); -- retest using a quoted and schema qualified table CREATE SCHEMA "MySchema"; @@ -364,3 +370,29 @@ SELECT * from SELECT dblink_cancel_query('dtest1'); SELECT dblink_error_message('dtest1'); SELECT dblink_disconnect('dtest1'); + +-- test dropped columns in dblink_build_sql_insert, dblink_build_sql_update +CREATE TEMP TABLE test_dropped +( + col1 INT NOT NULL DEFAULT 111, + id SERIAL PRIMARY KEY, + col2 INT NOT NULL DEFAULT 112, + col2b INT NOT NULL DEFAULT 113 +); + +INSERT INTO test_dropped VALUES(default); + +ALTER TABLE test_dropped + DROP COLUMN col1, + DROP COLUMN col2, + ADD COLUMN col3 VARCHAR(10) NOT NULL DEFAULT 'foo', + ADD COLUMN col4 INT NOT NULL DEFAULT 42; + +SELECT dblink_build_sql_insert('test_dropped', '2', 1, + ARRAY['1'::TEXT], ARRAY['2'::TEXT]); + +SELECT dblink_build_sql_update('test_dropped', '2', 1, + ARRAY['1'::TEXT], ARRAY['2'::TEXT]); + +SELECT dblink_build_sql_delete('test_dropped', '2', 1, + ARRAY['2'::TEXT]); diff --git a/contrib/dblink/uninstall_dblink.sql b/contrib/dblink/uninstall_dblink.sql index da5116e7f184..41b0373ec23f 100644 --- a/contrib/dblink/uninstall_dblink.sql +++ b/contrib/dblink/uninstall_dblink.sql @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/contrib/dblink/uninstall_dblink.sql,v 1.5 2007/11/13 04:24:27 momjian Exp $ */ +/* $PostgreSQL: pgsql/contrib/dblink/uninstall_dblink.sql,v 1.5.2.1 2010/06/07 15:15:03 teodor Exp $ */ -- Adjust this setting to control where the objects get dropped. SET search_path = public; @@ -63,6 +63,10 @@ DROP FUNCTION dblink_connect (text, text); DROP FUNCTION dblink_connect (text); +DROP FUNCTION dblink_connect_u (text, text); + +DROP FUNCTION dblink_connect_u (text); + DROP FUNCTION dblink_cancel_query(text); DROP FUNCTION dblink_error_message(text); diff --git a/contrib/dict_int/.gitignore b/contrib/dict_int/.gitignore new file mode 100644 index 000000000000..932dda6d8419 --- /dev/null +++ b/contrib/dict_int/.gitignore @@ -0,0 +1,3 @@ +/dict_int.sql +# Generated subdirectories +/results/ diff --git a/contrib/dict_int/Makefile b/contrib/dict_int/Makefile new file mode 100644 index 000000000000..adf7b685b571 --- /dev/null +++ b/contrib/dict_int/Makefile @@ -0,0 +1,18 @@ +# $PostgreSQL: pgsql/contrib/dict_int/Makefile,v 1.2 2007/12/02 21:15:38 tgl Exp $ + +MODULE_big = dict_int +OBJS = dict_int.o +DATA_built = dict_int.sql +DATA = uninstall_dict_int.sql +REGRESS = dict_int + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/dict_int +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/dict_int/dict_int.c b/contrib/dict_int/dict_int.c new file mode 100644 index 000000000000..d0941c66ad92 --- /dev/null +++ b/contrib/dict_int/dict_int.c @@ -0,0 +1,100 @@ +/*------------------------------------------------------------------------- + * + * dict_int.c + * Text search dictionary for integers + * + * Copyright (c) 2007-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/contrib/dict_int/dict_int.c,v 1.3 2008/01/01 20:31:21 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "commands/defrem.h" +#include "fmgr.h" +#include "tsearch/ts_public.h" + +PG_MODULE_MAGIC; + + +typedef struct +{ + int maxlen; + bool rejectlong; +} DictInt; + + +PG_FUNCTION_INFO_V1(dintdict_init); +Datum dintdict_init(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(dintdict_lexize); +Datum dintdict_lexize(PG_FUNCTION_ARGS); + +Datum +dintdict_init(PG_FUNCTION_ARGS) +{ + List *dictoptions = (List *) PG_GETARG_POINTER(0); + DictInt *d; + ListCell *l; + + d = (DictInt *) palloc0(sizeof(DictInt)); + d->maxlen = 6; + d->rejectlong = false; + + foreach(l, dictoptions) + { + DefElem *defel = (DefElem *) lfirst(l); + + if (pg_strcasecmp(defel->defname, "MAXLEN") == 0) + { + d->maxlen = atoi(defGetString(defel)); + } + else if (pg_strcasecmp(defel->defname, "REJECTLONG") == 0) + { + d->rejectlong = defGetBoolean(defel); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized intdict parameter: \"%s\"", + defel->defname))); + } + } + + PG_RETURN_POINTER(d); +} + +Datum +dintdict_lexize(PG_FUNCTION_ARGS) +{ + DictInt *d = (DictInt *) PG_GETARG_POINTER(0); + char *in = (char *) PG_GETARG_POINTER(1); + char *txt = pnstrdup(in, PG_GETARG_INT32(2)); + TSLexeme *res = palloc0(sizeof(TSLexeme) * 2); + + res[1].lexeme = NULL; + if (PG_GETARG_INT32(2) > d->maxlen) + { + if (d->rejectlong) + { + /* reject by returning void array */ + pfree(txt); + res[0].lexeme = NULL; + } + else + { + /* trim integer */ + txt[d->maxlen] = '\0'; + res[0].lexeme = txt; + } + } + else + { + res[0].lexeme = txt; + } + + PG_RETURN_POINTER(res); +} diff --git a/contrib/dict_int/dict_int.sql.in b/contrib/dict_int/dict_int.sql.in new file mode 100644 index 000000000000..5245349ae18d --- /dev/null +++ b/contrib/dict_int/dict_int.sql.in @@ -0,0 +1,25 @@ +/* $PostgreSQL: pgsql/contrib/dict_int/dict_int.sql.in,v 1.3 2007/11/13 04:24:27 momjian Exp $ */ + +-- Adjust this setting to control where the objects get created. +SET search_path = public; + +CREATE OR REPLACE FUNCTION dintdict_init(internal) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT; + +CREATE OR REPLACE FUNCTION dintdict_lexize(internal, internal, internal, internal) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT; + +CREATE TEXT SEARCH TEMPLATE intdict_template ( + LEXIZE = dintdict_lexize, + INIT = dintdict_init +); + +CREATE TEXT SEARCH DICTIONARY intdict ( + TEMPLATE = intdict_template +); + +COMMENT ON TEXT SEARCH DICTIONARY intdict IS 'dictionary for integers'; diff --git a/contrib/dict_int/expected/dict_int.out b/contrib/dict_int/expected/dict_int.out new file mode 100644 index 000000000000..7feb493e153f --- /dev/null +++ b/contrib/dict_int/expected/dict_int.out @@ -0,0 +1,308 @@ +-- +-- first, define the datatype. Turn off echoing so that expected file +-- does not depend on contents of this file. +-- +SET client_min_messages = warning; +\set ECHO none +RESET client_min_messages; +--lexize +select ts_lexize('intdict', '511673'); + ts_lexize +----------- + {511673} +(1 row) + +select ts_lexize('intdict', '129'); + ts_lexize +----------- + {129} +(1 row) + +select ts_lexize('intdict', '40865854'); + ts_lexize +----------- + {408658} +(1 row) + +select ts_lexize('intdict', '952'); + ts_lexize +----------- + {952} +(1 row) + +select ts_lexize('intdict', '654980341'); + ts_lexize +----------- + {654980} +(1 row) + +select ts_lexize('intdict', '09810106'); + ts_lexize +----------- + {098101} +(1 row) + +select ts_lexize('intdict', '14262713'); + ts_lexize +----------- + {142627} +(1 row) + +select ts_lexize('intdict', '6532082986'); + ts_lexize +----------- + {653208} +(1 row) + +select ts_lexize('intdict', '0150061'); + ts_lexize +----------- + {015006} +(1 row) + +select ts_lexize('intdict', '7778'); + ts_lexize +----------- + {7778} +(1 row) + +select ts_lexize('intdict', '9547'); + ts_lexize +----------- + {9547} +(1 row) + +select ts_lexize('intdict', '753395478'); + ts_lexize +----------- + {753395} +(1 row) + +select ts_lexize('intdict', '647652'); + ts_lexize +----------- + {647652} +(1 row) + +select ts_lexize('intdict', '6988655574'); + ts_lexize +----------- + {698865} +(1 row) + +select ts_lexize('intdict', '1279'); + ts_lexize +----------- + {1279} +(1 row) + +select ts_lexize('intdict', '1266645909'); + ts_lexize +----------- + {126664} +(1 row) + +select ts_lexize('intdict', '7594193969'); + ts_lexize +----------- + {759419} +(1 row) + +select ts_lexize('intdict', '16928207'); + ts_lexize +----------- + {169282} +(1 row) + +select ts_lexize('intdict', '196850350328'); + ts_lexize +----------- + {196850} +(1 row) + +select ts_lexize('intdict', '22026985592'); + ts_lexize +----------- + {220269} +(1 row) + +select ts_lexize('intdict', '2063765'); + ts_lexize +----------- + {206376} +(1 row) + +select ts_lexize('intdict', '242387310'); + ts_lexize +----------- + {242387} +(1 row) + +select ts_lexize('intdict', '93595'); + ts_lexize +----------- + {93595} +(1 row) + +select ts_lexize('intdict', '9374'); + ts_lexize +----------- + {9374} +(1 row) + +select ts_lexize('intdict', '996969'); + ts_lexize +----------- + {996969} +(1 row) + +select ts_lexize('intdict', '353595982'); + ts_lexize +----------- + {353595} +(1 row) + +select ts_lexize('intdict', '925860'); + ts_lexize +----------- + {925860} +(1 row) + +select ts_lexize('intdict', '11848378337'); + ts_lexize +----------- + {118483} +(1 row) + +select ts_lexize('intdict', '333'); + ts_lexize +----------- + {333} +(1 row) + +select ts_lexize('intdict', '799287416765'); + ts_lexize +----------- + {799287} +(1 row) + +select ts_lexize('intdict', '745939'); + ts_lexize +----------- + {745939} +(1 row) + +select ts_lexize('intdict', '67601305734'); + ts_lexize +----------- + {676013} +(1 row) + +select ts_lexize('intdict', '3361113'); + ts_lexize +----------- + {336111} +(1 row) + +select ts_lexize('intdict', '9033778607'); + ts_lexize +----------- + {903377} +(1 row) + +select ts_lexize('intdict', '7507648'); + ts_lexize +----------- + {750764} +(1 row) + +select ts_lexize('intdict', '1166'); + ts_lexize +----------- + {1166} +(1 row) + +select ts_lexize('intdict', '9360498'); + ts_lexize +----------- + {936049} +(1 row) + +select ts_lexize('intdict', '917795'); + ts_lexize +----------- + {917795} +(1 row) + +select ts_lexize('intdict', '9387894'); + ts_lexize +----------- + {938789} +(1 row) + +select ts_lexize('intdict', '42764329'); + ts_lexize +----------- + {427643} +(1 row) + +select ts_lexize('intdict', '564062'); + ts_lexize +----------- + {564062} +(1 row) + +select ts_lexize('intdict', '5413377'); + ts_lexize +----------- + {541337} +(1 row) + +select ts_lexize('intdict', '060965'); + ts_lexize +----------- + {060965} +(1 row) + +select ts_lexize('intdict', '08273593'); + ts_lexize +----------- + {082735} +(1 row) + +select ts_lexize('intdict', '593556010144'); + ts_lexize +----------- + {593556} +(1 row) + +select ts_lexize('intdict', '17988843352'); + ts_lexize +----------- + {179888} +(1 row) + +select ts_lexize('intdict', '252281774'); + ts_lexize +----------- + {252281} +(1 row) + +select ts_lexize('intdict', '313425'); + ts_lexize +----------- + {313425} +(1 row) + +select ts_lexize('intdict', '641439323669'); + ts_lexize +----------- + {641439} +(1 row) + +select ts_lexize('intdict', '314532610153'); + ts_lexize +----------- + {314532} +(1 row) + diff --git a/contrib/dict_int/sql/dict_int.sql b/contrib/dict_int/sql/dict_int.sql new file mode 100644 index 000000000000..3a335f8f3d03 --- /dev/null +++ b/contrib/dict_int/sql/dict_int.sql @@ -0,0 +1,61 @@ +-- +-- first, define the datatype. Turn off echoing so that expected file +-- does not depend on contents of this file. +-- +SET client_min_messages = warning; +\set ECHO none +\i dict_int.sql +\set ECHO all +RESET client_min_messages; + +--lexize +select ts_lexize('intdict', '511673'); +select ts_lexize('intdict', '129'); +select ts_lexize('intdict', '40865854'); +select ts_lexize('intdict', '952'); +select ts_lexize('intdict', '654980341'); +select ts_lexize('intdict', '09810106'); +select ts_lexize('intdict', '14262713'); +select ts_lexize('intdict', '6532082986'); +select ts_lexize('intdict', '0150061'); +select ts_lexize('intdict', '7778'); +select ts_lexize('intdict', '9547'); +select ts_lexize('intdict', '753395478'); +select ts_lexize('intdict', '647652'); +select ts_lexize('intdict', '6988655574'); +select ts_lexize('intdict', '1279'); +select ts_lexize('intdict', '1266645909'); +select ts_lexize('intdict', '7594193969'); +select ts_lexize('intdict', '16928207'); +select ts_lexize('intdict', '196850350328'); +select ts_lexize('intdict', '22026985592'); +select ts_lexize('intdict', '2063765'); +select ts_lexize('intdict', '242387310'); +select ts_lexize('intdict', '93595'); +select ts_lexize('intdict', '9374'); +select ts_lexize('intdict', '996969'); +select ts_lexize('intdict', '353595982'); +select ts_lexize('intdict', '925860'); +select ts_lexize('intdict', '11848378337'); +select ts_lexize('intdict', '333'); +select ts_lexize('intdict', '799287416765'); +select ts_lexize('intdict', '745939'); +select ts_lexize('intdict', '67601305734'); +select ts_lexize('intdict', '3361113'); +select ts_lexize('intdict', '9033778607'); +select ts_lexize('intdict', '7507648'); +select ts_lexize('intdict', '1166'); +select ts_lexize('intdict', '9360498'); +select ts_lexize('intdict', '917795'); +select ts_lexize('intdict', '9387894'); +select ts_lexize('intdict', '42764329'); +select ts_lexize('intdict', '564062'); +select ts_lexize('intdict', '5413377'); +select ts_lexize('intdict', '060965'); +select ts_lexize('intdict', '08273593'); +select ts_lexize('intdict', '593556010144'); +select ts_lexize('intdict', '17988843352'); +select ts_lexize('intdict', '252281774'); +select ts_lexize('intdict', '313425'); +select ts_lexize('intdict', '641439323669'); +select ts_lexize('intdict', '314532610153'); diff --git a/contrib/dict_int/uninstall_dict_int.sql b/contrib/dict_int/uninstall_dict_int.sql new file mode 100644 index 000000000000..d94343fd36d2 --- /dev/null +++ b/contrib/dict_int/uninstall_dict_int.sql @@ -0,0 +1,12 @@ +/* $PostgreSQL: pgsql/contrib/dict_int/uninstall_dict_int.sql,v 1.3 2007/11/13 04:24:27 momjian Exp $ */ + +-- Adjust this setting to control where the objects get dropped. +SET search_path = public; + +DROP TEXT SEARCH DICTIONARY intdict; + +DROP TEXT SEARCH TEMPLATE intdict_template; + +DROP FUNCTION dintdict_init(internal); + +DROP FUNCTION dintdict_lexize(internal,internal,internal,internal); diff --git a/contrib/dict_xsyn/.gitignore b/contrib/dict_xsyn/.gitignore new file mode 100644 index 000000000000..0ebd61caaff9 --- /dev/null +++ b/contrib/dict_xsyn/.gitignore @@ -0,0 +1,3 @@ +/dict_xsyn.sql +# Generated subdirectories +/results/ diff --git a/contrib/dict_xsyn/Makefile b/contrib/dict_xsyn/Makefile new file mode 100644 index 000000000000..2a879b7eff78 --- /dev/null +++ b/contrib/dict_xsyn/Makefile @@ -0,0 +1,19 @@ +# $PostgreSQL: pgsql/contrib/dict_xsyn/Makefile,v 1.3 2007/12/02 21:15:38 tgl Exp $ + +MODULE_big = dict_xsyn +OBJS = dict_xsyn.o +DATA_built = dict_xsyn.sql +DATA = uninstall_dict_xsyn.sql +DATA_TSEARCH = xsyn_sample.rules +REGRESS = dict_xsyn + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/dict_xsyn +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/dict_xsyn/dict_xsyn.c b/contrib/dict_xsyn/dict_xsyn.c new file mode 100644 index 000000000000..892983c48b17 --- /dev/null +++ b/contrib/dict_xsyn/dict_xsyn.c @@ -0,0 +1,237 @@ +/*------------------------------------------------------------------------- + * + * dict_xsyn.c + * Extended synonym dictionary + * + * Copyright (c) 2007-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/contrib/dict_xsyn/dict_xsyn.c,v 1.4.2.1 2008/06/18 20:55:49 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include + +#include "commands/defrem.h" +#include "fmgr.h" +#include "tsearch/ts_locale.h" +#include "tsearch/ts_utils.h" + +PG_MODULE_MAGIC; + +typedef struct +{ + char *key; /* Word */ + char *value; /* Unparsed list of synonyms, including the + * word itself */ +} Syn; + +typedef struct +{ + int len; + Syn *syn; + + bool keeporig; +} DictSyn; + + +PG_FUNCTION_INFO_V1(dxsyn_init); +Datum dxsyn_init(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(dxsyn_lexize); +Datum dxsyn_lexize(PG_FUNCTION_ARGS); + +static char * +find_word(char *in, char **end) +{ + char *start; + + *end = NULL; + while (*in && t_isspace(in)) + in += pg_mblen(in); + + if (!*in || *in == '#') + return NULL; + start = in; + + while (*in && !t_isspace(in)) + in += pg_mblen(in); + + *end = in; + + return start; +} + +static int +compare_syn(const void *a, const void *b) +{ + return strcmp(((Syn *) a)->key, ((Syn *) b)->key); +} + +static void +read_dictionary(DictSyn *d, char *filename) +{ + char *real_filename = get_tsearch_config_filename(filename, "rules"); + tsearch_readline_state trst; + char *line; + int cur = 0; + + if (!tsearch_readline_begin(&trst, real_filename)) + ereport(ERROR, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not open synonym file \"%s\": %m", + real_filename))); + + while ((line = tsearch_readline(&trst)) != NULL) + { + char *value; + char *key; + char *end = NULL; + + if (*line == '\0') + continue; + + value = lowerstr(line); + pfree(line); + + key = find_word(value, &end); + if (!key) + { + pfree(value); + continue; + } + + if (cur == d->len) + { + d->len = (d->len > 0) ? 2 * d->len : 16; + if (d->syn) + d->syn = (Syn *) repalloc(d->syn, sizeof(Syn) * d->len); + else + d->syn = (Syn *) palloc(sizeof(Syn) * d->len); + } + + d->syn[cur].key = pnstrdup(key, end - key); + d->syn[cur].value = value; + + cur++; + } + + tsearch_readline_end(&trst); + + d->len = cur; + if (cur > 1) + qsort(d->syn, d->len, sizeof(Syn), compare_syn); + + pfree(real_filename); +} + +Datum +dxsyn_init(PG_FUNCTION_ARGS) +{ + List *dictoptions = (List *) PG_GETARG_POINTER(0); + DictSyn *d; + ListCell *l; + + d = (DictSyn *) palloc0(sizeof(DictSyn)); + d->len = 0; + d->syn = NULL; + d->keeporig = true; + + foreach(l, dictoptions) + { + DefElem *defel = (DefElem *) lfirst(l); + + if (pg_strcasecmp(defel->defname, "KEEPORIG") == 0) + { + d->keeporig = defGetBoolean(defel); + } + else if (pg_strcasecmp(defel->defname, "RULES") == 0) + { + read_dictionary(d, defGetString(defel)); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized xsyn parameter: \"%s\"", + defel->defname))); + } + } + + PG_RETURN_POINTER(d); +} + +Datum +dxsyn_lexize(PG_FUNCTION_ARGS) +{ + DictSyn *d = (DictSyn *) PG_GETARG_POINTER(0); + char *in = (char *) PG_GETARG_POINTER(1); + int length = PG_GETARG_INT32(2); + Syn word; + Syn *found; + TSLexeme *res = NULL; + + if (!length || d->len == 0) + PG_RETURN_POINTER(NULL); + + /* Create search pattern */ + { + char *temp = pnstrdup(in, length); + + word.key = lowerstr(temp); + pfree(temp); + word.value = NULL; + } + + /* Look for matching syn */ + found = (Syn *) bsearch(&word, d->syn, d->len, sizeof(Syn), compare_syn); + pfree(word.key); + + if (!found) + PG_RETURN_POINTER(NULL); + + /* Parse string of synonyms and return array of words */ + { + char *value = pstrdup(found->value); + int value_length = strlen(value); + char *pos = value; + int nsyns = 0; + bool is_first = true; + + res = palloc(sizeof(TSLexeme)); + + while (pos < value + value_length) + { + char *end; + char *syn = find_word(pos, &end); + + if (!syn) + break; + *end = '\0'; + + res = repalloc(res, sizeof(TSLexeme) * (nsyns + 2)); + + /* first word is added to result only if KEEPORIG flag is set */ + if (d->keeporig || !is_first) + { + res[nsyns].lexeme = pstrdup(syn); + res[nsyns].nvariant = 0; + res[nsyns].flags = 0; + + nsyns++; + } + + is_first = false; + + pos = end + 1; + } + + res[nsyns].lexeme = NULL; + + pfree(value); + } + + PG_RETURN_POINTER(res); +} diff --git a/contrib/dict_xsyn/dict_xsyn.sql.in b/contrib/dict_xsyn/dict_xsyn.sql.in new file mode 100644 index 000000000000..ac014a757d1c --- /dev/null +++ b/contrib/dict_xsyn/dict_xsyn.sql.in @@ -0,0 +1,25 @@ +/* $PostgreSQL: pgsql/contrib/dict_xsyn/dict_xsyn.sql.in,v 1.3 2007/11/13 04:24:27 momjian Exp $ */ + +-- Adjust this setting to control where the objects get created. +SET search_path = public; + +CREATE OR REPLACE FUNCTION dxsyn_init(internal) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT; + +CREATE OR REPLACE FUNCTION dxsyn_lexize(internal, internal, internal, internal) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT; + +CREATE TEXT SEARCH TEMPLATE xsyn_template ( + LEXIZE = dxsyn_lexize, + INIT = dxsyn_init +); + +CREATE TEXT SEARCH DICTIONARY xsyn ( + TEMPLATE = xsyn_template +); + +COMMENT ON TEXT SEARCH DICTIONARY xsyn IS 'eXtended synonym dictionary'; diff --git a/contrib/dict_xsyn/expected/dict_xsyn.out b/contrib/dict_xsyn/expected/dict_xsyn.out new file mode 100644 index 000000000000..99071ea8c747 --- /dev/null +++ b/contrib/dict_xsyn/expected/dict_xsyn.out @@ -0,0 +1,22 @@ +-- +-- first, define the datatype. Turn off echoing so that expected file +-- does not depend on contents of this file. +-- +SET client_min_messages = warning; +\set ECHO none +RESET client_min_messages; +--configuration +ALTER TEXT SEARCH DICTIONARY xsyn (RULES='xsyn_sample', KEEPORIG=false); +--lexize +SELECT ts_lexize('xsyn', 'supernova'); + ts_lexize +---------------- + {sn,sne,1987a} +(1 row) + +SELECT ts_lexize('xsyn', 'grb'); + ts_lexize +----------- + +(1 row) + diff --git a/contrib/dict_xsyn/sql/dict_xsyn.sql b/contrib/dict_xsyn/sql/dict_xsyn.sql new file mode 100644 index 000000000000..17f6df9cf3d2 --- /dev/null +++ b/contrib/dict_xsyn/sql/dict_xsyn.sql @@ -0,0 +1,16 @@ +-- +-- first, define the datatype. Turn off echoing so that expected file +-- does not depend on contents of this file. +-- +SET client_min_messages = warning; +\set ECHO none +\i dict_xsyn.sql +\set ECHO all +RESET client_min_messages; + +--configuration +ALTER TEXT SEARCH DICTIONARY xsyn (RULES='xsyn_sample', KEEPORIG=false); + +--lexize +SELECT ts_lexize('xsyn', 'supernova'); +SELECT ts_lexize('xsyn', 'grb'); diff --git a/contrib/dict_xsyn/uninstall_dict_xsyn.sql b/contrib/dict_xsyn/uninstall_dict_xsyn.sql new file mode 100644 index 000000000000..844d2e999740 --- /dev/null +++ b/contrib/dict_xsyn/uninstall_dict_xsyn.sql @@ -0,0 +1,12 @@ +/* $PostgreSQL: pgsql/contrib/dict_xsyn/uninstall_dict_xsyn.sql,v 1.3 2007/11/13 04:24:27 momjian Exp $ */ + +-- Adjust this setting to control where the objects get dropped. +SET search_path = public; + +DROP TEXT SEARCH DICTIONARY xsyn; + +DROP TEXT SEARCH TEMPLATE xsyn_template; + +DROP FUNCTION dxsyn_init(internal); + +DROP FUNCTION dxsyn_lexize(internal,internal,internal,internal); diff --git a/contrib/dict_xsyn/xsyn_sample.rules b/contrib/dict_xsyn/xsyn_sample.rules new file mode 100644 index 000000000000..203bec793a18 --- /dev/null +++ b/contrib/dict_xsyn/xsyn_sample.rules @@ -0,0 +1,6 @@ +# Sample rules file for eXtended Synonym (xsyn) dictionary +# format is as follows: +# +# word synonym1 synonym2 ... +# +supernova sn sne 1987a diff --git a/contrib/earthdistance/.gitignore b/contrib/earthdistance/.gitignore new file mode 100644 index 000000000000..366a0a399e7c --- /dev/null +++ b/contrib/earthdistance/.gitignore @@ -0,0 +1,3 @@ +/earthdistance.sql +# Generated subdirectories +/results/ diff --git a/contrib/extprotocol/Makefile b/contrib/extprotocol/Makefile index 9d6e115111b6..8ac73895a436 100644 --- a/contrib/extprotocol/Makefile +++ b/contrib/extprotocol/Makefile @@ -5,7 +5,8 @@ PG_CPPFLAGS = -I$(libpq_srcdir) PG_LIBS = $(libpq_pgport) ifdef USE_PGXS -PGXS := $(shell pg_config --pgxs) +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) else subdir = contrib/extprotocol diff --git a/contrib/formatter/Makefile b/contrib/formatter/Makefile index 3cffcf7dd2f0..24e74f5e77de 100644 --- a/contrib/formatter/Makefile +++ b/contrib/formatter/Makefile @@ -5,7 +5,8 @@ PG_CPPFLAGS = -I$(libpq_srcdir) PG_LIBS = $(libpq_pgport) ifdef USE_PGXS -PGXS := $(shell pg_config --pgxs) +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) else subdir = contrib/formatter diff --git a/contrib/fuzzystrmatch/.gitignore b/contrib/fuzzystrmatch/.gitignore index 8006defef4e0..f4962c630bac 100644 --- a/contrib/fuzzystrmatch/.gitignore +++ b/contrib/fuzzystrmatch/.gitignore @@ -1 +1 @@ -fuzzystrmatch.sql +/fuzzystrmatch.sql diff --git a/contrib/fuzzystrmatch/Makefile b/contrib/fuzzystrmatch/Makefile index 53a2b4fdfc6a..61538ffb5e7f 100644 --- a/contrib/fuzzystrmatch/Makefile +++ b/contrib/fuzzystrmatch/Makefile @@ -2,9 +2,8 @@ MODULE_big = fuzzystrmatch OBJS = fuzzystrmatch.o dmetaphone.o - DATA_built = fuzzystrmatch.sql -DATA = uninstall_fuzzystrmatch.sql +DATA = uninstall_fuzzystrmatch.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/fuzzystrmatch/fuzzystrmatch.sql.in b/contrib/fuzzystrmatch/fuzzystrmatch.sql.in index 218274b2d91a..269d4e6737c8 100644 --- a/contrib/fuzzystrmatch/fuzzystrmatch.sql.in +++ b/contrib/fuzzystrmatch/fuzzystrmatch.sql.in @@ -1,7 +1,7 @@ /* contrib/fuzzystrmatch/fuzzystrmatch--1.0.sql */ SET search_path = public; -CREATE FUNCTION levenshtein (text,text) RETURNS int +CREATE OR REPLACE FUNCTION levenshtein (text,text) RETURNS int AS 'MODULE_PATHNAME','levenshtein' LANGUAGE C IMMUTABLE STRICT; @@ -17,26 +17,26 @@ CREATE FUNCTION levenshtein_less_equal (text,text,int,int,int,int) RETURNS int AS 'MODULE_PATHNAME','levenshtein_less_equal_with_costs' LANGUAGE C IMMUTABLE STRICT; -CREATE FUNCTION metaphone (text,int) RETURNS text +CREATE OR REPLACE FUNCTION metaphone (text,int) RETURNS text AS 'MODULE_PATHNAME','metaphone' LANGUAGE C IMMUTABLE STRICT; -CREATE FUNCTION soundex(text) RETURNS text +CREATE OR REPLACE FUNCTION soundex(text) RETURNS text AS 'MODULE_PATHNAME', 'soundex' LANGUAGE C IMMUTABLE STRICT; -CREATE FUNCTION text_soundex(text) RETURNS text +CREATE OR REPLACE FUNCTION text_soundex(text) RETURNS text AS 'MODULE_PATHNAME', 'soundex' LANGUAGE C IMMUTABLE STRICT; -CREATE FUNCTION difference(text,text) RETURNS int +CREATE OR REPLACE FUNCTION difference(text,text) RETURNS int AS 'MODULE_PATHNAME', 'difference' LANGUAGE C IMMUTABLE STRICT; -CREATE FUNCTION dmetaphone (text) RETURNS text +CREATE OR REPLACE FUNCTION dmetaphone (text) RETURNS text AS 'MODULE_PATHNAME', 'dmetaphone' LANGUAGE C IMMUTABLE STRICT; -CREATE FUNCTION dmetaphone_alt (text) RETURNS text +CREATE OR REPLACE FUNCTION dmetaphone_alt (text) RETURNS text AS 'MODULE_PATHNAME', 'dmetaphone_alt' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/gp_internal_tools/Makefile b/contrib/gp_internal_tools/Makefile index 2f6f1506d40f..bee0baa8167d 100755 --- a/contrib/gp_internal_tools/Makefile +++ b/contrib/gp_internal_tools/Makefile @@ -3,7 +3,8 @@ MODULES = gp_persistent_util gp_ao_co_diagnostics PG_CPPFLAGS = -I$(libpq_srcdir) ifdef USE_PGXS -PGXS := $(shell pg_config --pgxs) +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) else subdir = contrib/gp_diagnostic_libraries diff --git a/contrib/hstore/.gitignore b/contrib/hstore/.gitignore index acaeaa166cae..d7af95330c38 100644 --- a/contrib/hstore/.gitignore +++ b/contrib/hstore/.gitignore @@ -1 +1,3 @@ -hstore.sql +/hstore.sql +# Generated subdirectories +/results/ diff --git a/contrib/hstore/README.hstore b/contrib/hstore/README.hstore deleted file mode 100755 index d1b9d44d1c2a..000000000000 --- a/contrib/hstore/README.hstore +++ /dev/null @@ -1,190 +0,0 @@ -Hstore - contrib module for storing (key,value) pairs - -[Online version] (http://www.sai.msu.su/~megera/oddmuse/index.cgi?Hstore) - -Motivation - -Many attributes rarely searched, semistructural data, lazy DBA - -Authors - - * Oleg Bartunov , Moscow, Moscow University, Russia - * Teodor Sigaev , Moscow, Delta-Soft Ltd.,Russia - -LEGAL NOTICES: This module is released under BSD license (as PostgreSQL -itself) - -Operations - - * hstore -> text - get value , perl analogy $h{key} - -select 'a=>q, b=>g'->'a'; - ? ------- - q - - * hstore || hstore - concatenation, perl analogy %a=( %b, %c ); - -regression=# select 'a=>b'::hstore || 'c=>d'::hstore; - ?column? --------------------- - "a"=>"b", "c"=>"d" -(1 row) - -but, notice - -regression=# select 'a=>b'::hstore || 'a=>d'::hstore; - ?column? ----------- - "a"=>"d" -(1 row) - - * text => text - creates hstore type from two text strings - -select 'a'=>'b'; - ?column? ----------- - "a"=>"b" - - * hstore @> hstore - contains operation, check if left operand contains right. - -regression=# select 'a=>b, b=>1, c=>NULL'::hstore @> 'a=>c'; - ?column? ----------- - f -(1 row) - -regression=# select 'a=>b, b=>1, c=>NULL'::hstore @> 'b=>1'; - ?column? ----------- - t -(1 row) - - * hstore <@ hstore - contained operation, check if left operand is contained - in right - -(Before PostgreSQL 8.2, the containment operators @> and <@ were -respectively called @ and ~. These names are still available, but are -deprecated and will eventually be retired. Notice that the old names -are reversed from the convention formerly followed by the core geometric -datatypes!) - -Functions - - * akeys(hstore) - returns all keys from hstore as array - -regression=# select akeys('a=>1,b=>2'); - akeys -------- - {a,b} - - * skeys(hstore) - returns all keys from hstore as strings - -regression=# select skeys('a=>1,b=>2'); - skeys -------- - a - b - - * avals(hstore) - returns all values from hstore as array - -regression=# select avals('a=>1,b=>2'); - avals -------- - {1,2} - - * svals(hstore) - returns all values from hstore as strings - -regression=# select svals('a=>1,b=>2'); - svals -------- - 1 - 2 - - * delete (hstore,text) - delete (key,value) from hstore if key matches - argument. - -regression=# select delete('a=>1,b=>2','b'); - delete ----------- - "a"=>"1" - - * each(hstore) return (key, value) pairs - -regression=# select * from each('a=>1,b=>2'); - key | value ------+------- - a | 1 - b | 2 - - * exist (hstore,text) - returns 'true if key is exists in hstore and - false otherwise. - -regression=# select exist('a=>1','a'); - exist ----------- - t - - * defined (hstore,text) - returns true if key is exists in hstore and - its value is not NULL. - -regression=# select defined('a=>NULL','a'); - defined ---------- - f - -Indices - -Module provides index support for '@>' and '<@' operations. - -create index hidx on testhstore using gist(h); - -Note - -Use parenthesis in select below, because priority of 'is' is higher than that of '->' - -select id from entrants where (info->'education_period') is not null; - -Examples - - * add key - -update tt set h=h||'c=>3'; - - * delete key - -update tt set h=delete(h,'k1'); - - * Statistics - -hstore type, because of its intrinsic liberality, could contain a lot of -different keys. Checking for valid keys is the task of application. -Examples below demonstrate several techniques how to check keys statistics. - - o simple example - -select * from each('aaa=>bq, b=>NULL, ""=>1 '); - - o using table - -select (each(h)).key, (each(h)).value into stat from testhstore ; - - o online stat - -select key, count(*) from (select (each(h)).key from testhstore) as stat group by key order by count desc, key; - key | count ------------+------- - line | 883 - query | 207 - pos | 203 - node | 202 - space | 197 - status | 195 - public | 194 - title | 190 - org | 189 -................... - -In the current implementation, neither the key nor the value -string can exceed 65535 bytes in length; an error will be thrown if this -limit is exceeded. These maximum lengths may change in future releases. diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h index e8ea58b56727..bad65728eb77 100644 --- a/contrib/hstore/hstore.h +++ b/contrib/hstore/hstore.h @@ -5,6 +5,15 @@ #define __HSTORE_H__ #include "fmgr.h" +#include "postgres.h" + +#include "funcapi.h" +#include "access/gist.h" +#include "access/itup.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/builtins.h" +#include "storage/bufpage.h" typedef struct @@ -21,6 +30,11 @@ typedef struct #define HSTORE_MAX_KEY_LEN 65535 #define HSTORE_MAX_VALUE_LEN 65535 +/* these are determined by the sizes of the keylen and vallen fields */ +/* in struct HEntry and struct Pairs */ +#define HSTORE_MAX_KEY_LEN 65535 +#define HSTORE_MAX_VALUE_LEN 65535 + typedef struct { diff --git a/contrib/hstore/hstore.sql.in b/contrib/hstore/hstore.sql.in index 3fa8ad01841f..0a7be0b5bfc2 100644 --- a/contrib/hstore/hstore.sql.in +++ b/contrib/hstore/hstore.sql.in @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/contrib/hstore/hstore.sql.in,v 1.8 2007/11/13 04:24:28 momjian Exp $ */ +/* $PostgreSQL: pgsql/contrib/hstore/hstore.sql.in,v 1.8.2.1 2010/06/22 11:36:36 rhaas Exp $ */ -- Adjust this setting to control where the objects get created. SET search_path = public; @@ -127,6 +127,12 @@ CREATE OPERATOR ~ ( CREATE OR REPLACE FUNCTION tconvert(text,text) RETURNS hstore AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE; + +-- For forward compatibility with PostgreSQL >= 9.0 +CREATE OR REPLACE FUNCTION hstore(text,text) +RETURNS hstore +AS 'MODULE_PATHNAME', 'tconvert' LANGUAGE C IMMUTABLE; -- not STRICT CREATE OPERATOR => ( diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c index 0f6eac347c76..cfb70a7c106b 100644 --- a/contrib/hstore/hstore_gist.c +++ b/contrib/hstore/hstore_gist.c @@ -5,7 +5,6 @@ #include "access/gist.h" #include "access/itup.h" -#include "access/skey.h" #include "crc32.h" #include "hstore.h" diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c index 40e22f37dd65..0f994c49b01e 100644 --- a/contrib/hstore/hstore_op.c +++ b/contrib/hstore/hstore_op.c @@ -74,13 +74,15 @@ exists(PG_FUNCTION_ARGS) HStore *hs = PG_GETARG_HS(0); text *key = PG_GETARG_TEXT_P(1); HEntry *entry; + bool res; entry = findkey(hs, VARDATA(key), VARSIZE(key) - VARHDRSZ); + res = (entry != NULL) ? true : false; PG_FREE_IF_COPY(hs, 0); PG_FREE_IF_COPY(key, 1); - PG_RETURN_BOOL(entry); + PG_RETURN_BOOL(res); } PG_FUNCTION_INFO_V1(defined); diff --git a/contrib/hstore/uninstall_hstore.sql b/contrib/hstore/uninstall_hstore.sql index d9e08927a5ef..ae2676a1b057 100644 --- a/contrib/hstore/uninstall_hstore.sql +++ b/contrib/hstore/uninstall_hstore.sql @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/contrib/hstore/uninstall_hstore.sql,v 1.6 2007/11/13 04:24:28 momjian Exp $ */ +/* $PostgreSQL: pgsql/contrib/hstore/uninstall_hstore.sql,v 1.6.2.1 2010/06/22 11:36:36 rhaas Exp $ */ -- Adjust this setting to control where the objects get dropped. SET search_path = public; @@ -26,6 +26,7 @@ DROP FUNCTION hs_concat(hstore,hstore); DROP FUNCTION hs_contains(hstore,hstore); DROP FUNCTION hs_contained(hstore,hstore); DROP FUNCTION tconvert(text,text); +DROP FUNCTION hstore(text,text); DROP FUNCTION akeys(hstore); DROP FUNCTION avals(hstore); DROP FUNCTION skeys(hstore); diff --git a/contrib/indexscan/indexscan.c b/contrib/indexscan/indexscan.c index d4f3d49fe34f..7cc6b69f0e95 100644 --- a/contrib/indexscan/indexscan.c +++ b/contrib/indexscan/indexscan.c @@ -44,8 +44,6 @@ istatus_text(ItemId itemid) initStringInfo(&buf); - if (ItemIdDeleted(itemid)) - appendStringInfoString(&buf, "DELETED "); if (ItemIdIsNormal(itemid)) appendStringInfoString(&buf, "USED "); if (ItemIdIsDead(itemid)) diff --git a/contrib/intagg/.gitignore b/contrib/intagg/.gitignore new file mode 100644 index 000000000000..d8e2c4043111 --- /dev/null +++ b/contrib/intagg/.gitignore @@ -0,0 +1 @@ +/int_aggregate.sql diff --git a/contrib/intarray/_int_gin.c b/contrib/intarray/_int_gin.c index 5bbff5922b29..ecc10389201a 100644 --- a/contrib/intarray/_int_gin.c +++ b/contrib/intarray/_int_gin.c @@ -65,7 +65,7 @@ ginint4_queryextract(PG_FUNCTION_ARGS) } } - if (nentries == 0) + if (*nentries == 0) { switch (strategy) { diff --git a/contrib/isn/.gitignore b/contrib/isn/.gitignore new file mode 100644 index 000000000000..1df12e3b7534 --- /dev/null +++ b/contrib/isn/.gitignore @@ -0,0 +1 @@ +/isn.sql diff --git a/contrib/isn/Makefile b/contrib/isn/Makefile index bfc3a37de12e..8431921d83ee 100644 --- a/contrib/isn/Makefile +++ b/contrib/isn/Makefile @@ -1,12 +1,12 @@ -# $PostgreSQL: pgsql/contrib/isn/Makefile,v 1.1 2006/09/09 04:07:52 tgl Exp $ +# $PostgreSQL: pgsql/contrib/isn/Makefile,v 1.3 2007/11/10 23:59:51 momjian Exp $ MODULES = isn DATA_built = isn.sql DATA = uninstall_isn.sql -DOCS = README.isn ifdef USE_PGXS -PGXS = $(shell pg_config --pgxs) +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) else subdir = contrib/isn diff --git a/contrib/isn/README.isn b/contrib/isn/README.isn deleted file mode 100755 index 22154266f0ea..000000000000 --- a/contrib/isn/README.isn +++ /dev/null @@ -1,220 +0,0 @@ - --- EAN13 - UPC - ISBN (books) - ISMN (music) - ISSN (serials) -------------------------------------------------------------- - -Copyright Germán Méndez Bravo (Kronuz), 2004 - 2006 -This module is released under the same BSD license as the rest of PostgreSQL. - -The information to implement this module was collected through -several sites, including: - http://www.isbn-international.org/ - http://www.issn.org/ - http://www.ismn-international.org/ - http://www.wikipedia.org/ -the prefixes used for hyphenation where also compiled from: - http://www.gs1.org/productssolutions/idkeys/support/prefix_list.html - http://www.isbn-international.org/en/identifiers.html - http://www.ismn-international.org/ranges.html -Care was taken during the creation of the algorithms and they -were meticulously verified against the suggested algorithms -in the official ISBN, ISMN, ISSN User Manuals. - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -THIS MODULE IS PROVIDED "AS IS" AND WITHOUT ANY WARRANTY - OF ANY KIND, EXPRESS OR IMPLIED. -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - --- Content of the Module -------------------------------------------------- - -This directory contains definitions for a few PostgreSQL -data types, for the following international-standard namespaces: -EAN13, UPC, ISBN (books), ISMN (music), and ISSN (serials). This module -is inspired by Garrett A. Wollman's isbn_issn code. - -I wanted the database to fully validate numbers and also to use the -upcoming ISBN-13 and the EAN13 standards, as well as to have it -automatically doing hyphenations for ISBN numbers. - -This new module validates, and automatically adds the correct -hyphenations to the numbers. Also, it supports the new ISBN-13 -numbers to be used starting in January 2007. - -Premises: -1. ISBN13, ISMN13, ISSN13 numbers are all EAN13 numbers -2. EAN13 numbers aren't always ISBN13, ISMN13 or ISSN13 (some are) -3. some ISBN13 numbers can be displayed as ISBN -4. some ISMN13 numbers can be displayed as ISMN -5. some ISSN13 numbers can be displayed as ISSN -6. all UPC, ISBN, ISMN and ISSN can be represented as EAN13 numbers - -Note: All types are internally represented as 64 bit integers, - and internally all are consistently interchangeable. - -We have the following data types: - -+ EAN13 for European Article Numbers. - This type will always show the EAN13-display format. - Te output function for this is -> ean13_out() - -+ ISBN13 for International Standard Book Numbers to be displayed in - the new EAN13-display format. -+ ISMN13 for International Standard Music Numbers to be displayed in - the new EAN13-display format. -+ ISSN13 for International Standard Serial Numbers to be displayed - in the new EAN13-display format. - These types will always display the long version of the ISxN (EAN13) - The output function to do this is -> ean13_out() - * The need for these types is just for displaying in different - ways the same data: - ISBN13 is actually the same as ISBN, ISMN13=ISMN and ISSN13=ISSN. - -+ ISBN for International Standard Book Numbers to be displayed in - the current short-display format. -+ ISMN for International Standard Music Numbers to be displayed in - the current short-display format. -+ ISSN for International Standard Serial Numbers to be displayed - in the current short-display format. - These types will display the short version of the ISxN (ISxN 10) - whenever it's possible, and it will show ISxN 13 when it's - impossible to show the short version. - The output function to do this is -> isn_out() - -+ UPC for Universal Product Codes. - UPC numbers are a subset of the EAN13 numbers (they are basically - EAN13 without the first '0' digit.) - The output function to do this is also -> isn_out() - -We have the following input functions: -+ To take a string and return an EAN13 -> ean13_in() -+ To take a string and return valid ISBN or ISBN13 numbers -> isbn_in() -+ To take a string and return valid ISMN or ISMN13 numbers -> ismn_in() -+ To take a string and return valid ISSN or ISSN13 numbers -> issn_in() -+ To take a string and return an UPC codes -> upc_in() - -We are able to cast from: -+ ISBN13 -> EAN13 -+ ISMN13 -> EAN13 -+ ISSN13 -> EAN13 - -+ ISBN -> EAN13 -+ ISMN -> EAN13 -+ ISSN -> EAN13 -+ UPC -> EAN13 - -+ ISBN <-> ISBN13 -+ ISMN <-> ISMN13 -+ ISSN <-> ISSN13 - -We have two operator classes (for btree and for hash) so each data type -can be indexed for faster access. - -The C API is implemented as: -extern Datum isn_out(PG_FUNCTION_ARGS); -extern Datum ean13_out(PG_FUNCTION_ARGS); -extern Datum ean13_in(PG_FUNCTION_ARGS); -extern Datum isbn_in(PG_FUNCTION_ARGS); -extern Datum ismn_in(PG_FUNCTION_ARGS); -extern Datum issn_in(PG_FUNCTION_ARGS); -extern Datum upc_in(PG_FUNCTION_ARGS); - -On success: -+ isn_out() takes any of our types and returns a string containing - the shortes possible representation of the number. - -+ ean13_out() takes any of our types and returns the - EAN13 (long) representation of the number. - -+ ean13_in() takes a string and return a EAN13. Which, as stated in (2) - could or could not be any of our types, but it certainly is an EAN13 - number. Only if the string is a valid EAN13 number, otherwise it fails. - -+ isbn_in() takes a string and return an ISBN/ISBN13. Only if the string - is really a ISBN/ISBN13, otherwise it fails. - -+ ismn_in() takes a string and return an ISMN/ISMN13. Only if the string - is really a ISMN/ISMN13, otherwise it fails. - -+ issn_in() takes a string and return an ISSN/ISSN13. Only if the string - is really a ISSN/ISSN13, otherwise it fails. - -+ upc_in() takes a string and return an UPC. Only if the string is - really a UPC, otherwise it fails. - -(on failure, the functions 'ereport' the error) - --- Testing/Playing Functions -------------------------------------------------- -isn_weak(boolean) - Sets the weak input mode. -This function is intended for testing use only! -isn_weak() gets the current status of the weak mode. - -"Weak" mode is used to be able to insert "invalid" data to a table. -"Invalid" as in the check digit being wrong, not missing numbers. - -Why would you want to use the weak mode? well, it could be that -you have a huge collection of ISBN numbers, and that there are so many of -them that for weird reasons some have the wrong check digit (perhaps the -numbers where scanned from a printed list and the OCR got the numbers wrong, -perhaps the numbers were manually captured... who knows.) Anyway, the thing -is you might want to clean the mess up, but you still want to be able to have -all the numbers in your database and maybe use an external tool to access -the invalid numbers in the database so you can verify the information and -validate it more easily; as selecting all the invalid numbers in the table. - -When you insert invalid numbers in a table using the weak mode, the number -will be inserted with the corrected check digit, but it will be flagged -with an exclamation mark ('!') at the end (i.e. 0-11-000322-5!) - -You can also force the insertion of invalid numbers even not in the weak mode, -appending the '!' character at the end of the number. - -To work with invalid numbers, you can use two functions: - + make_valid(), which validates an invalid number (deleting the invalid flag) - + is_valid(), which checks for the invalid flag presence. - --- Examples of Use -------------------------------------------------- ---Using the types directly: - select isbn('978-0-393-04002-9'); - select isbn13('0901690546'); - select issn('1436-4522'); - ---Casting types: --- note that you can only cast from ean13 to other type when the casted --- number would be valid in the realm of the casted type; --- thus, the following will NOT work: select isbn(ean13('0220356483481')); --- but these will: - select upc(ean13('0220356483481')); - select ean13(upc('220356483481')); - ---Create a table with a single column to hold ISBN numbers: - create table test ( id isbn ); - insert into test values('9780393040029'); - ---Automatically calculating check digits (observe the '?'): - insert into test values('220500896?'); - insert into test values('978055215372?'); - - select issn('3251231?'); - select ismn('979047213542?'); - ---Using the weak mode: - select isn_weak(true); - insert into test values('978-0-11-000533-4'); - insert into test values('9780141219307'); - insert into test values('2-205-00876-X'); - select isn_weak(false); - - select id from test where not is_valid(id); - update test set id=make_valid(id) where id = '2-205-00876-X!'; - - select * from test; - - select isbn13(id) from test; - --- Contact -------------------------------------------------- -Please suggestions or bug reports to kronuz at users.sourceforge.net - -Last reviewed on August 23, 2006 by Kronuz. diff --git a/contrib/lo/.gitignore b/contrib/lo/.gitignore new file mode 100644 index 000000000000..979347bd008b --- /dev/null +++ b/contrib/lo/.gitignore @@ -0,0 +1 @@ +/lo.sql diff --git a/contrib/lo/uninstall_lo.sql b/contrib/lo/uninstall_lo.sql index de0281fabd18..7cbc796a3dec 100644 --- a/contrib/lo/uninstall_lo.sql +++ b/contrib/lo/uninstall_lo.sql @@ -1,11 +1,13 @@ +/* $PostgreSQL: pgsql/contrib/lo/uninstall_lo.sql,v 1.3 2007/11/13 04:24:28 momjian Exp $ */ + +-- Adjust this setting to control where the objects get dropped. +SET search_path = public; + -- -- This removes the LO type -- It's used just for development -- --- Adjust this setting to control where the objects get created. -SET search_path = public; - -- drop the type and associated functions DROP TYPE lo CASCADE; diff --git a/contrib/ltree/.gitignore b/contrib/ltree/.gitignore new file mode 100644 index 000000000000..49883e82a3ac --- /dev/null +++ b/contrib/ltree/.gitignore @@ -0,0 +1,3 @@ +/ltree.sql +# Generated subdirectories +/results/ diff --git a/contrib/ltree/uninstall_ltree.sql b/contrib/ltree/uninstall_ltree.sql index a409ea4fef8d..4d976839a4a5 100644 --- a/contrib/ltree/uninstall_ltree.sql +++ b/contrib/ltree/uninstall_ltree.sql @@ -1,3 +1,6 @@ +/* $PostgreSQL: pgsql/contrib/ltree/uninstall_ltree.sql,v 1.5 2007/11/13 04:24:28 momjian Exp $ */ + +-- Adjust this setting to control where the objects get dropped. SET search_path = public; DROP OPERATOR CLASS gist__ltree_ops USING gist; diff --git a/contrib/oid2name/.gitignore b/contrib/oid2name/.gitignore new file mode 100644 index 000000000000..fdefde108ddd --- /dev/null +++ b/contrib/oid2name/.gitignore @@ -0,0 +1 @@ +/oid2name diff --git a/contrib/pageinspect/.gitignore b/contrib/pageinspect/.gitignore new file mode 100644 index 000000000000..fad166aaee87 --- /dev/null +++ b/contrib/pageinspect/.gitignore @@ -0,0 +1 @@ +/pageinspect.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile new file mode 100644 index 000000000000..63da705215d5 --- /dev/null +++ b/contrib/pageinspect/Makefile @@ -0,0 +1,24 @@ +#------------------------------------------------------------------------- +# +# pageinspect Makefile +# +# $PostgreSQL: pgsql/contrib/pageinspect/Makefile,v 1.3 2007/11/10 23:59:51 momjian Exp $ +# +#------------------------------------------------------------------------- + +MODULE_big = pageinspect +OBJS = rawpage.o heapfuncs.o btreefuncs.o +DATA_built = pageinspect.sql +DATA = uninstall_pageinspect.sql + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/pageinspect +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c new file mode 100644 index 000000000000..81cfbf1c0f24 --- /dev/null +++ b/contrib/pageinspect/btreefuncs.c @@ -0,0 +1,500 @@ +/* + * btreefuncs.c + * + * Copyright (c) 2006 Satoshi Nagayasu + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose, without fee, and without a + * written agreement is hereby granted, provided that the above + * copyright notice and this paragraph and the following two + * paragraphs appear in all copies. + * + * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS + * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS + * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "postgres.h" + +#include "access/heapam.h" +#include "access/nbtree.h" +#include "catalog/namespace.h" +#include "catalog/pg_type.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "utils/builtins.h" + + +extern Datum bt_metap(PG_FUNCTION_ARGS); +extern Datum bt_page_items(PG_FUNCTION_ARGS); +extern Datum bt_page_stats(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(bt_metap); +PG_FUNCTION_INFO_V1(bt_page_items); +PG_FUNCTION_INFO_V1(bt_page_stats); + +#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX) +#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID) + +#define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \ + if ( !(FirstOffsetNumber <= (offnum) && \ + (offnum) <= PageGetMaxOffsetNumber(pg)) ) \ + elog(ERROR, "page offset number out of range"); } + +/* note: BlockNumber is unsigned, hence can't be negative */ +#define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \ + if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \ + elog(ERROR, "block number out of range"); } + +/* ------------------------------------------------ + * structure for single btree page statistics + * ------------------------------------------------ + */ +typedef struct BTPageStat +{ + uint32 blkno; + uint32 live_items; + uint32 dead_items; + uint32 page_size; + uint32 max_avail; + uint32 free_size; + uint32 avg_item_size; + char type; + + /* opaque data */ + BlockNumber btpo_prev; + BlockNumber btpo_next; + union + { + uint32 level; + TransactionId xact; + } btpo; + uint16 btpo_flags; + BTCycleId btpo_cycleid; +} BTPageStat; + + +/* ------------------------------------------------- + * GetBTPageStatistics() + * + * Collect statistics of single b-tree page + * ------------------------------------------------- + */ +static void +GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat * stat) +{ + Page page = BufferGetPage(buffer); + PageHeader phdr = (PageHeader) page; + OffsetNumber maxoff = PageGetMaxOffsetNumber(page); + BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page); + int item_size = 0; + int off; + + stat->blkno = blkno; + + stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData); + + stat->dead_items = stat->live_items = 0; + + stat->page_size = PageGetPageSize(page); + + /* page type (flags) */ + if (P_ISDELETED(opaque)) + { + stat->type = 'd'; + stat->btpo.xact = opaque->btpo.xact; + return; + } + else if (P_IGNORE(opaque)) + stat->type = 'e'; + else if (P_ISLEAF(opaque)) + stat->type = 'l'; + else if (P_ISROOT(opaque)) + stat->type = 'r'; + else + stat->type = 'i'; + + /* btpage opaque data */ + stat->btpo_prev = opaque->btpo_prev; + stat->btpo_next = opaque->btpo_next; + stat->btpo.level = opaque->btpo.level; + stat->btpo_flags = opaque->btpo_flags; + stat->btpo_cycleid = opaque->btpo_cycleid; + + /* count live and dead tuples, and free space */ + for (off = FirstOffsetNumber; off <= maxoff; off++) + { + IndexTuple itup; + + ItemId id = PageGetItemId(page, off); + + itup = (IndexTuple) PageGetItem(page, id); + + item_size += IndexTupleSize(itup); + + if (!ItemIdIsDead(id)) + stat->live_items++; + else + stat->dead_items++; + } + stat->free_size = PageGetFreeSpace(page); + + if ((stat->live_items + stat->dead_items) > 0) + stat->avg_item_size = item_size / (stat->live_items + stat->dead_items); + else + stat->avg_item_size = 0; +} + +/* ----------------------------------------------- + * bt_page_stats() + * + * Usage: SELECT * FROM bt_page_stats('t1_pkey', 1); + * ----------------------------------------------- + */ +Datum +bt_page_stats(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_P(0); + uint32 blkno = PG_GETARG_UINT32(1); + Buffer buffer; + Relation rel; + RangeVar *relrv; + Datum result; + HeapTuple tuple; + TupleDesc tupleDesc; + int j; + char *values[11]; + BTPageStat stat; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use pageinspect functions")))); + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + if (!IS_INDEX(rel) || !IS_BTREE(rel)) + elog(ERROR, "relation \"%s\" is not a btree index", + RelationGetRelationName(rel)); + + /* + * Reject attempts to read non-local temporary relations; we would + * be likely to get wrong data since we have no visibility into the + * owning session's local buffers. + */ + if (isOtherTempNamespace(RelationGetNamespace(rel))) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"))); + + if (blkno == 0) + elog(ERROR, "block 0 is a meta page"); + + CHECK_RELATION_BLOCK_RANGE(rel, blkno); + + buffer = ReadBuffer(rel, blkno); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + /* keep compiler quiet */ + stat.btpo_prev = stat.btpo_next = InvalidBlockNumber; + stat.btpo_flags = stat.free_size = stat.avg_item_size = 0; + + GetBTPageStatistics(blkno, buffer, &stat); + + UnlockReleaseBuffer(buffer); + relation_close(rel, AccessShareLock); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + j = 0; + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.blkno); + values[j] = palloc(32); + snprintf(values[j++], 32, "%c", stat.type); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.live_items); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.dead_items); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.avg_item_size); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.page_size); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.free_size); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.btpo_prev); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.btpo_next); + values[j] = palloc(32); + if (stat.type == 'd') + snprintf(values[j++], 32, "%d", stat.btpo.xact); + else + snprintf(values[j++], 32, "%d", stat.btpo.level); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", stat.btpo_flags); + + tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), + values); + + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} + +/*------------------------------------------------------- + * bt_page_items() + * + * Get IndexTupleData set in a btree page + * + * Usage: SELECT * FROM bt_page_items('t1_pkey', 1); + *------------------------------------------------------- + */ + +/* + * cross-call data structure for SRF + */ +struct user_args +{ + Page page; + OffsetNumber offset; +}; + +Datum +bt_page_items(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_P(0); + uint32 blkno = PG_GETARG_UINT32(1); + Datum result; + char *values[6]; + HeapTuple tuple; + FuncCallContext *fctx; + MemoryContext mctx; + struct user_args *uargs; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use pageinspect functions")))); + + if (SRF_IS_FIRSTCALL()) + { + RangeVar *relrv; + Relation rel; + Buffer buffer; + BTPageOpaque opaque; + TupleDesc tupleDesc; + + fctx = SRF_FIRSTCALL_INIT(); + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + if (!IS_INDEX(rel) || !IS_BTREE(rel)) + elog(ERROR, "relation \"%s\" is not a btree index", + RelationGetRelationName(rel)); + + /* + * Reject attempts to read non-local temporary relations; we would + * be likely to get wrong data since we have no visibility into the + * owning session's local buffers. + */ + if (isOtherTempNamespace(RelationGetNamespace(rel))) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"))); + + if (blkno == 0) + elog(ERROR, "block 0 is a meta page"); + + CHECK_RELATION_BLOCK_RANGE(rel, blkno); + + buffer = ReadBuffer(rel, blkno); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + /* + * We copy the page into local storage to avoid holding pin on the + * buffer longer than we must, and possibly failing to release it at + * all if the calling query doesn't fetch all rows. + */ + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + uargs = palloc(sizeof(struct user_args)); + + uargs->page = palloc(BLCKSZ); + memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ); + + UnlockReleaseBuffer(buffer); + relation_close(rel, AccessShareLock); + + uargs->offset = FirstOffsetNumber; + + opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); + + if (P_ISDELETED(opaque)) + elog(NOTICE, "page is deleted"); + + fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); + + fctx->user_fctx = uargs; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + uargs = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + ItemId id; + IndexTuple itup; + int j; + int off; + int dlen; + char *dump; + char *ptr; + + id = PageGetItemId(uargs->page, uargs->offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(uargs->page, id); + + j = 0; + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", uargs->offset); + values[j] = palloc(32); + snprintf(values[j++], 32, "(%u,%u)", + BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)), + itup->t_tid.ip_posid); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup)); + values[j] = palloc(32); + snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f'); + values[j] = palloc(32); + snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); + + ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); + dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); + dump = palloc0(dlen * 3 + 1); + values[j] = dump; + for (off = 0; off < dlen; off++) + { + if (off > 0) + *dump++ = ' '; + sprintf(dump, "%02x", *(ptr + off) & 0xff); + dump += 2; + } + + tuple = BuildTupleFromCStrings(fctx->attinmeta, values); + result = HeapTupleGetDatum(tuple); + + uargs->offset = uargs->offset + 1; + + SRF_RETURN_NEXT(fctx, result); + } + else + { + pfree(uargs->page); + pfree(uargs); + SRF_RETURN_DONE(fctx); + } +} + + +/* ------------------------------------------------ + * bt_metap() + * + * Get a btree's meta-page information + * + * Usage: SELECT * FROM bt_metap('t1_pkey') + * ------------------------------------------------ + */ +Datum +bt_metap(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_P(0); + Datum result; + Relation rel; + RangeVar *relrv; + BTMetaPageData *metad; + TupleDesc tupleDesc; + int j; + char *values[6]; + Buffer buffer; + Page page; + HeapTuple tuple; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use pageinspect functions")))); + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + if (!IS_INDEX(rel) || !IS_BTREE(rel)) + elog(ERROR, "relation \"%s\" is not a btree index", + RelationGetRelationName(rel)); + + /* + * Reject attempts to read non-local temporary relations; we would + * be likely to get wrong data since we have no visibility into the + * owning session's local buffers. + */ + if (isOtherTempNamespace(RelationGetNamespace(rel))) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"))); + + buffer = ReadBuffer(rel, 0); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + page = BufferGetPage(buffer); + metad = BTPageGetMeta(page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + j = 0; + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", metad->btm_magic); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", metad->btm_version); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", metad->btm_root); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", metad->btm_level); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", metad->btm_fastroot); + values[j] = palloc(32); + snprintf(values[j++], 32, "%d", metad->btm_fastlevel); + + tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), + values); + + result = HeapTupleGetDatum(tuple); + + UnlockReleaseBuffer(buffer); + relation_close(rel, AccessShareLock); + + PG_RETURN_DATUM(result); +} diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c new file mode 100644 index 000000000000..3b82389729be --- /dev/null +++ b/contrib/pageinspect/heapfuncs.c @@ -0,0 +1,232 @@ +/*------------------------------------------------------------------------- + * + * heapfuncs.c + * Functions to investigate heap pages + * + * We check the input to these functions for corrupt pointers etc. that + * might cause crashes, but at the same time we try to print out as much + * information as possible, even if it's nonsense. That's because if a + * page is corrupt, we don't know why and how exactly it is corrupt, so we + * let the user to judge it. + * + * These functions are restricted to superusers for the fear of introducing + * security holes if the input checking isn't as water-tight as it should. + * You'd need to be superuser to obtain a raw page image anyway, so + * there's hardly any use case for using these without superuser-rights + * anyway. + * + * Copyright (c) 2007-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/contrib/pageinspect/heapfuncs.c,v 1.4 2008/01/01 20:31:21 tgl Exp $ + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "funcapi.h" +#include "access/heapam.h" +#include "access/transam.h" +#include "catalog/namespace.h" +#include "catalog/pg_type.h" +#include "utils/builtins.h" +#include "miscadmin.h" + +Datum heap_page_items(PG_FUNCTION_ARGS); + +#define GET_TEXT(str_) \ + DirectFunctionCall1(textin, CStringGetDatum(str_)) + +/* + * bits_to_text + * + * Converts a bits8-array of 'len' bits to a human-readable + * c-string representation. + */ +static char * +bits_to_text(bits8 *bits, int len) +{ + int i; + char *str; + + str = palloc(len + 1); + + for (i = 0; i < len; i++) + str[i] = (bits[(i / 8)] & (1 << (i % 8))) ? '1' : '0'; + + str[i] = '\0'; + + return str; +} + + +/* + * heap_page_items + * + * Allows inspection of line pointers and tuple headers of a heap page. + */ +PG_FUNCTION_INFO_V1(heap_page_items); + +typedef struct heap_page_items_state +{ + TupleDesc tupd; + Page page; + uint16 offset; +} heap_page_items_state; + +Datum +heap_page_items(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + heap_page_items_state *inter_call_data = NULL; + FuncCallContext *fctx; + int raw_page_size; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + raw_page_size = VARSIZE(raw_page) - VARHDRSZ; + + if (SRF_IS_FIRSTCALL()) + { + TupleDesc tupdesc; + MemoryContext mctx; + + if (raw_page_size < SizeOfPageHeaderData) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page too small (%d bytes)", raw_page_size))); + + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + inter_call_data = palloc(sizeof(heap_page_items_state)); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + inter_call_data->tupd = tupdesc; + + inter_call_data->offset = FirstOffsetNumber; + inter_call_data->page = VARDATA(raw_page); + + fctx->max_calls = PageGetMaxOffsetNumber(inter_call_data->page); + fctx->user_fctx = inter_call_data; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + inter_call_data = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + Page page = inter_call_data->page; + HeapTuple resultTuple; + Datum result; + ItemId id; + Datum values[13]; + bool nulls[13]; + uint16 lp_offset; + uint16 lp_flags; + uint16 lp_len; + + memset(nulls, 0, sizeof(nulls)); + + /* Extract information from the line pointer */ + + id = PageGetItemId(page, inter_call_data->offset); + + lp_offset = ItemIdGetOffset(id); + lp_flags = ItemIdGetFlags(id); + lp_len = ItemIdGetLength(id); + + values[0] = UInt16GetDatum(inter_call_data->offset); + values[1] = UInt16GetDatum(lp_offset); + values[2] = UInt16GetDatum(lp_flags); + values[3] = UInt16GetDatum(lp_len); + + /* + * We do just enough validity checking to make sure we don't reference + * data outside the page passed to us. The page could be corrupt in + * many other ways, but at least we won't crash. + */ + if (ItemIdHasStorage(id) && + lp_len >= sizeof(HeapTupleHeader) && + lp_offset == MAXALIGN(lp_offset) && + lp_offset + lp_len <= raw_page_size) + { + HeapTupleHeader tuphdr; + int bits_len; + + /* Extract information from the tuple header */ + + tuphdr = (HeapTupleHeader) PageGetItem(page, id); + + values[4] = UInt32GetDatum(HeapTupleHeaderGetXmin(tuphdr)); + values[5] = UInt32GetDatum(HeapTupleHeaderGetXmax(tuphdr)); + values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr)); /* shared with xvac */ + values[7] = PointerGetDatum(&tuphdr->t_ctid); + values[8] = UInt16GetDatum(tuphdr->t_infomask2); + values[9] = UInt16GetDatum(tuphdr->t_infomask); + values[10] = UInt8GetDatum(tuphdr->t_hoff); + + /* + * We already checked that the item as is completely within the + * raw page passed to us, with the length given in the line + * pointer.. Let's check that t_hoff doesn't point over lp_len, + * before using it to access t_bits and oid. + */ + if (tuphdr->t_hoff >= sizeof(HeapTupleHeader) && + tuphdr->t_hoff <= lp_len) + { + if (tuphdr->t_infomask & HEAP_HASNULL) + { + bits_len = tuphdr->t_hoff - + (((char *) tuphdr->t_bits) -((char *) tuphdr)); + + values[11] = GET_TEXT( + bits_to_text(tuphdr->t_bits, bits_len * 8)); + } + else + nulls[11] = true; + + if (tuphdr->t_infomask & HEAP_HASOID) + values[12] = HeapTupleHeaderGetOid(tuphdr); + else + nulls[12] = true; + } + else + { + nulls[11] = true; + nulls[12] = true; + } + } + else + { + /* + * The line pointer is not used, or it's invalid. Set the rest of + * the fields to NULL + */ + int i; + + for (i = 4; i <= 12; i++) + nulls[i] = true; + } + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + inter_call_data->offset++; + + SRF_RETURN_NEXT(fctx, result); + } + else + SRF_RETURN_DONE(fctx); +} diff --git a/contrib/pageinspect/pageinspect.sql.in b/contrib/pageinspect/pageinspect.sql.in new file mode 100644 index 000000000000..1af59f70f46a --- /dev/null +++ b/contrib/pageinspect/pageinspect.sql.in @@ -0,0 +1,94 @@ +/* $PostgreSQL: pgsql/contrib/pageinspect/pageinspect.sql.in,v 1.4 2007/11/13 04:24:28 momjian Exp $ */ + +-- Adjust this setting to control where the objects get created. +SET search_path = public; + +-- +-- get_raw_page() +-- +CREATE OR REPLACE FUNCTION get_raw_page(text, int4) +RETURNS bytea +AS 'MODULE_PATHNAME', 'get_raw_page' +LANGUAGE C STRICT; + +-- +-- page_header() +-- +CREATE OR REPLACE FUNCTION page_header(IN page bytea, + OUT lsn text, + OUT tli smallint, + OUT flags smallint, + OUT lower smallint, + OUT upper smallint, + OUT special smallint, + OUT pagesize smallint, + OUT version smallint, + OUT prune_xid xid) +AS 'MODULE_PATHNAME', 'page_header' +LANGUAGE C STRICT; + +-- +-- heap_page_items() +-- +CREATE OR REPLACE FUNCTION heap_page_items(IN page bytea, + OUT lp smallint, + OUT lp_off smallint, + OUT lp_flags smallint, + OUT lp_len smallint, + OUT t_xmin xid, + OUT t_xmax xid, + OUT t_field3 int4, + OUT t_ctid tid, + OUT t_infomask2 smallint, + OUT t_infomask smallint, + OUT t_hoff smallint, + OUT t_bits text, + OUT t_oid oid) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'heap_page_items' +LANGUAGE C STRICT; + +-- +-- bt_metap() +-- +CREATE OR REPLACE FUNCTION bt_metap(IN relname text, + OUT magic int4, + OUT version int4, + OUT root int4, + OUT level int4, + OUT fastroot int4, + OUT fastlevel int4) +AS 'MODULE_PATHNAME', 'bt_metap' +LANGUAGE C STRICT; + +-- +-- bt_page_stats() +-- +CREATE OR REPLACE FUNCTION bt_page_stats(IN relname text, IN blkno int4, + OUT blkno int4, + OUT type "char", + OUT live_items int4, + OUT dead_items int4, + OUT avg_item_size int4, + OUT page_size int4, + OUT free_size int4, + OUT btpo_prev int4, + OUT btpo_next int4, + OUT btpo int4, + OUT btpo_flags int4) +AS 'MODULE_PATHNAME', 'bt_page_stats' +LANGUAGE C STRICT; + +-- +-- bt_page_items() +-- +CREATE OR REPLACE FUNCTION bt_page_items(IN relname text, IN blkno int4, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint, + OUT nulls bool, + OUT vars bool, + OUT data text) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'bt_page_items' +LANGUAGE C STRICT; diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c new file mode 100644 index 000000000000..75ab6fbfa32e --- /dev/null +++ b/contrib/pageinspect/rawpage.c @@ -0,0 +1,175 @@ +/*------------------------------------------------------------------------- + * + * rawpage.c + * Functions to extract a raw page as bytea and inspect it + * + * Access-method specific inspection functions are in separate files. + * + * Copyright (c) 2007-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/contrib/pageinspect/rawpage.c,v 1.4.2.1 2009/03/31 22:54:52 tgl Exp $ + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "funcapi.h" +#include "access/heapam.h" +#include "access/transam.h" +#include "catalog/namespace.h" +#include "catalog/pg_type.h" +#include "utils/builtins.h" +#include "miscadmin.h" + +PG_MODULE_MAGIC; + +Datum get_raw_page(PG_FUNCTION_ARGS); +Datum page_header(PG_FUNCTION_ARGS); + +/* + * get_raw_page + * + * Returns a copy of a page from shared buffers as a bytea + */ +PG_FUNCTION_INFO_V1(get_raw_page); + +Datum +get_raw_page(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_P(0); + uint32 blkno = PG_GETARG_UINT32(1); + + Relation rel; + RangeVar *relrv; + bytea *raw_page; + char *raw_page_data; + Buffer buf; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw functions")))); + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + /* Check that this relation has storage */ + if (rel->rd_rel->relkind == RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from view \"%s\"", + RelationGetRelationName(rel)))); + if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from composite type \"%s\"", + RelationGetRelationName(rel)))); + + /* + * Reject attempts to read non-local temporary relations; we would + * be likely to get wrong data since we have no visibility into the + * owning session's local buffers. + */ + if (isOtherTempNamespace(RelationGetNamespace(rel))) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"))); + + if (blkno >= RelationGetNumberOfBlocks(rel)) + elog(ERROR, "block number %u is out of range for relation \"%s\"", + blkno, RelationGetRelationName(rel)); + + /* Initialize buffer to copy to */ + raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ); + SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ); + raw_page_data = VARDATA(raw_page); + + /* Take a verbatim copy of the page */ + + buf = ReadBuffer(rel, blkno); + LockBuffer(buf, BUFFER_LOCK_SHARE); + + memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ); + + LockBuffer(buf, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buf); + + relation_close(rel, AccessShareLock); + + PG_RETURN_BYTEA_P(raw_page); +} + +/* + * page_header + * + * Allows inspection of page header fields of a raw page + */ + +PG_FUNCTION_INFO_V1(page_header); + +Datum +page_header(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + int raw_page_size; + + TupleDesc tupdesc; + + Datum result; + HeapTuple tuple; + Datum values[9]; + bool nulls[9]; + + PageHeader page; + XLogRecPtr lsn; + char lsnchar[64]; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + raw_page_size = VARSIZE(raw_page) - VARHDRSZ; + + /* + * Check that enough data was supplied, so that we don't try to access + * fields outside the supplied buffer. + */ + if (raw_page_size < sizeof(PageHeaderData)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page too small (%d bytes)", raw_page_size))); + + page = (PageHeader) VARDATA(raw_page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Extract information from the page header */ + + lsn = PageGetLSN(page); + snprintf(lsnchar, sizeof(lsnchar), "%X/%X", lsn.xlogid, lsn.xrecoff); + + values[0] = DirectFunctionCall1(textin, CStringGetDatum(lsnchar)); + values[1] = UInt16GetDatum(PageGetTLI(page)); + values[2] = UInt16GetDatum(page->pd_flags); + values[3] = UInt16GetDatum(page->pd_lower); + values[4] = UInt16GetDatum(page->pd_upper); + values[5] = UInt16GetDatum(page->pd_special); + values[6] = UInt16GetDatum(PageGetPageSize(page)); + values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page)); + values[8] = TransactionIdGetDatum(page->pd_prune_xid); + + /* Build and return the tuple. */ + + memset(nulls, 0, sizeof(nulls)); + + tuple = heap_form_tuple(tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} diff --git a/contrib/pageinspect/uninstall_pageinspect.sql b/contrib/pageinspect/uninstall_pageinspect.sql new file mode 100644 index 000000000000..161393d5a9ba --- /dev/null +++ b/contrib/pageinspect/uninstall_pageinspect.sql @@ -0,0 +1,11 @@ +/* $PostgreSQL: pgsql/contrib/pageinspect/uninstall_pageinspect.sql,v 1.4 2007/11/13 04:24:28 momjian Exp $ */ + +-- Adjust this setting to control where the objects get dropped. +SET search_path = public; + +DROP FUNCTION get_raw_page(text, int4); +DROP FUNCTION page_header(bytea); +DROP FUNCTION heap_page_items(bytea); +DROP FUNCTION bt_metap(text); +DROP FUNCTION bt_page_stats(text, int4); +DROP FUNCTION bt_page_items(text, int4); diff --git a/contrib/pg_buffercache/.gitignore b/contrib/pg_buffercache/.gitignore new file mode 100644 index 000000000000..fea8b0b3d40b --- /dev/null +++ b/contrib/pg_buffercache/.gitignore @@ -0,0 +1 @@ +/pg_buffercache.sql diff --git a/contrib/pg_freespacemap/.gitignore b/contrib/pg_freespacemap/.gitignore new file mode 100644 index 000000000000..645433a39fa2 --- /dev/null +++ b/contrib/pg_freespacemap/.gitignore @@ -0,0 +1 @@ +/pg_freespacemap.sql diff --git a/contrib/pg_freespacemap/README.pg_freespacemap b/contrib/pg_freespacemap/README.pg_freespacemap deleted file mode 100755 index 9210419cb8c4..000000000000 --- a/contrib/pg_freespacemap/README.pg_freespacemap +++ /dev/null @@ -1,173 +0,0 @@ -Pg_freespacemap - Real time queries on the free space map (FSM). ---------------- - - This module consists of two C functions: 'pg_freespacemap_relations()' and - 'pg_freespacemap_pages()' that return a set of records, plus two views - 'pg_freespacemap_relations' and 'pg_freespacemap_pages' for more - user-friendly access to the functions. - - The module provides the ability to examine the contents of the free space - map, without having to restart or rebuild the server with additional - debugging code. - - By default public access is REVOKED from the functions and views, just in - case there are security issues present in the code. - - -Installation ------------- - - Build and install the main Postgresql source, then this contrib module: - - $ cd contrib/pg_freespacemap - $ gmake - $ gmake install - - - To register the functions and views: - - $ psql -d -f pg_freespacemap.sql - - -Notes ------ - - The definitions for the columns exposed in the views are: - - pg_freespacemap_relations - - Column | references | Description - ------------------+----------------------+---------------------------------- - reltablespace | pg_tablespace.oid | Tablespace oid of the relation. - reldatabase | pg_database.oid | Database oid of the relation. - relfilenode | pg_class.relfilenode | Relfilenode of the relation. - avgrequest | | Moving average of free space - | | requests (NULL for indexes) - interestingpages | | Count of pages last reported as - | | containing useful free space. - storedpages | | Count of pages actually stored - | | in free space map. - nextpage | | Page index (from 0) to start next - | | search at. - - - pg_freespacemap_pages - - Column | references | Description - ----------------+----------------------+------------------------------------ - reltablespace | pg_tablespace.oid | Tablespace oid of the relation. - reldatabase | pg_database.oid | Database oid of the relation. - relfilenode | pg_class.relfilenode | Relfilenode of the relation. - relblocknumber | | Page number in the relation. - bytes | | Free bytes in the page, or NULL - | | for an index page (see below). - - - For pg_freespacemap_relations, there is one row for each relation in the free - space map. storedpages is the number of pages actually stored in the map, - while interestingpages is the number of pages the last VACUUM thought had - useful amounts of free space. - - If storedpages is consistently less than interestingpages then it'd be a - good idea to increase max_fsm_pages. Also, if the number of rows in - pg_freespacemap_relations is close to max_fsm_relations, then you should - consider increasing max_fsm_relations. - - For pg_freespacemap_pages, there is one row for each page in the free space - map. The number of rows for a relation will match the storedpages column - in pg_freespacemap_relations. - - For indexes, what is tracked is entirely-unused pages, rather than free - space within pages. Therefore, the average request size and free bytes - within a page are not meaningful, and are shown as NULL. - - Because the map is shared by all the databases, it will include relations - not belonging to the current database. - - When either of the views are accessed, internal free space map locks are - taken, and a copy of the map data is made for them to display. - This ensures that the views produce a consistent set of results, while not - blocking normal activity longer than necessary. Nonetheless there - could be some impact on database performance if they are read often. - - -Sample output - pg_freespacemap_relations -------------- - -regression=# \d pg_freespacemap_relations -View "public.pg_freespacemap_relations" - Column | Type | Modifiers -------------------+---------+----------- - reltablespace | oid | - reldatabase | oid | - relfilenode | oid | - avgrequest | integer | - interestingpages | integer | - storedpages | integer | - nextpage | integer | -View definition: - SELECT p.reltablespace, p.reldatabase, p.relfilenode, p.avgrequest, p.interestingpages, p.storedpages, p.nextpage - FROM pg_freespacemap_relations() p(reltablespace oid, reldatabase oid, relfilenode oid, avgrequest integer, interestingpages integer, storedpages integer, nextpage integer); - -regression=# SELECT c.relname, r.avgrequest, r.interestingpages, r.storedpages - FROM pg_freespacemap_relations r INNER JOIN pg_class c - ON c.relfilenode = r.relfilenode INNER JOIN pg_database d - ON r.reldatabase = d.oid AND (d.datname = current_database()) - ORDER BY r.storedpages DESC LIMIT 10; - relname | avgrequest | interestingpages | storedpages ----------------------------------+------------+------------------+------------- - onek | 256 | 109 | 109 - pg_attribute | 167 | 93 | 93 - pg_class | 191 | 49 | 49 - pg_attribute_relid_attnam_index | | 48 | 48 - onek2 | 256 | 37 | 37 - pg_depend | 95 | 26 | 26 - pg_type | 199 | 16 | 16 - pg_rewrite | 1011 | 13 | 13 - pg_class_relname_nsp_index | | 10 | 10 - pg_proc | 302 | 8 | 8 -(10 rows) - - -Sample output - pg_freespacemap_pages -------------- - -regression=# \d pg_freespacemap_pages - View "public.pg_freespacemap_pages" - Column | Type | Modifiers -----------------+---------+----------- - reltablespace | oid | - reldatabase | oid | - relfilenode | oid | - relblocknumber | bigint | - bytes | integer | -View definition: - SELECT p.reltablespace, p.reldatabase, p.relfilenode, p.relblocknumber, p.bytes - FROM pg_freespacemap_pages() p(reltablespace oid, reldatabase oid, relfilenode oid, relblocknumber bigint, bytes integer); - -regression=# SELECT c.relname, p.relblocknumber, p.bytes - FROM pg_freespacemap_pages p INNER JOIN pg_class c - ON c.relfilenode = p.relfilenode INNER JOIN pg_database d - ON (p.reldatabase = d.oid AND d.datname = current_database()) - ORDER BY c.relname LIMIT 10; - relname | relblocknumber | bytes ---------------+----------------+------- - a_star | 0 | 8040 - abstime_tbl | 0 | 7908 - aggtest | 0 | 8008 - altinhoid | 0 | 8128 - altstartwith | 0 | 8128 - arrtest | 0 | 7172 - b_star | 0 | 7976 - box_tbl | 0 | 7912 - bt_f8_heap | 54 | 7728 - bt_i4_heap | 49 | 8008 -(10 rows) - - - -Author ------- - - * Mark Kirkwood - diff --git a/contrib/pg_standby/.gitignore b/contrib/pg_standby/.gitignore new file mode 100644 index 000000000000..a401b085a895 --- /dev/null +++ b/contrib/pg_standby/.gitignore @@ -0,0 +1 @@ +/pg_standby diff --git a/contrib/pg_standby/Makefile b/contrib/pg_standby/Makefile index e66b1b5c118e..3e0ccb00fa53 100644 --- a/contrib/pg_standby/Makefile +++ b/contrib/pg_standby/Makefile @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/contrib/pg_standby/Makefile,v 1.2 2007/02/09 17:04:00 petere Exp $ +# $PostgreSQL: pgsql/contrib/pg_standby/Makefile,v 1.4 2007/11/10 23:59:51 momjian Exp $ PROGRAM = pg_standby OBJS = pg_standby.o @@ -6,10 +6,9 @@ OBJS = pg_standby.o PG_CPPFLAGS = -I$(libpq_srcdir) PG_LIBS = $(libpq_pgport) -DOCS = README.pg_standby - ifdef USE_PGXS -PGXS := $(shell pg_config --pgxs) +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) else subdir = contrib/pg_standby diff --git a/contrib/pg_standby/README.pg_standby b/contrib/pg_standby/README.pg_standby deleted file mode 100644 index 9c4ef8ecb6f8..000000000000 --- a/contrib/pg_standby/README.pg_standby +++ /dev/null @@ -1,164 +0,0 @@ -pg_standby README 2006/12/08 Simon Riggs - -o What is pg_standby? - - pg_standby is a production-ready program that can be used to - create a Warm Standby server. Other configuration is required - as well, all of which is described in the main server manual. - - The program is designed to be a wait-for restore_command, - required to turn a normal archive recovery into a Warm Standby. - Within the restore_command of the recovery.conf you could - configure pg_standby in the following way: - - restore_command = 'pg_standby archiveDir %f %p' - - which would be sufficient to define that files will be restored - from archiveDir. - -o features of pg_standby - - - pg_standby is written in C. So it is very portable - and easy to install. - - - supports copy or link from a directory (only) - - - source easy to modify, with specifically designated - sections to modify for your own needs, allowing - interfaces to be written for additional Backup Archive Restore - (BAR) systems - - - portable: tested on Linux and Windows - -o How to install pg_standby - - $make - $make install - -o How to use pg_standby? - - pg_standby should be used within the restore_command of the - recovery.conf file. See the main PostgreSQL manual for details. - - The basic usage should be like this: - - restore_command = 'pg_standby archiveDir %f %p' - - with the pg_standby command usage as - - pg_standby [OPTION]... [ARCHIVELOCATION] [NEXTWALFILE] [XLOGFILEPATH] - - When used within the restore_command the %f and %p macros - will provide the actual file and path required for the restore/recovery. - -o options - - pg_standby has number of options. - - -c - use copy/cp command to restore WAL files from archive - - -d - debug/logging option. - - -k numfiles - Cleanup files in the archive so that we maintain no more - than this many files in the archive. - - You should be wary against setting this number too low, - since this may mean you cannot restart the standby. This - is because the last restartpoint marked in the WAL files - may be many files in the past and can vary considerably. - This should be set to a value exceeding the number of WAL - files that can be recovered in 2*checkpoint_timeout seconds, - according to the value in the warm standby postgresql.conf. - It is wholly unrelated to the setting of checkpoint_segments - on either primary or standby. - - If in doubt, use a large value or do not set a value at all. - - -l - use ln command to restore WAL files from archive - WAL files will remain in archive - - Link is more efficient, but the default is copy to - allow you to maintain the WAL archive for recovery - purposes as well as high-availability. - - This option uses the Windows Vista command mklink - to provide a file-to-file symbolic link. -l will - not work on versions of Windows prior to Vista. - Use the -c option instead. - see http://en.wikipedia.org/wiki/NTFS_symbolic_link - - -r maxretries - the maximum number of times to retry the restore command if it - fails. After each failure, we wait for sleeptime * num_retries - so that the wait time increases progressively, so by default - we will wait 5 secs, 10 secs then 15 secs before reporting - the failure back to the database server. This will be - interpreted as and end of recovery and the Standby will come - up fully as a result. - Default=3 - - -s sleeptime - the number of seconds to sleep between testing to see - if the file to be restored is available in the archive yet. - The default setting is not necessarily recommended, - consult the main database server manual for discussion. - Default=5 - - -t triggerfile - the presence of the triggerfile will cause recovery to end - whether or not the next file is available - It is recommended that you use a structured filename to - avoid confusion as to which server is being triggered - when multiple servers exist on same system. - e.g. /tmp/pgsql.trigger.5432 - - -w maxwaittime - the maximum number of seconds to wait for the next file, - after which recovery will end and the Standby will come up. - The default setting is not necessarily recommended, - consult the main database server manual for discussion. - Default=0 - - Note: --help is not supported since pg_standby is not intended - for interactive use, except during dev/test - -o examples - - Linux - - archive_command = 'cp %p ../archive/%f' - - restore_command = 'pg_standby -l -d -k 255 -r 2 -s 2 -w 0 -t /tmp/pgsql.trigger.5442 $PWD/../archive %f %p 2>> standby.log' - - which will - - use a ln command to restore WAL files from archive - - produce logfile output in standby.log - - keep the last 255 full WAL files, plus the current one - - sleep for 2 seconds between checks for next WAL file is full - - never timeout if file not found - - stop waiting when a trigger file called /tmp.pgsql.trigger.5442 appears - - Windows - - archive_command = 'copy %p ..\\archive\\%f' - Note that backslashes need to be doubled in the archive_command, but - *not* in the restore_command, in 8.2, 8.1, 8.0 on Windows. - - restore_command = 'pg_standby -c -d -s 5 -w 0 -t C:\pgsql.trigger.5442 -..\archive %f %p 2>> standby.log' - - which will - - use a copy command to restore WAL files from archive - - produce logfile output in standby.log - - sleep for 5 seconds between checks for next WAL file is full - - never timeout if file not found - - stop waiting when a trigger file called C:\pgsql.trigger.5442 appears - -o reported test success - - SUSE Linux 10.2 - Windows XP Pro diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c index 98d04c26f071..93139ed57c36 100644 --- a/contrib/pg_standby/pg_standby.c +++ b/contrib/pg_standby/pg_standby.c @@ -1,12 +1,12 @@ /* * pg_standby.c - * + * * Production-ready example of how to create a Warm Standby - * database server using continuous archiving as a + * database server using continuous archiving as a * replication mechanism * * We separate the parameters for archive and nextWALfile - * so that we can check the archive exists, even if the + * so that we can check the archive exists, even if the * WAL file doesn't (yet). * * This program will be executed once in full for each file @@ -14,12 +14,11 @@ * * It is designed to cater to a variety of needs, as well * providing a customizable section. - * - * Original author: Simon Riggs simon@2ndquadrant.com - * Current maintainer: Simon Riggs + * + * Original author: Simon Riggs simon@2ndquadrant.com + * Current maintainer: Simon Riggs */ #include "postgres_fe.h" -#include "pg_config_manual.h" #include #include @@ -27,7 +26,7 @@ #include #ifdef WIN32 -int getopt(int argc, char * const argv[], const char *optstring); +int getopt(int argc, char *const argv[], const char *optstring); #else #include #include @@ -35,42 +34,49 @@ int getopt(int argc, char * const argv[], const char *optstring); #ifdef HAVE_GETOPT_H #include #endif - #endif /* ! WIN32 */ extern char *optarg; extern int optind; /* Options and defaults */ -int sleeptime = 5; /* amount of time to sleep between file checks */ -int waittime = -1; /* how long we have been waiting, -1 no wait yet */ -int maxwaittime = 0; /* how long are we prepared to wait for? */ -int keepfiles = 0; /* number of WAL files to keep, 0 keep all */ -int maxretries = 3; /* number of retries on restore command */ -bool debug = false; /* are we debugging? */ -bool triggered = false; -bool signaled = false; - -char *archiveLocation; /* where to find the archive? */ -char *triggerPath; /* where to find the trigger file? */ -char *xlogFilePath; /* where we are going to restore to */ -char *nextWALFileName; /* the file we need to get from archive */ -char *priorWALFileName; /* the file we need to get from archive */ -char WALFilePath[MAXPGPATH];/* the file path including archive */ -char restoreCommand[MAXPGPATH]; /* run this to restore */ -char inclusiveCleanupFileName[MAXPGPATH]; /* the file we need to get from archive */ +int sleeptime = 5; /* amount of time to sleep between file checks */ +int waittime = -1; /* how long we have been waiting, -1 no wait + * yet */ +int maxwaittime = 0; /* how long are we prepared to wait for? */ +int keepfiles = 0; /* number of WAL files to keep, 0 keep all */ +int maxretries = 3; /* number of retries on restore command */ +bool debug = false; /* are we debugging? */ +bool triggered = false; /* have we been triggered? */ +bool need_cleanup = false; /* do we need to remove files from + * archive? */ + +#ifndef WIN32 +static volatile sig_atomic_t signaled = false; +#endif + +char *archiveLocation; /* where to find the archive? */ +char *triggerPath; /* where to find the trigger file? */ +char *xlogFilePath; /* where we are going to restore to */ +char *nextWALFileName; /* the file we need to get from archive */ +char *restartWALFileName; /* the file from which we can restart restore */ +char *priorWALFileName; /* the file we need to get from archive */ +char WALFilePath[MAXPGPATH]; /* the file path including archive */ +char restoreCommand[MAXPGPATH]; /* run this to restore */ +char exclusiveCleanupFileName[MAXPGPATH]; /* the file we need to + * get from archive */ #define RESTORE_COMMAND_COPY 0 #define RESTORE_COMMAND_LINK 1 -int restoreCommandType; +int restoreCommandType; #define XLOG_DATA 0 #define XLOG_HISTORY 1 #define XLOG_BACKUP_LABEL 2 -int nextWALFileType; +int nextWALFileType; #define SET_RESTORE_COMMAND(cmd, arg1, arg2) \ - snprintf(restoreCommand, MAXPGPATH, cmd " %s %s", arg1, arg2) + snprintf(restoreCommand, MAXPGPATH, cmd " \"%s\" \"%s\"", arg1, arg2) struct stat stat_buf; @@ -84,21 +90,21 @@ struct stat stat_buf; * accessible directory. If you want to make other assumptions, * such as using a vendor-specific archive and access API, these * routines are the ones you'll need to change. You're - * enouraged to submit any changes to pgsql-patches@postgresql.org - * or personally to the current maintainer. Those changes may be + * enouraged to submit any changes to pgsql-hackers@postgresql.org + * or personally to the current maintainer. Those changes may be * folded in to later versions of this program. */ -#define XLOG_DATA_FNAME_LEN 24 +#define XLOG_DATA_FNAME_LEN 24 /* Reworked from access/xlog_internal.h */ #define XLogFileName(fname, tli, log, seg) \ snprintf(fname, XLOG_DATA_FNAME_LEN + 1, "%08X%08X%08X", tli, log, seg) /* - * Initialize allows customized commands into the warm standby program. + * Initialize allows customized commands into the warm standby program. * - * As an example, and probably the common case, we use either - * cp/ln commands on *nix, or copy/move command on Windows. + * As an example, and probably the common case, we use either + * cp/ln commands on *nix, or copy/move command on Windows. * */ static void @@ -109,79 +115,79 @@ CustomizableInitialize(void) switch (restoreCommandType) { case RESTORE_COMMAND_LINK: - SET_RESTORE_COMMAND("mklink",WALFilePath, xlogFilePath); + SET_RESTORE_COMMAND("mklink", WALFilePath, xlogFilePath); case RESTORE_COMMAND_COPY: default: - SET_RESTORE_COMMAND("copy",WALFilePath, xlogFilePath); + SET_RESTORE_COMMAND("copy", WALFilePath, xlogFilePath); break; - } + } #else snprintf(WALFilePath, MAXPGPATH, "%s/%s", archiveLocation, nextWALFileName); switch (restoreCommandType) { case RESTORE_COMMAND_LINK: #if HAVE_WORKING_LINK - SET_RESTORE_COMMAND("ln -s -f",WALFilePath, xlogFilePath); + SET_RESTORE_COMMAND("ln -s -f", WALFilePath, xlogFilePath); break; #endif case RESTORE_COMMAND_COPY: default: - SET_RESTORE_COMMAND("cp",WALFilePath, xlogFilePath); + SET_RESTORE_COMMAND("cp", WALFilePath, xlogFilePath); break; - } + } #endif /* - * This code assumes that archiveLocation is a directory - * You may wish to add code to check for tape libraries, etc.. - * So, since it is a directory, we use stat to test if its accessible + * This code assumes that archiveLocation is a directory You may wish to + * add code to check for tape libraries, etc.. So, since it is a + * directory, we use stat to test if it's accessible */ if (stat(archiveLocation, &stat_buf) != 0) { - fprintf(stderr, "pg_standby: archiveLocation \"%s\" does not exist\n", archiveLocation); + fprintf(stderr, "pg_standby: archiveLocation \"%s\" does not exist\n", archiveLocation); fflush(stderr); - exit(2); + exit(2); } } /* * CustomizableNextWALFileReady() - * + * * Is the requested file ready yet? */ -static bool +static bool CustomizableNextWALFileReady() { if (stat(WALFilePath, &stat_buf) == 0) { /* - * If its a backup file, return immediately - * If its a regular file return only if its the right size already + * If it's a backup file, return immediately. If it's a regular file + * return only if it's the right size already. */ if (strlen(nextWALFileName) > 24 && strspn(nextWALFileName, "0123456789ABCDEF") == 24 && - strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".backup"), - ".backup") == 0) + strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".backup"), + ".backup") == 0) { nextWALFileType = XLOG_BACKUP_LABEL; - return true; + return true; } - else - if (stat_buf.st_size == XLOG_SEG_SIZE) - { + else if (stat_buf.st_size == XLOG_SEG_SIZE) + { #ifdef WIN32 - /* - * Windows reports that the file has the right number of bytes - * even though the file is still being copied and cannot be - * opened by pg_standby yet. So we wait for sleeptime secs - * before attempting to restore. If that is not enough, we - * will rely on the retry/holdoff mechanism. - */ - pg_usleep(sleeptime * 1000000L); + + /* + * Windows reports that the file has the right number of bytes + * even though the file is still being copied and cannot be opened + * by pg_standby yet. So we wait for sleeptime secs before + * attempting to restore. If that is not enough, we will rely on + * the retry/holdoff mechanism. + */ + pg_usleep(sleeptime * 1000000L); #endif - nextWALFileType = XLOG_DATA; - return true; - } + nextWALFileType = XLOG_DATA; + return true; + } /* * If still too small, wait until it is the correct size @@ -190,10 +196,10 @@ CustomizableNextWALFileReady() { if (debug) { - fprintf(stderr, "file size greater than expected\n"); + fprintf(stderr, "file size greater than expected\n"); fflush(stderr); } - exit(3); + exit(3); } } @@ -205,78 +211,69 @@ CustomizableNextWALFileReady() static void CustomizableCleanupPriorWALFiles(void) { - uint32 tli, - log, - seg; - int signed_log = 0; - - if (keepfiles > 0) - { - sscanf(nextWALFileName, "%08X%08X%08X", &tli, &log, &seg); - signed_log = log - (keepfiles / MaxSegmentsPerLogFile); - if (keepfiles <= seg) - seg -= keepfiles; - else - { - seg = MaxSegmentsPerLogFile - (keepfiles % MaxSegmentsPerLogFile); - signed_log--; - } - log = (uint32) signed_log; - } - /* * Work out name of prior file from current filename */ - if (keepfiles > 0 && signed_log >= 0 && nextWALFileType == XLOG_DATA) + if (nextWALFileType == XLOG_DATA) { - int rc; - DIR *xldir; - struct dirent *xlde; - - XLogFileName(inclusiveCleanupFileName, tli, log, seg); + int rc; + DIR *xldir; + struct dirent *xlde; /* - * Assume its OK to keep failing. The failure situation may change over - * time, so we'd rather keep going on the main processing than fail - * because we couldnt clean up yet. + * Assume it's OK to keep failing. The failure situation may change + * over time, so we'd rather keep going on the main processing than + * fail because we couldnt clean up yet. */ if ((xldir = opendir(archiveLocation)) != NULL) { while ((xlde = readdir(xldir)) != NULL) { /* - * We ignore the timeline part of the XLOG segment identifiers in - * deciding whether a segment is still needed. This ensures that we - * won't prematurely remove a segment from a parent timeline. We could - * probably be a little more proactive about removing segments of - * non-parent timelines, but that would be a whole lot more - * complicated. + * We ignore the timeline part of the XLOG segment identifiers + * in deciding whether a segment is still needed. This + * ensures that we won't prematurely remove a segment from a + * parent timeline. We could probably be a little more + * proactive about removing segments of non-parent timelines, + * but that would be a whole lot more complicated. * - * We use the alphanumeric sorting property of the filenames to decide - * which ones are earlier than the inclusiveCleanupFileName file. + * We use the alphanumeric sorting property of the filenames + * to decide which ones are earlier than the + * exclusiveCleanupFileName file. Note that this means files + * are not removed in the order they were originally written, + * in case this worries you. */ if (strlen(xlde->d_name) == XLOG_DATA_FNAME_LEN && strspn(xlde->d_name, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN && - strcmp(xlde->d_name + 8, inclusiveCleanupFileName + 8) <= 0) + strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0) { #ifdef WIN32 snprintf(WALFilePath, MAXPGPATH, "%s\\%s", archiveLocation, xlde->d_name); #else snprintf(WALFilePath, MAXPGPATH, "%s/%s", archiveLocation, xlde->d_name); #endif - rc = unlink(WALFilePath); if (debug) - fprintf(stderr, "\npg_standby: removed \"%s\"\n", WALFilePath); + fprintf(stderr, "\nremoving \"%s\"", WALFilePath); + + rc = unlink(WALFilePath); + if (rc != 0) + { + fprintf(stderr, "\npg_standby: ERROR failed to remove \"%s\": %s", + WALFilePath, strerror(errno)); + break; + } } } + if (debug) + fprintf(stderr, "\n"); } else - fprintf(stderr, "pg_standby: archiveLocation \"%s\" open error\n", archiveLocation); + fprintf(stderr, "pg_standby: archiveLocation \"%s\" open error\n", archiveLocation); closedir(xldir); + fflush(stderr); } - fflush(stderr); } /* ===================================================================== @@ -284,40 +281,106 @@ CustomizableCleanupPriorWALFiles(void) * ===================================================================== */ +/* + * SetWALFileNameForCleanup() + * + * Set the earliest WAL filename that we want to keep on the archive + * and decide whether we need_cleanup + */ +static bool +SetWALFileNameForCleanup(void) +{ + uint32 tli = 1, + log = 0, + seg = 0; + uint32 log_diff = 0, + seg_diff = 0; + bool cleanup = false; + + if (restartWALFileName) + { + /* + * Don't do cleanup if the restartWALFileName provided + * is later than the xlog file requested. This is an error + * and we must not remove these files from archive. + * This shouldn't happen, but better safe than sorry. + */ + if (strcmp(restartWALFileName, nextWALFileName) > 0) + return false; + + strcpy(exclusiveCleanupFileName, restartWALFileName); + return true; + } + + if (keepfiles > 0) + { + sscanf(nextWALFileName, "%08X%08X%08X", &tli, &log, &seg); + if (tli > 0 && log >= 0 && seg > 0) + { + log_diff = keepfiles / MaxSegmentsPerLogFile; + seg_diff = keepfiles % MaxSegmentsPerLogFile; + if (seg_diff > seg) + { + log_diff++; + seg = MaxSegmentsPerLogFile - (seg_diff - seg); + } + else + seg -= seg_diff; + + if (log >= log_diff) + { + log -= log_diff; + cleanup = true; + } + else + { + log = 0; + seg = 0; + } + } + } + + XLogFileName(exclusiveCleanupFileName, tli, log, seg); + + return cleanup; +} + /* * CheckForExternalTrigger() - * + * * Is there a trigger file? */ -static bool +static bool CheckForExternalTrigger(void) { - int rc; + int rc; /* - * Look for a trigger file, if that option has been selected + * Look for a trigger file, if that option has been selected * - * We use stat() here because triggerPath is always a file - * rather than potentially being in an archive + * We use stat() here because triggerPath is always a file rather than + * potentially being in an archive */ if (triggerPath && stat(triggerPath, &stat_buf) == 0) { - fprintf(stderr, "trigger file found\n"); + fprintf(stderr, "trigger file found\n"); fflush(stderr); /* - * If trigger file found, we *must* delete it. Here's why: - * When recovery completes, we will be asked again - * for the same file from the archive using pg_standby - * so must remove trigger file so we can reload file again - * and come up correctly. + * If trigger file found, we *must* delete it. Here's why: When + * recovery completes, we will be asked again for the same file from + * the archive using pg_standby so must remove trigger file so we can + * reload file again and come up correctly. + * + * If it fails, return with an exit code that the server will treat + * as a FATAL error. */ rc = unlink(triggerPath); if (rc != 0) { - fprintf(stderr, "\n ERROR: unable to remove \"%s\", rc=%d", triggerPath, rc); + fprintf(stderr, "\n ERROR: could not remove \"%s\": %s", triggerPath, strerror(errno)); fflush(stderr); - exit(rc); + exit(200); } return true; } @@ -327,14 +390,14 @@ CheckForExternalTrigger(void) /* * RestoreWALFileForRecovery() - * + * * Perform the action required to restore the file from archive */ static bool RestoreWALFileForRecovery(void) { - int rc = 0; - int numretries = 0; + int rc = 0; + int numretries = 0; if (debug) { @@ -342,19 +405,19 @@ RestoreWALFileForRecovery(void) fflush(stderr); } - while (numretries < maxretries) + while (numretries <= maxretries) { rc = system(restoreCommand); if (rc == 0) { if (debug) { - fprintf(stderr, " success\n"); + fprintf(stderr, " OK"); fflush(stderr); } return true; } - pg_usleep(numretries++ * sleeptime * 1000000L); + pg_usleep(numretries++ * sleeptime * 1000000L); } /* @@ -366,42 +429,68 @@ RestoreWALFileForRecovery(void) } static void -usage() +usage(void) { fprintf(stderr, "\npg_standby allows Warm Standby servers to be configured\n"); fprintf(stderr, "Usage:\n"); - fprintf(stderr, " pg_standby [OPTION]... [ARCHIVELOCATION] [NEXTWALFILE] [XLOGFILEPATH]\n"); - fprintf(stderr, " note space between [ARCHIVELOCATION] and [NEXTWALFILE]\n"); - fprintf(stderr, "with main intended use via restore_command in the recovery.conf\n"); - fprintf(stderr, " restore_command = 'pg_standby [OPTION]... [ARCHIVELOCATION] %%f %%p'\n"); - fprintf(stderr, "e.g. restore_command = 'pg_standby -l -u /mnt/server/archiverdir %%f %%p'\n"); + fprintf(stderr, " pg_standby [OPTION]... ARCHIVELOCATION NEXTWALFILE XLOGFILEPATH [RESTARTWALFILE]\n"); + fprintf(stderr, " note space between ARCHIVELOCATION and NEXTWALFILE\n"); + fprintf(stderr, "with main intended use as a restore_command in the recovery.conf\n"); + fprintf(stderr, " restore_command = 'pg_standby [OPTION]... ARCHIVELOCATION %%f %%p %%r'\n"); + fprintf(stderr, "e.g. restore_command = 'pg_standby -l /mnt/server/archiverdir %%f %%p %%r'\n"); fprintf(stderr, "\nOptions:\n"); fprintf(stderr, " -c copies file from archive (default)\n"); fprintf(stderr, " -d generate lots of debugging output (testing only)\n"); - fprintf(stderr, " -k [NUMFILESTOKEEP] keeps history of # files in archives; unlinks/removes files beyond that\n"); + fprintf(stderr, " -k NUMFILESTOKEEP if RESTARTWALFILE not used, removes files prior to limit (0 keeps all)\n"); fprintf(stderr, " -l links into archive (leaves file in archive)\n"); - fprintf(stderr, " -t [TRIGGERFILE] defines a trigger file to initiate failover (no default)\n"); - fprintf(stderr, " -r [MAXRETRIES] maximum number of times to retry, with progressive wait (default=3)\n"); - fprintf(stderr, " -s [SLEEPTIME] number of seconds to wait between file checks (default=5)\n"); - fprintf(stderr, " -w [MAXWAITTIME] max number of seconds to wait for a file (0 disables)(default=0)\n"); + fprintf(stderr, " -r MAXRETRIES max number of times to retry, with progressive wait (default=3)\n"); + fprintf(stderr, " -s SLEEPTIME seconds to wait between file checks (min=1, max=60, default=5)\n"); + fprintf(stderr, " -t TRIGGERFILE defines a trigger file to initiate failover (no default)\n"); + fprintf(stderr, " -w MAXWAITTIME max seconds to wait for a file (0=no limit)(default=0)\n"); fflush(stderr); } +#ifndef WIN32 static void sighandler(int sig) { - triggered = true; signaled = true; } +/* We don't want SIGQUIT to core dump */ +static void +sigquit_handler(int sig) +{ + signal(SIGINT, SIG_DFL); + kill(getpid(), SIGINT); +} +#endif + /*------------ MAIN ----------------------------------------*/ -int +int main(int argc, char **argv) { int c; - (void) signal(SIGINT, sighandler); - (void) signal(SIGQUIT, sighandler); +#ifndef WIN32 + /* + * You can send SIGUSR1 to trigger failover. + * + * Postmaster uses SIGQUIT to request immediate shutdown. The default + * action is to core dump, but we don't want that, so trap it and + * commit suicide without core dump. + * + * We used to use SIGINT and SIGQUIT to trigger failover, but that + * turned out to be a bad idea because postmaster uses SIGQUIT to + * request immediate shutdown. We still trap SIGINT, but that may + * change in a future release. + * + * There's no way to trigger failover via signal on Windows. + */ + (void) signal(SIGUSR1, sighandler); + (void) signal(SIGINT, sighandler); /* deprecated, use SIGUSR1 */ + (void) signal(SIGQUIT, sigquit_handler); +#endif while ((c = getopt(argc, argv, "cdk:lr:s:t:w:")) != -1) { @@ -415,9 +504,9 @@ main(int argc, char **argv) break; case 'k': /* keepfiles */ keepfiles = atoi(optarg); - if (keepfiles <= 0) + if (keepfiles < 0) { - fprintf(stderr, "usage: pg_standby -k keepfiles must be > 0\n"); + fprintf(stderr, "usage: pg_standby -k keepfiles must be >= 0\n"); usage(); exit(2); } @@ -429,7 +518,7 @@ main(int argc, char **argv) maxretries = atoi(optarg); if (maxretries < 0) { - fprintf(stderr, "usage: pg_standby -r maxretries must be > 0\n"); + fprintf(stderr, "usage: pg_standby -r maxretries must be >= 0\n"); usage(); exit(2); } @@ -446,8 +535,8 @@ main(int argc, char **argv) case 't': /* Trigger file */ triggerPath = optarg; if (CheckForExternalTrigger()) - exit(1); /* Normal exit, with non-zero */ - break; + exit(1); /* Normal exit, with non-zero */ + break; case 'w': /* Max wait time */ maxwaittime = atoi(optarg); if (maxwaittime < 0) @@ -464,7 +553,7 @@ main(int argc, char **argv) } } - /* + /* * Parameter checking - after checking to see if trigger file present */ if (argc == 1) @@ -475,8 +564,8 @@ main(int argc, char **argv) /* * We will go to the archiveLocation to get nextWALFileName. - * nextWALFileName may not exist yet, which would not be an error, - * so we separate the archiveLocation and nextWALFileName so we can check + * nextWALFileName may not exist yet, which would not be an error, so we + * separate the archiveLocation and nextWALFileName so we can check * separately whether archiveLocation exists, if not that is an error */ if (optind < argc) @@ -486,7 +575,7 @@ main(int argc, char **argv) } else { - fprintf(stderr, "pg_standby: must specify archiveLocation\n"); + fprintf(stderr, "pg_standby: must specify archiveLocation\n"); usage(); exit(2); } @@ -498,7 +587,7 @@ main(int argc, char **argv) } else { - fprintf(stderr, "pg_standby: use %%f to specify nextWALFileName\n"); + fprintf(stderr, "pg_standby: use %%f to specify nextWALFileName\n"); usage(); exit(2); } @@ -510,28 +599,37 @@ main(int argc, char **argv) } else { - fprintf(stderr, "pg_standby: use %%p to specify xlogFilePath\n"); + fprintf(stderr, "pg_standby: use %%p to specify xlogFilePath\n"); usage(); exit(2); } + if (optind < argc) + { + restartWALFileName = argv[optind]; + optind++; + } + CustomizableInitialize(); + need_cleanup = SetWALFileNameForCleanup(); + if (debug) { - fprintf(stderr, "\nTrigger file : %s", triggerPath ? triggerPath : ""); - fprintf(stderr, "\nWaiting for WAL file : %s", WALFilePath); - fprintf(stderr, "\nWAL file path : %s", nextWALFileName); - fprintf(stderr, "\nRestoring to... : %s", xlogFilePath); - fprintf(stderr, "\nSleep interval : %d second%s", - sleeptime, (sleeptime > 1 ? "s" : " ")); - fprintf(stderr, "\nMax wait interval : %d %s", - maxwaittime, (maxwaittime > 0 ? "seconds" : "forever")); + fprintf(stderr, "\nTrigger file : %s", triggerPath ? triggerPath : ""); + fprintf(stderr, "\nWaiting for WAL file : %s", nextWALFileName); + fprintf(stderr, "\nWAL file path : %s", WALFilePath); + fprintf(stderr, "\nRestoring to... : %s", xlogFilePath); + fprintf(stderr, "\nSleep interval : %d second%s", + sleeptime, (sleeptime > 1 ? "s" : " ")); + fprintf(stderr, "\nMax wait interval : %d %s", + maxwaittime, (maxwaittime > 0 ? "seconds" : "forever")); fprintf(stderr, "\nCommand for restore : %s", restoreCommand); - if (keepfiles > 0) - fprintf(stderr, "\nNum archived files kept : last %d files", keepfiles); + fprintf(stderr, "\nKeep archive history : "); + if (need_cleanup) + fprintf(stderr, "%s and later", exclusiveCleanupFileName); else - fprintf(stderr, "\nNum archived files kept : all files"); + fprintf(stderr, "No cleanup required"); fflush(stderr); } @@ -558,61 +656,61 @@ main(int argc, char **argv) } } - /* + /* * Main wait loop */ while (!CustomizableNextWALFileReady() && !triggered) { if (sleeptime <= 60) - pg_usleep(sleeptime * 1000000L); + pg_usleep(sleeptime * 1000000L); +#ifndef WIN32 if (signaled) { + triggered = true; if (debug) { - fprintf(stderr, "\nsignaled to exit\n"); + fprintf(stderr, "\nsignaled to exit\n"); fflush(stderr); } } else +#endif { if (debug) { - fprintf(stderr, "\nWAL file not present yet."); + fprintf(stderr, "\nWAL file not present yet."); if (triggerPath) - fprintf(stderr, " Checking for trigger file..."); + fprintf(stderr, " Checking for trigger file..."); fflush(stderr); } waittime += sleeptime; - + if (!triggered && (CheckForExternalTrigger() || (waittime >= maxwaittime && maxwaittime > 0))) { triggered = true; if (debug && waittime >= maxwaittime && maxwaittime > 0) - fprintf(stderr, "\nTimed out after %d seconds\n",waittime); + fprintf(stderr, "\nTimed out after %d seconds\n", waittime); } } } - /* - * Action on exit + /* + * Action on exit */ if (triggered) - exit(1); /* Normal exit, with non-zero */ - else - { - /* - * Once we have restored this file successfully we - * can remove some prior WAL files. - * If this restore fails we musn't remove any - * file because some of them will be requested again - * immediately after the failed restore, or when - * we restart recovery. - */ - if (RestoreWALFileForRecovery()) - CustomizableCleanupPriorWALFiles(); - exit(0); - } + exit(1); /* Normal exit, with non-zero */ + + /* + * Once we have restored this file successfully we can remove some prior + * WAL files. If this restore fails we musn't remove any file because some + * of them will be requested again immediately after the failed restore, + * or when we restart recovery. + */ + if (RestoreWALFileForRecovery() && need_cleanup) + CustomizableCleanupPriorWALFiles(); + + return 0; } diff --git a/contrib/pg_trgm/.gitignore b/contrib/pg_trgm/.gitignore new file mode 100644 index 000000000000..9cda826ca43f --- /dev/null +++ b/contrib/pg_trgm/.gitignore @@ -0,0 +1,3 @@ +/pg_trgm.sql +# Generated subdirectories +/results/ diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h index 5a8d2e362a77..b8fb7cb0c4d0 100644 --- a/contrib/pg_trgm/trgm.h +++ b/contrib/pg_trgm/trgm.h @@ -28,6 +28,14 @@ typedef char trgm[3]; *(((char*)(a))+2) = *(((char*)(b))+2); \ } while(0); +uint32 trgm2int(trgm *ptr); + +#ifdef KEEPONLYALNUM +#define ISPRINTABLECHAR(a) ( isascii( *(unsigned char*)(a) ) && (isalnum( *(unsigned char*)(a) ) || *(unsigned char*)(a)==' ') ) +#else +#define ISPRINTABLECHAR(a) ( isascii( *(unsigned char*)(a) ) && isprint( *(unsigned char*)(a) ) ) +#endif +#define ISPRINTABLETRGM(t) ( ISPRINTABLECHAR( ((char*)t) ) && ISPRINTABLECHAR( ((char*)t)+1 ) && ISPRINTABLECHAR( ((char*)t)+2 ) ) #define TRGMINT(a) ( (*(((char*)(a))+2)<<16)+(*(((char*)(a))+1)<<8)+*(((char*)(a))+0) ) typedef struct diff --git a/contrib/pgbench/.gitignore b/contrib/pgbench/.gitignore index 929a08fea0c7..489a2d62d068 100644 --- a/contrib/pgbench/.gitignore +++ b/contrib/pgbench/.gitignore @@ -1 +1 @@ -pgbench +/pgbench diff --git a/contrib/pgcrypto/.gitignore b/contrib/pgcrypto/.gitignore index d7bfb2059cce..078157c95296 100644 --- a/contrib/pgcrypto/.gitignore +++ b/contrib/pgcrypto/.gitignore @@ -2,7 +2,8 @@ libpgcrypto.a libpgcrypto.so libpgcrypto.so.0 libpgcrypto.so.0.0 -pgcrypto.sql log -results tmp_check +/pgcrypto.sql +# Generated subdirectories +/results/ diff --git a/contrib/pgcrypto/README.pgcrypto b/contrib/pgcrypto/README.pgcrypto deleted file mode 100755 index e7aaceaf8f73..000000000000 --- a/contrib/pgcrypto/README.pgcrypto +++ /dev/null @@ -1,724 +0,0 @@ -pgcrypto - cryptographic functions for PostgreSQL -================================================= -Marko Kreen - -// Note: this document is in asciidoc format. - - -1. Installation ------------------ - -// Note: These installation instructions assume you have obtained a - precompiled build of pgcrypto from Greenplum. - -1. Unzip and untar the pgcrypto package. For example: - - unzip greenplum-pgcrypto--.tar.zip - mkdir pgcrypto - mv greenplum-pgcrypto--.tar pgcrypto - untar -xvf pgcryto/greenplum-pgcrypto--.tar - -2. Copy the pgcrypto.so file to your Greenplum installation's - library directory. - - cp pgcrypto/lib/postgresql/pgcrypto.so $GPHOME/lib/postgresql - -3. (optional) Edit the pgcrypto.sql file and edit the initial - SET command that specifies the schema where the pgcrypto - objects will be created. By default they will be created in - the 'public' schema. For example: - - vi pgcrypto/share/postgresql/contrib/pgcrypto.sql - - SET search_path = YOUR_SCHEMA; - -4. Run the commands in the pgcrypto.sql file using psql to - install the pgcrypto objects in your database. For example: - - psql -d DBNAME -f pgcrypto/share/postgresql/contrib/pgcrypto.sql - - -// Note: To uninstall the pgcrypto objects, use uninstall_pgcrypto.sql - - -2. Notes ----------- - -2.1. Configuration -~~~~~~~~~~~~~~~~~~~~ - -Greenplum's build of pgcrypto is compiled with zlib and -OpenSSL linked in. - -When compiled with zlib, PGP encryption functions are able to -compress data before encrypting. - -When compiled with OpenSSL there will be more algorithms available. -Also public-key encryption functions will be faster as OpenSSL -has more optimized BIGNUM functions. - -Summary of functionality with and without OpenSSL: - -`----------------------------`---------`------------ - Functionality built-in OpenSSL ----------------------------------------------------- - MD5 yes yes - SHA1 yes yes - SHA224/256/384/512 yes yes (3) - Any other digest algo no yes (1) - Blowfish yes yes - AES yes yes (2) - DES/3DES/CAST5 no yes - Raw encryption yes yes - PGP Symmetric encryption yes yes - PGP Public-Key encryption yes yes ----------------------------------------------------- - -1. Any digest algorithm OpenSSL supports is automatically picked up. - This is not possible with ciphers, which need to be supported - explicitly. - -2. AES is included in OpenSSL since version 0.9.7. If pgcrypto is - compiled against older version, it will use built-in AES code, - so it has AES always available. - -3. SHA2 algorithms were added to OpenSSL in version 0.9.8. For - older versions, pgcrypto will use built-in code. - - -2.2. NULL handling -~~~~~~~~~~~~~~~~~~~~ - -As standard in SQL, all functions return NULL, if any of the arguments -are NULL. This may create security risks on careless usage. - - -2.3. Security -~~~~~~~~~~~~~~~ - -All the functions here run inside database server. That means that all -the data and passwords move between pgcrypto and client application in -clear-text. Thus you must: - -1. Connect locally or use SSL connections. -2. Trust both system and database administrator. - -If you cannot, then better do crypto inside client application. - - -3. General hashing --------------------- - -3.1. digest(data, type) -~~~~~~~~~~~~~~~~~~~~~~~~~ - - digest(data text, type text) RETURNS bytea - digest(data bytea, type text) RETURNS bytea - -Type is here the algorithm to use. Standard algorithms are `md5` and -`sha1`, although there may be more supported, depending on build -options. - -Returns binary hash. - -If you want hexadecimal string, use `encode()` on result. Example: - - CREATE OR REPLACE FUNCTION sha1(bytea) RETURNS text AS $$ - SELECT encode(digest($1, 'sha1'), 'hex') - $$ LANGUAGE SQL STRICT IMMUTABLE; - - -3.2. hmac(data, key, type) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - hmac(data text, key text, type text) RETURNS bytea - hmac(data bytea, key text, type text) RETURNS bytea - -Calculates Hashed MAC over data. `type` is the same as in `digest()`. -If the key is larger than hash block size it will first hashed and the -hash will be used as key. - -It is similar to digest() but the hash can be recalculated only knowing -the key. This avoids the scenario of someone altering data and also -changing the hash. - -Returns binary hash. - - - -4. Password hashing ---------------------- - -The functions `crypt()` and `gen_salt()` are specifically designed -for hashing passwords. `crypt()` does the hashing and `gen_salt()` -prepares algorithm parameters for it. - -The algorithms in `crypt()` differ from usual hashing algorithms like -MD5 or SHA1 in following respects: - -1. They are slow. As the amount of data is so small, this is only - way to make brute-forcing passwords hard. -2. Include random 'salt' with result, so that users having same - password would have different crypted passwords. This is also - additional defense against reversing the algorithm. -3. Include algorithm type in the result, so passwords hashed with - different algorithms can co-exist. -4. Some of them are adaptive - that means after computers get - faster, you can tune the algorithm to be slower, without - introducing incompatibility with existing passwords. - -Supported algorithms: -`------`-------------`---------`----------`--------------------------- - Type Max password Adaptive Salt bits Description ----------------------------------------------------------------------- -`bf` 72 yes 128 Blowfish-based, variant 2a -`md5` unlimited no 48 md5-based crypt() -`xdes` 8 yes 24 Extended DES -`des` 8 no 12 Original UNIX crypt ----------------------------------------------------------------------- - - -4.1. crypt(password, salt) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - crypt(password text, salt text) RETURNS text - -Calculates UN*X crypt(3) style hash of password. When storing new -password, you need to use function `gen_salt()` to generate new salt. -When checking password you should use existing hash as salt. - -Example - setting new password: - - UPDATE .. SET pswhash = crypt('new password', gen_salt('md5')); - -Example - authentication: - - SELECT pswhash = crypt('entered password', pswhash) WHERE .. ; - -returns true or false whether the entered password is correct. -It also can return NULL if `pswhash` field is NULL. - - -4.2. gen_salt(type) -~~~~~~~~~~~~~~~~~~~~~ - - gen_salt(type text) RETURNS text - -Generates a new random salt for usage in `crypt()`. For adaptible -algorithms, it uses the default iteration count. - -Accepted types are: `des`, `xdes`, `md5` and `bf`. - - -4.3. gen_salt(type, rounds) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - gen_salt(type text, rounds integer) RETURNS text - -Same as above, but lets user specify iteration count for some -algorithms. The higher the count, the more time it takes to hash -the password and therefore the more time to break it. Although with -too high count the time to calculate a hash may be several years -- which is somewhat impractical. - -Number is algorithm specific: - -`-----'---------'-----'---------- - type default min max ---------------------------------- - `xdes` 725 1 16777215 - `bf` 6 4 31 ---------------------------------- - -In case of xdes there is a additional limitation that the count must be -a odd number. - -Notes: - -- Original DES crypt was designed to have the speed of 4 hashes per - second on the hardware of that time. -- Slower than 4 hashes per second would probably dampen usability. -- Faster than 100 hashes per second is probably too fast. -- See next section about possible values for `crypt-bf`. - - -4.4. Comparison of crypt and regular hashes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Here is a table that should give overview of relative slowness -of different hashing algorithms. - -* The goal is to crack a 8-character password, which consists: - 1. Only of lowercase letters - 2. Numbers, lower- and uppercase letters. -* The table below shows how much time it would take to try all - combinations of characters. -* The `crypt-bf` is featured in several settings - the number - after slash is the `rounds` parameter of `gen_salt()`. - -`------------'----------'--------------'-------------------- -Algorithm Hashes/sec Chars: [a-z] Chars: [A-Za-z0-9] ------------------------------------------------------------- -crypt-bf/8 28 246 years 251322 years -crypt-bf/7 57 121 years 123457 years -crypt-bf/6 112 62 years 62831 years -crypt-bf/5 211 33 years 33351 years -crypt-md5 2681 2.6 years 2625 years -crypt-des 362837 7 days 19 years -sha1 590223 4 days 12 years -md5 2345086 1 day 3 years ------------------------------------------------------------- - -* The machine used is 1.5GHz Pentium 4. -* crypt-des and crypt-md5 algorithm numbers are taken from - John the Ripper v1.6.38 `-test` output. -* MD5 numbers are from mdcrack 1.2. -* SHA1 numbers are from lcrack-20031130-beta. -* `crypt-bf` numbers are taken using simple program that loops - over 1000 8-character passwords. That way I can show the speed with - different number of rounds. For reference: `john -test` shows 213 - loops/sec for crypt-bf/5. (The small difference in results is in - accordance to the fact that the `crypt-bf` implementation in pgcrypto - is same one that is used in John the Ripper.) - -Note that "try all combinations" is not a realistic exercise. -Usually password cracking is done with the help of dictionaries, which -contain both regular words and various mutations of them. So, even -somewhat word-like passwords could be cracked much faster than the above -numbers suggest, and a 6-character non-word like password may escape -cracking. Or not. - - -5. PGP encryption -------------------- - -The functions here implement the encryption part of OpenPGP (RFC2440) -standard. Supported are both symmetric-key and public-key encryption. - - -5.1. Overview -~~~~~~~~~~~~~~~ - -Encrypted PGP message consists of 2 packets: - -- Packet for session key - either symmetric- or public-key encrypted. -- Packet for session-key encrypted data. - -When encrypting with password: - -1. Given password is hashed using String2Key (S2K) algorithm. This - is rather similar to `crypt()` algorithm - purposefully slow - and with random salt - but it produces a full-length binary key. -2. If separate session key is requested, new random key will be - generated. Otherwise S2K key will be used directly as session key. -3. If S2K key is to be used directly, then only S2K settings will be put - into session key packet. Otherwise session key will be encrypted with - S2K key and put into session key packet. - -When encrypting with public key: - -1. New random session key is generated. -2. It is encrypted using public key and put into session key packet. - -Now common part, the session-key encrypted data packet: - -1. Optional data-manipulation: compression, conversion to UTF-8, - conversion of line-endings. -2. Data is prefixed with block of random bytes. This is equal - to using random IV. -3. A SHA1 hash of random prefix and data is appended. -4. All this is encrypted with session key. - - -5.2. pgp_sym_encrypt(data, psw) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - pgp_sym_encrypt(data text, psw text [, options text] ) RETURNS bytea - pgp_sym_encrypt_bytea(data bytea, psw text [, options text] ) RETURNS bytea - -Return a symmetric-key encrypted PGP message. - -Options are described in section 5.8. - - -5.3. pgp_sym_decrypt(msg, psw) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - pgp_sym_decrypt(msg bytea, psw text [, options text] ) RETURNS text - pgp_sym_decrypt_bytea(msg bytea, psw text [, options text] ) RETURNS bytea - -Decrypt a symmetric-key encrypted PGP message. - -Decrypting bytea data with `pgp_sym_decrypt` is disallowed. -This is to avoid outputting invalid character data. Decrypting -originally textual data with `pgp_sym_decrypt_bytea` is fine. - -Options are described in section 5.8. - - -5.4. pgp_pub_encrypt(data, pub_key) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - pgp_pub_encrypt(data text, key bytea [, options text] ) RETURNS bytea - pgp_pub_encrypt_bytea(data bytea, key bytea [, options text] ) RETURNS bytea - -Encrypt data with a public key. Giving this function a secret key will -produce a error. - -Options are described in section 5.8. - - -5.5. pgp_pub_decrypt(msg, sec_key [, psw]) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text]] ) \ - RETURNS text - pgp_pub_decrypt_bytea(msg bytea, key bytea [,psw text [, options text]] ) \ - RETURNS bytea - -Decrypt a public-key encrypted message with secret key. If the secret -key is password-protected, you must give the password in `psw`. If -there is no password, but you want to specify option for function, you -need to give empty password. - -Decrypting bytea data with `pgp_pub_decrypt` is disallowed. -This is to avoid outputting invalid character data. Decrypting -originally textual data with `pgp_pub_decrypt_bytea` is fine. - -Options are described in section 5.8. - - -5.6. pgp_key_id(key / msg) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - pgp_key_id(key or msg bytea) RETURNS text - -It shows you either key ID if given PGP public or secret key. Or it -gives the key ID that was used for encrypting the data, if given -encrypted message. - -It can return 2 special key IDs: - -SYMKEY:: - The data is encrypted with symmetric key. - -ANYKEY:: - The data is public-key encrypted, but the key ID is cleared. - That means you need to try all your secret keys on it to see - which one decrypts it. pgcrypto itself does not produce such - messages. - -Note that different keys may have same ID. This is rare but normal -event. Client application should then try to decrypt with each one, -to see which fits - like handling ANYKEY. - - -5.7. armor / dearmor -~~~~~~~~~~~~~~~~~~~~~~ - - armor(data bytea) RETURNS text - dearmor(data text) RETURNS bytea - -Those wrap/unwrap data into PGP Ascii Armor which is basically Base64 -with CRC and additional formatting. - - -5.8. Options for PGP functions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Options are named to be similar to GnuPG. Values should be given after -an equal sign; separate options from each other with commas. Example: - - pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256') - -All of the options except `convert-crlf` apply only to encrypt -functions. Decrypt functions get the parameters from PGP data. - -Most interesting options are probably `compression-algo` and -`unicode-mode`. The rest should have reasonable defaults. - - -cipher-algo:: - What cipher algorithm to use. - - Values: bf, aes128, aes192, aes256 (OpenSSL-only: `3des`, `cast5`) - Default: aes128 - Applies: pgp_sym_encrypt, pgp_pub_encrypt - -compress-algo:: - Which compression algorithm to use. Needs building with zlib. - - Values: - 0 - no compression - 1 - ZIP compression - 2 - ZLIB compression [=ZIP plus meta-data and block-CRC's] - Default: 0 - Applies: pgp_sym_encrypt, pgp_pub_encrypt - -compress-level:: - How much to compress. Bigger level compresses smaller but is slower. - 0 disables compression. - - Values: 0, 1-9 - Default: 6 - Applies: pgp_sym_encrypt, pgp_pub_encrypt - -convert-crlf:: - Whether to convert `\n` into `\r\n` when encrypting and `\r\n` to `\n` - when decrypting. RFC2440 specifies that text data should be stored - using `\r\n` line-feeds. Use this to get fully RFC-compliant - behavior. - - Values: 0, 1 - Default: 0 - Applies: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt - -disable-mdc:: - Do not protect data with SHA-1. Only good reason to use this - option is to achieve compatibility with ancient PGP products, as the - SHA-1 protected packet is from upcoming update to RFC2440. (Currently - at version RFC2440bis-14.) Recent gnupg.org and pgp.com software - supports it fine. - - Values: 0, 1 - Default: 0 - Applies: pgp_sym_encrypt, pgp_pub_encrypt - -enable-session-key:: - Use separate session key. Public-key encryption always uses separate - session key, this is for symmetric-key encryption, which by default - uses S2K directly. - - Values: 0, 1 - Default: 0 - Applies: pgp_sym_encrypt - -s2k-mode:: - Which S2K algorithm to use. - - Values: - 0 - Without salt. Dangerous! - 1 - With salt but with fixed iteration count. - 3 - Variable iteration count. - Default: 3 - Applies: pgp_sym_encrypt - -s2k-digest-algo:: - Which digest algorithm to use in S2K calculation. - - Values: md5, sha1 - Default: sha1 - Applies: pgp_sym_encrypt - -s2k-cipher-algo:: - Which cipher to use for encrypting separate session key. - - Values: bf, aes, aes128, aes192, aes256 - Default: use cipher-algo. - Applies: pgp_sym_encrypt - -unicode-mode:: - Whether to convert textual data from database internal encoding to - UTF-8 and back. If your database already is UTF-8, no conversion will - be done, only the data will be tagged as UTF-8. Without this option - it will not be. - - Values: 0, 1 - Default: 0 - Applies: pgp_sym_encrypt, pgp_pub_encrypt - - -5.9. Generating keys with GnuPG -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Generate a new key: - - gpg --gen-key - -The preferred key type is "DSA and Elgamal". - -For RSA encryption you must create either DSA or RSA sign-only key -as master and then add RSA encryption subkey with `gpg --edit-key`. - -List keys: - - gpg --list-secret-keys - -Export ascii-armored public key: - - gpg -a --export KEYID > public.key - -Export ascii-armored secret key: - - gpg -a --export-secret-keys KEYID > secret.key - -You need to use `dearmor()` on them before giving them to -pgp_pub_* functions. Or if you can handle binary data, you can drop -"-a" from gpg. - -For more details see `man gpg`, http://www.gnupg.org/gph/en/manual.html[ -The GNU Privacy Handbook] and other docs on http://www.gnupg.org[] site. - - -5.10. Limitations of PGP code -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- No support for signing. That also means that it is not checked - whether the encryption subkey belongs to master key. - -- No support for encryption key as master key. As such practice - is generally discouraged, it should not be a problem. - -- No support for several subkeys. This may seem like a problem, as this - is common practice. On the other hand, you should not use your regular - GPG/PGP keys with pgcrypto, but create new ones, as the usage scenario - is rather different. - - -6. Raw encryption -------------------- - -Those functions only run a cipher over data, they don't have any advanced -features of PGP encryption. Therefore they have some major problems: - -1. They use user key directly as cipher key. -2. They don't provide any integrity checking, to see - if the encrypted data was modified. -3. They expect that users manage all encryption parameters - themselves, even IV. -4. They don't handle text. - -So, with the introduction of PGP encryption, usage of raw -encryption functions is discouraged. - - - encrypt(data bytea, key bytea, type text) RETURNS bytea - decrypt(data bytea, key bytea, type text) RETURNS bytea - - encrypt_iv(data bytea, key bytea, iv bytea, type text) RETURNS bytea - decrypt_iv(data bytea, key bytea, iv bytea, type text) RETURNS bytea - -Encrypt/decrypt data with cipher, padding data if needed. - -`type` parameter description in pseudo-noteup: - - algo ['-' mode] ['/pad:' padding] - -Supported algorithms: - -* `bf` - Blowfish -* `aes` - AES (Rijndael-128) - -Modes: - -* `cbc` - next block depends on previous. (default) -* `ecb` - each block is encrypted separately. - (for testing only) - -Padding: - -* `pkcs` - data may be any length (default) -* `none` - data must be multiple of cipher block size. - -IV is initial value for mode, defaults to all zeroes. It is ignored for -ECB. It is clipped or padded with zeroes if not exactly block size. - -So, example: - - encrypt(data, 'fooz', 'bf') - -is equal to - - encrypt(data, 'fooz', 'bf-cbc/pad:pkcs') - - -7. Random bytes ------------------ - - gen_random_bytes(count integer) - -Returns `count` cryptographically strong random bytes as bytea value. -There can be maximally 1024 bytes extracted at a time. This is to avoid -draining the randomness generator pool. - - -8. Credits ------------- - -I have used code from following sources: - -`--------------------`-------------------------`------------------------------- - Algorithm Author Source origin -------------------------------------------------------------------------------- - DES crypt() David Burren and others FreeBSD libcrypt - MD5 crypt() Poul-Henning Kamp FreeBSD libcrypt - Blowfish crypt() Solar Designer www.openwall.com - Blowfish cipher Niels Provos OpenBSD sys/crypto - Rijndael cipher Brian Gladman OpenBSD sys/crypto - MD5 and SHA1 WIDE Project KAME kame/sys/crypto - SHA256/384/512 Aaron D. Gifford OpenBSD sys/crypto - BIGNUM math Michael J. Fromberger dartmouth.edu/~sting/sw/imath -------------------------------------------------------------------------------- - - -9. Legalese -------------- - -* I owe a beer to Poul-Henning. -* This product includes software developed by Niels Provos. - - -10. References/Links ----------------------- - -10.1. Useful reading -~~~~~~~~~~~~~~~~~~~~~~ - -http://www.gnupg.org/gph/en/manual.html[]:: - The GNU Privacy Handbook - -http://www.openwall.com/crypt/[]:: - Describes the crypt-blowfish algorithm. - -http://www.stack.nl/~galactus/remailers/passphrase-faq.html[]:: - How to choose good password. - -http://world.std.com/~reinhold/diceware.html[]:: - Interesting idea for picking passwords. - -http://www.interhack.net/people/cmcurtin/snake-oil-faq.html[]:: - Describes good and bad cryptography. - - -10.2. Technical references -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -http://www.ietf.org/rfc/rfc2440.txt[]:: - OpenPGP message format - -http://www.imc.org/draft-ietf-openpgp-rfc2440bis[]:: - New version of RFC2440. - -http://www.ietf.org/rfc/rfc1321.txt[]:: - The MD5 Message-Digest Algorithm - -http://www.ietf.org/rfc/rfc2104.txt[]:: - HMAC: Keyed-Hashing for Message Authentication - -http://www.usenix.org/events/usenix99/provos.html[]:: - Comparison of crypt-des, crypt-md5 and bcrypt algorithms. - -http://csrc.nist.gov/cryptval/des.htm[]:: - Standards for DES, 3DES and AES. - -http://en.wikipedia.org/wiki/Fortuna_(PRNG)[]:: - Description of Fortuna CSPRNG. - -http://jlcooke.ca/random/[]:: - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux. - -http://www.cs.ut.ee/~helger/crypto/[]:: - Collection of cryptology pointers. - - -// $PostgreSQL: pgsql/contrib/pgcrypto/README.pgcrypto,v 1.18 2006/09/05 21:26:48 tgl Exp $ diff --git a/contrib/pgcrypto/expected/init.out b/contrib/pgcrypto/expected/init.out index 649f9e8e49b4..86c1fe225793 100755 --- a/contrib/pgcrypto/expected/init.out +++ b/contrib/pgcrypto/expected/init.out @@ -1,7 +1,13 @@ -- -- init pgcrypto -- +-- +-- first, define the functions. Turn off echoing so that expected file +-- does not depend on contents of pgcrypto.sql. +-- +SET client_min_messages = warning; \set ECHO none +RESET client_min_messages; -- check for encoding fn's SELECT encode('foo', 'hex'); encode diff --git a/contrib/pgcrypto/imath.h b/contrib/pgcrypto/imath.h index 09d0e3e818b0..158171c0acd8 100644 --- a/contrib/pgcrypto/imath.h +++ b/contrib/pgcrypto/imath.h @@ -118,10 +118,10 @@ mp_result mp_int_mul_pow2(mp_int a, int p2, mp_int c); mp_result mp_int_sqr(mp_int a, mp_int c); /* c = a * a */ mp_result -mp_int_div(mp_int a, mp_int b, /* q = a / b */ +mp_int_div(mp_int a, mp_int b, /* q = a / b */ mp_int q, mp_int r); /* r = a % b */ mp_result -mp_int_div_value(mp_int a, int value, /* q = a / value */ +mp_int_div_value(mp_int a, int value, /* q = a / value */ mp_int q, int *r); /* r = a % value */ mp_result mp_int_div_pow2(mp_int a, int p2, /* q = a / 2^p2 */ diff --git a/contrib/pgcrypto/pgcrypto.c b/contrib/pgcrypto/pgcrypto.c index bf882de6e8b8..8da2ae77897e 100644 --- a/contrib/pgcrypto/pgcrypto.c +++ b/contrib/pgcrypto/pgcrypto.c @@ -380,8 +380,8 @@ pg_encrypt_iv(PG_FUNCTION_ARGS) err = px_combo_init(c, (uint8 *) VARDATA(key), klen, (uint8 *) VARDATA(iv), ivlen); if (!err) - px_combo_encrypt(c, (uint8 *) VARDATA(data), dlen, - (uint8 *) VARDATA(res), &rlen); + err = px_combo_encrypt(c, (uint8 *) VARDATA(data), dlen, + (uint8 *) VARDATA(res), &rlen); px_combo_free(c); @@ -434,8 +434,8 @@ pg_decrypt_iv(PG_FUNCTION_ARGS) err = px_combo_init(c, (uint8 *) VARDATA(key), klen, (uint8 *) VARDATA(iv), ivlen); if (!err) - px_combo_decrypt(c, (uint8 *) VARDATA(data), dlen, - (uint8 *) VARDATA(res), &rlen); + err = px_combo_decrypt(c, (uint8 *) VARDATA(data), dlen, + (uint8 *) VARDATA(res), &rlen); px_combo_free(c); diff --git a/contrib/pgcrypto/sql/init.sql b/contrib/pgcrypto/sql/init.sql index d11d1314d75f..4e404c9e1fcb 100644 --- a/contrib/pgcrypto/sql/init.sql +++ b/contrib/pgcrypto/sql/init.sql @@ -2,9 +2,15 @@ -- init pgcrypto -- +-- +-- first, define the functions. Turn off echoing so that expected file +-- does not depend on contents of pgcrypto.sql. +-- +SET client_min_messages = warning; \set ECHO none \i pgcrypto.sql \set ECHO all +RESET client_min_messages; -- check for encoding fn's SELECT encode('foo', 'hex'); diff --git a/contrib/pgrowlocks/README.pgrowlocks b/contrib/pgrowlocks/README.pgrowlocks deleted file mode 100755 index ebaea4ca5a3b..000000000000 --- a/contrib/pgrowlocks/README.pgrowlocks +++ /dev/null @@ -1,98 +0,0 @@ -$PostgreSQL: pgsql/contrib/pgrowlocks/README.pgrowlocks,v 1.1 2006/04/23 01:12:58 ishii Exp $ - -pgrowlocks README Tatsuo Ishii - -1. What is pgrowlocks? - - pgrowlocks shows row locking information for specified table. - - pgrowlocks returns following data type: - -CREATE TYPE pgrowlocks_type AS ( - locked_row TID, -- row TID - lock_type TEXT, -- lock type - locker XID, -- locking XID - multi bool, -- multi XID? - xids xid[], -- multi XIDs - pids INTEGER[] -- locker's process id -); - - Here is a sample execution of pgrowlocks: - -test=# SELECT * FROM pgrowlocks('t1'); - locked_row | lock_type | locker | multi | xids | pids -------------+-----------+--------+-------+-----------+--------------- - (0,1) | Shared | 19 | t | {804,805} | {29066,29068} - (0,2) | Shared | 19 | t | {804,805} | {29066,29068} - (0,3) | Exclusive | 804 | f | {804} | {29066} - (0,4) | Exclusive | 804 | f | {804} | {29066} -(4 rows) - - locked_row -- tuple ID(TID) of each locked rows - lock_type -- "Shared" for shared lock, "Exclusive" for exclusive lock - locker -- transaction ID of locker (note 1) - multi -- "t" if locker is a multi transaction, otherwise "f" - xids -- XIDs of lockers (note 2) - pids -- process ids of locking backends - - note1: if the locker is multi transaction, it represents the multi ID - - note2: if the locker is multi, multiple data are shown - -2. Installing pgrowlocks - - Installing pgrowlocks requires PostgreSQL 8.0 or later source tree. - - $ cd /usr/local/src/postgresql-8.1/contrib - $ tar xfz /tmp/pgrowlocks-1.0.tar.gz - - If you are using PostgreSQL 8.0, you need to modify pgrowlocks source code. - Around line 61, you will see: - - #undef MAKERANGEVARFROMNAMELIST_HAS_TWO_ARGS - - change this to: - - #define MAKERANGEVARFROMNAMELIST_HAS_TWO_ARGS - - $ make - $ make install - - $ psql -e -f pgrowlocks.sql test - -3. How to use pgrowlocks - - The calling sequence for pgrowlocks is as follows: - - CREATE OR REPLACE FUNCTION pgrowlocks(text) RETURNS pgrowlocks_type - AS 'MODULE_PATHNAME', 'pgrowlocks' - LANGUAGE 'c' WITH (isstrict); - - The parameter is a name of table. pgrowlocks returns type pgrowlocks_type. - - pgrowlocks grab AccessShareLock for the target table and read each - row one by one to get the row locking information. You should - notice that: - - 1) if the table is exclusive locked by someone else, pgrowlocks - will be blocked. - - 2) pgrowlocks may show incorrect information if there's a new - lock or a lock is freeed while its execution. - - pgrowlocks does not show the contents of locked rows. If you want - to take a look at the row contents at the same time, you could do - something like this: - - SELECT * FROM accounts AS a, pgrowlocks('accounts') AS p WHERE p.locked_ row = a.ctid; - - -4. License - - pgrowlocks is distribute under (modified) BSD license described in - the source file. - -5. History - - 2006/03/21 pgrowlocks version 1.1 released (tested on 8.2 current) - 2005/08/22 pgrowlocks version 1.0 released diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c index 6dae0d8af9bf..9885ef0c66b6 100644 --- a/contrib/pgrowlocks/pgrowlocks.c +++ b/contrib/pgrowlocks/pgrowlocks.c @@ -121,7 +121,7 @@ pgrowlocks(PG_FUNCTION_ARGS) LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); if (HeapTupleSatisfiesUpdate(rel, tuple->t_data, - GetCurrentCommandId(/*false*/), + GetCurrentCommandId(false), scan->rs_cbuf) == HeapTupleBeingUpdated) { diff --git a/contrib/pgstattuple/.gitignore b/contrib/pgstattuple/.gitignore new file mode 100644 index 000000000000..69b22b64cd92 --- /dev/null +++ b/contrib/pgstattuple/.gitignore @@ -0,0 +1 @@ +/pgstattuple.sql diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c index eb6bb00386d2..45e14dde1c0c 100644 --- a/contrib/pgstattuple/pgstatindex.c +++ b/contrib/pgstattuple/pgstatindex.c @@ -24,84 +24,24 @@ #include "postgres.h" -#include "fmgr.h" -#include "funcapi.h" #include "access/heapam.h" -#include "access/itup.h" #include "access/nbtree.h" -#include "access/transam.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" +#include "funcapi.h" #include "miscadmin.h" #include "utils/builtins.h" -#include "utils/inval.h" -PG_FUNCTION_INFO_V1(pgstatindex); -PG_FUNCTION_INFO_V1(bt_metap); -PG_FUNCTION_INFO_V1(bt_page_items); -PG_FUNCTION_INFO_V1(bt_page_stats); -PG_FUNCTION_INFO_V1(pg_relpages); extern Datum pgstatindex(PG_FUNCTION_ARGS); -extern Datum bt_metap(PG_FUNCTION_ARGS); -extern Datum bt_page_items(PG_FUNCTION_ARGS); -extern Datum bt_page_stats(PG_FUNCTION_ARGS); extern Datum pg_relpages(PG_FUNCTION_ARGS); -#define PGSTATINDEX_TYPE "public.pgstatindex_type" -#define PGSTATINDEX_NCOLUMNS 10 - -#define BTMETAP_TYPE "public.bt_metap_type" -#define BTMETAP_NCOLUMNS 6 - -#define BTPAGEITEMS_TYPE "public.bt_page_items_type" -#define BTPAGEITEMS_NCOLUMNS 6 - -#define BTPAGESTATS_TYPE "public.bt_page_stats_type" -#define BTPAGESTATS_NCOLUMNS 11 - +PG_FUNCTION_INFO_V1(pgstatindex); +PG_FUNCTION_INFO_V1(pg_relpages); -#define IS_INDEX(r) ((r)->rd_rel->relkind == 'i') +#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX) #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID) -#define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \ - if ( !(FirstOffsetNumber <= (offnum) && \ - (offnum) <= PageGetMaxOffsetNumber(pg)) ) \ - elog(ERROR, "page offset number out of range"); } - -/* note: BlockNumber is unsigned, hence can't be negative */ -#define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \ - if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \ - elog(ERROR, "block number out of range"); } - -/* ------------------------------------------------ - * structure for single btree page statistics - * ------------------------------------------------ - */ -typedef struct BTPageStat -{ - uint32 blkno; - uint32 live_items; - uint32 dead_items; - uint32 page_size; - uint32 max_avail; - uint32 free_size; - uint32 avg_item_size; - uint32 fragments; - char type; - - /* opaque data */ - BlockNumber btpo_prev; - BlockNumber btpo_next; - union - { - uint32 level; - TransactionId xact; - } btpo; - uint16 btpo_flags; - BTCycleId btpo_cycleid; -} BTPageStat; - /* ------------------------------------------------ * A structure for a whole btree index statistics * used by pgstatindex(). @@ -109,120 +49,22 @@ typedef struct BTPageStat */ typedef struct BTIndexStat { - uint32 magic; uint32 version; BlockNumber root_blkno; uint32 level; - BlockNumber fastroot; - uint32 fastlevel; - - uint32 live_items; - uint32 dead_items; - uint32 root_pages; uint32 internal_pages; uint32 leaf_pages; uint32 empty_pages; uint32 deleted_pages; - uint32 page_size; - uint32 avg_item_size; - uint32 max_avail; uint32 free_space; uint32 fragments; } BTIndexStat; -/* ------------------------------------------------- - * GetBTPageStatistics() - * - * Collect statistics of single b-tree leaf page - * ------------------------------------------------- - */ -static bool -GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat * stat) -{ - Page page = BufferGetPage(buffer); - PageHeader phdr = (PageHeader) page; - OffsetNumber maxoff = PageGetMaxOffsetNumber(page); - BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page); - int item_size = 0; - int off; - - stat->blkno = blkno; - - stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData); - - stat->dead_items = stat->live_items = 0; - - stat->page_size = PageGetPageSize(page); - - /* page type (flags) */ - if (P_ISDELETED(opaque)) - { - stat->type = 'd'; - return true; - } - else if (P_IGNORE(opaque)) - stat->type = 'e'; - else if (P_ISLEAF(opaque)) - stat->type = 'l'; - else if (P_ISROOT(opaque)) - stat->type = 'r'; - else - stat->type = 'i'; - - /* btpage opaque data */ - stat->btpo_prev = opaque->btpo_prev; - stat->btpo_next = opaque->btpo_next; - if (P_ISDELETED(opaque)) - stat->btpo.xact = opaque->btpo.xact; - else - stat->btpo.level = opaque->btpo.level; - stat->btpo_flags = opaque->btpo_flags; - stat->btpo_cycleid = opaque->btpo_cycleid; - - /*---------------------------------------------- - * If a next leaf is on the previous block, - * it means a fragmentation. - *---------------------------------------------- - */ - stat->fragments = 0; - if (stat->type == 'l') - { - if (opaque->btpo_next != P_NONE && opaque->btpo_next < blkno) - stat->fragments++; - } - - /* count live and dead tuples, and free space */ - for (off = FirstOffsetNumber; off <= maxoff; off++) - { - IndexTuple itup; - - ItemId id = PageGetItemId(page, off); - - itup = (IndexTuple) PageGetItem(page, id); - - item_size += IndexTupleSize(itup); - - if (!ItemIdDeleted(id)) - stat->live_items++; - else - stat->dead_items++; - } - stat->free_size = PageGetFreeSpace(page); - - if ((stat->live_items + stat->dead_items) > 0) - stat->avg_item_size = item_size / (stat->live_items + stat->dead_items); - else - stat->avg_item_size = 0; - - return true; -} - - /* ------------------------------------------------------ * pgstatindex() * @@ -249,23 +91,30 @@ pgstatindex(PG_FUNCTION_ARGS) rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) - elog(ERROR, "pgstatindex() can be used only on b-tree index."); + elog(ERROR, "relation \"%s\" is not a btree index", + RelationGetRelationName(rel)); + + /* + * Reject attempts to read non-local temporary relations; we would + * be likely to get wrong data since we have no visibility into the + * owning session's local buffers. + */ + if (isOtherTempNamespace(RelationGetNamespace(rel))) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"))); - /*------------------- - * Read a metapage - *------------------- + /* + * Read metapage */ { Buffer buffer = ReadBuffer(rel, 0); Page page = BufferGetPage(buffer); BTMetaPageData *metad = BTPageGetMeta(page); - indexStat.magic = metad->btm_magic; indexStat.version = metad->btm_version; indexStat.root_blkno = metad->btm_root; indexStat.level = metad->btm_level; - indexStat.fastroot = metad->btm_fastroot; - indexStat.fastlevel = metad->btm_fastlevel; ReleaseBuffer(buffer); } @@ -290,47 +139,52 @@ pgstatindex(PG_FUNCTION_ARGS) */ for (blkno = 1; blkno < nblocks; blkno++) { - Buffer buffer = ReadBuffer(rel, blkno); - BTPageStat stat; - - /* scan one page */ - stat.blkno = blkno; - GetBTPageStatistics(blkno, buffer, &stat); - - /*--------------------- - * page status (type) - *--------------------- - */ - switch (stat.type) - { - case 'd': - indexStat.deleted_pages++; - break; - case 'l': - indexStat.leaf_pages++; - break; - case 'i': - indexStat.internal_pages++; - break; - case 'e': - indexStat.empty_pages++; - break; - case 'r': - indexStat.root_pages++; - break; - default: - elog(ERROR, "unknown page status."); - } + Buffer buffer; + Page page; + BTPageOpaque opaque; + + CHECK_FOR_INTERRUPTS(); + + /* Read and lock buffer */ + buffer = ReadBuffer(rel, blkno); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + page = BufferGetPage(buffer); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); - /* -- leaf fragmentation -- */ - indexStat.fragments += stat.fragments; + /* Determine page type, and update totals */ - if (stat.type == 'l') + if (P_ISDELETED(opaque)) + indexStat.deleted_pages++; + + else if (P_IGNORE(opaque)) + indexStat.empty_pages++; + + else if (P_ISLEAF(opaque)) { - indexStat.max_avail += stat.max_avail; - indexStat.free_space += stat.free_size; + int max_avail; + + max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData); + indexStat.max_avail += max_avail; + indexStat.free_space += PageGetFreeSpace(page); + + indexStat.leaf_pages++; + + /* + * If the next leaf is on an earlier block, it means a + * fragmentation. + */ + if (opaque->btpo_next != P_NONE && opaque->btpo_next < blkno) + indexStat.fragments++; } + else if (P_ISROOT(opaque)) + indexStat.root_pages++; + + else + indexStat.internal_pages++; + /* Unlock and release buffer */ + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); } @@ -343,12 +197,12 @@ pgstatindex(PG_FUNCTION_ARGS) { TupleDesc tupleDesc; int j; - char *values[PGSTATINDEX_NCOLUMNS]; - - HeapTupleData tupleData; - HeapTuple tuple = &tupleData; + char *values[10]; + HeapTuple tuple; - tupleDesc = RelationNameGetTupleDesc(PGSTATINDEX_TYPE); + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); j = 0; values[j] = palloc(32); @@ -372,337 +226,32 @@ pgstatindex(PG_FUNCTION_ARGS) values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.deleted_pages); values[j] = palloc(32); - snprintf(values[j++], 32, "%.2f", 100.0 - (float) indexStat.free_space / (float) indexStat.max_avail * 100.0); - values[j] = palloc(32); - snprintf(values[j++], 32, "%.2f", (float) indexStat.fragments / (float) indexStat.leaf_pages * 100.0); - - tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), - values); - - result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple); - } - - PG_RETURN_DATUM(result); -} -/* ----------------------------------------------- - * bt_page() - * - * Usage: SELECT * FROM bt_page('t1_pkey', 0); - * ----------------------------------------------- - */ -Datum -bt_page_stats(PG_FUNCTION_ARGS) -{ - text *relname = PG_GETARG_TEXT_P(0); - uint32 blkno = PG_GETARG_UINT32(1); - Buffer buffer; - - Relation rel; - RangeVar *relrv; - Datum result; - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to use pgstattuple functions")))); - - relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); - rel = relation_openrv(relrv, AccessShareLock); - - CHECK_RELATION_BLOCK_RANGE(rel, blkno); - - buffer = ReadBuffer(rel, blkno); - - if (!IS_INDEX(rel) || !IS_BTREE(rel)) - elog(ERROR, "bt_page_stats() can be used only on b-tree index."); - - if (blkno == 0) - elog(ERROR, "Block 0 is a meta page."); - - { - HeapTuple tuple; - TupleDesc tupleDesc; - int j; - char *values[BTPAGESTATS_NCOLUMNS]; - - BTPageStat stat; - - GetBTPageStatistics(blkno, buffer, &stat); - - tupleDesc = RelationNameGetTupleDesc(BTPAGESTATS_TYPE); - - j = 0; - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.blkno); - - values[j] = palloc(32); - snprintf(values[j++], 32, "%c", stat.type); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.live_items); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.dead_items); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.avg_item_size); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.page_size); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.free_size); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.btpo_prev); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.btpo_next); - - values[j] = palloc(32); - if (stat.type == 'd') - snprintf(values[j++], 32, "%d", stat.btpo.xact); + if (indexStat.max_avail > 0) + snprintf(values[j++], 32, "%.2f", + 100.0 - (double) indexStat.free_space / (double) indexStat.max_avail * 100.0); else - snprintf(values[j++], 32, "%d", stat.btpo.level); - + snprintf(values[j++], 32, "NaN"); values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.btpo_flags); - - tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), - values); - - result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple); - } - - ReleaseBuffer(buffer); - - relation_close(rel, AccessShareLock); - - PG_RETURN_DATUM(result); -} - -/*------------------------------------------------------- - * bt_page_items() - * - * Get IndexTupleData set in a leaf page - * - * Usage: SELECT * FROM bt_page_items('t1_pkey', 0); - *------------------------------------------------------- - */ -/* --------------------------------------------------- - * data structure for SRF to hold a scan information - * --------------------------------------------------- - */ -struct user_args -{ - TupleDesc tupd; - Relation rel; - Buffer buffer; - Page page; - uint16 offset; -}; - -Datum -bt_page_items(PG_FUNCTION_ARGS) -{ - text *relname = PG_GETARG_TEXT_P(0); - uint32 blkno = PG_GETARG_UINT32(1); - - RangeVar *relrv; - Datum result; - char *values[BTPAGEITEMS_NCOLUMNS]; - BTPageOpaque opaque; - HeapTuple tuple; - ItemId id; - - FuncCallContext *fctx; - MemoryContext mctx; - struct user_args *uargs = NULL; - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to use pgstattuple functions")))); - - if (blkno == 0) - elog(ERROR, "Block 0 is a meta page."); - - if (SRF_IS_FIRSTCALL()) - { - fctx = SRF_FIRSTCALL_INIT(); - mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); - - uargs = palloc(sizeof(struct user_args)); - - uargs->tupd = RelationNameGetTupleDesc(BTPAGEITEMS_TYPE); - uargs->offset = FirstOffsetNumber; - - relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); - uargs->rel = relation_openrv(relrv, AccessShareLock); - - CHECK_RELATION_BLOCK_RANGE(uargs->rel, blkno); - - uargs->buffer = ReadBuffer(uargs->rel, blkno); - - if (!IS_INDEX(uargs->rel) || !IS_BTREE(uargs->rel)) - elog(ERROR, "bt_page_items() can be used only on b-tree index."); - - uargs->page = BufferGetPage(uargs->buffer); - - opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); - - if (P_ISDELETED(opaque)) - elog(NOTICE, "bt_page_items(): this page is deleted."); - - fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); - fctx->user_fctx = uargs; - - MemoryContextSwitchTo(mctx); - } - - fctx = SRF_PERCALL_SETUP(); - uargs = fctx->user_fctx; - - if (fctx->call_cntr < fctx->max_calls) - { - IndexTuple itup; - - id = PageGetItemId(uargs->page, uargs->offset); - - if (!ItemIdIsValid(id)) - elog(ERROR, "Invalid ItemId."); - - itup = (IndexTuple) PageGetItem(uargs->page, id); - - { - int j = 0; - - BlockNumber blkno = BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)); - - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", uargs->offset); - values[j] = palloc(32); - snprintf(values[j++], 32, "(%u,%u)", blkno, itup->t_tid.ip_posid); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup)); - values[j] = palloc(32); - snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f'); - values[j] = palloc(32); - snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); - - { - int off; - char *dump; - char *ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); - - dump = palloc(IndexTupleSize(itup) * 3); - memset(dump, 0, IndexTupleSize(itup) * 3); - - for (off = 0; - off < IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); - off++) - { - if (dump[0] == '\0') - sprintf(dump, "%02x", *(ptr + off) & 0xff); - else - { - char buf[4]; - - sprintf(buf, " %02x", *(ptr + off) & 0xff); - strcat(dump, buf); - } - } - values[j] = dump; - } - - tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(uargs->tupd), values); - result = TupleGetDatum(TupleDescGetSlot(uargs->tupd), tuple); - } - - uargs->offset = uargs->offset + 1; - - SRF_RETURN_NEXT(fctx, result); - } - else - { - ReleaseBuffer(uargs->buffer); - relation_close(uargs->rel, AccessShareLock); - - SRF_RETURN_DONE(fctx); - } -} - - -/* ------------------------------------------------ - * bt_metap() - * - * Get a btree meta-page information - * - * Usage: SELECT * FROM bt_metap('t1_pkey') - * ------------------------------------------------ - */ -Datum -bt_metap(PG_FUNCTION_ARGS) -{ - text *relname = PG_GETARG_TEXT_P(0); - Buffer buffer; - - Relation rel; - RangeVar *relrv; - Datum result; - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to use pgstattuple functions")))); - - relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); - rel = relation_openrv(relrv, AccessShareLock); - - if (!IS_INDEX(rel) || !IS_BTREE(rel)) - elog(ERROR, "bt_metap() can be used only on b-tree index."); - - buffer = ReadBuffer(rel, 0); - - { - BTMetaPageData *metad; - - TupleDesc tupleDesc; - int j; - char *values[BTMETAP_NCOLUMNS]; - HeapTuple tuple; - - Page page = BufferGetPage(buffer); - - metad = BTPageGetMeta(page); - - tupleDesc = RelationNameGetTupleDesc(BTMETAP_TYPE); - - j = 0; - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", metad->btm_magic); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", metad->btm_version); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", metad->btm_root); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", metad->btm_level); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", metad->btm_fastroot); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", metad->btm_fastlevel); + if (indexStat.leaf_pages > 0) + snprintf(values[j++], 32, "%.2f", + (double) indexStat.fragments / (double) indexStat.leaf_pages * 100.0); + else + snprintf(values[j++], 32, "NaN"); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); - result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple); + result = HeapTupleGetDatum(tuple); } - ReleaseBuffer(buffer); - - relation_close(rel, AccessShareLock); - PG_RETURN_DATUM(result); } /* -------------------------------------------------------- * pg_relpages() * - * Get a number of pages of the table/index. + * Get the number of pages of the table/index. * * Usage: SELECT pg_relpages('t1'); * SELECT pg_relpages('t1_pkey'); @@ -712,7 +261,6 @@ Datum pg_relpages(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); - Relation rel; RangeVar *relrv; int4 relpages; @@ -725,6 +273,8 @@ pg_relpages(PG_FUNCTION_ARGS) relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); + /* note: this will work OK on non-local temp tables */ + relpages = RelationGetNumberOfBlocks(rel); relation_close(rel, AccessShareLock); diff --git a/contrib/pgstattuple/uninstall_pgstattuple.sql b/contrib/pgstattuple/uninstall_pgstattuple.sql index 5b857bb868f1..ae0ae90295f8 100644 --- a/contrib/pgstattuple/uninstall_pgstattuple.sql +++ b/contrib/pgstattuple/uninstall_pgstattuple.sql @@ -1,20 +1,9 @@ --- Adjust this setting to control where the objects get created. +/* $PostgreSQL: pgsql/contrib/pgstattuple/uninstall_pgstattuple.sql,v 1.6 2007/11/13 04:24:28 momjian Exp $ */ + +-- Adjust this setting to control where the objects get dropped. SET search_path = public; -DROP FUNCTION pgstattuple(oid); DROP FUNCTION pgstattuple(text); -DROP TYPE pgstattuple_type; - +DROP FUNCTION pgstattuple(oid); DROP FUNCTION pgstatindex(text); -DROP TYPE pgstatindex_type; - -DROP FUNCTION bt_metap(text); -DROP TYPE bt_metap_type; - -DROP FUNCTION bt_page_stats(text, int4); -DROP TYPE bt_page_stats_type; - -DROP FUNCTION bt_page_items(text, int4); -DROP TYPE bt_page_items_type; - DROP FUNCTION pg_relpages(text); diff --git a/contrib/seg/.gitignore b/contrib/seg/.gitignore new file mode 100644 index 000000000000..7331698e7719 --- /dev/null +++ b/contrib/seg/.gitignore @@ -0,0 +1,6 @@ +/segparse.c +/segparse.h +/segscan.c +/seg.sql +# Generated subdirectories +/results/ diff --git a/contrib/spi/.gitignore b/contrib/spi/.gitignore index cbe8dbb6b7ed..c214d26c3cac 100644 --- a/contrib/spi/.gitignore +++ b/contrib/spi/.gitignore @@ -1,6 +1,7 @@ -insert_username.sql moddatetime.so -moddatetime.sql timetravel.so -timetravel.sql -refint.sql +/autoinc.sql +/insert_username.sql +/moddatetime.sql +/refint.sql +/timetravel.sql diff --git a/contrib/spi/README.spi b/contrib/spi/README.spi deleted file mode 100755 index 65868f0fc7af..000000000000 --- a/contrib/spi/README.spi +++ /dev/null @@ -1,104 +0,0 @@ - -Here are general trigger functions provided as workable examples -of using SPI and triggers. "General" means that functions may be -used for defining triggers for any tables but you have to specify -table/field names (as described below) while creating a trigger. - -1. refint.c - functions for implementing referential integrity. - -check_primary_key () is to used for foreign keys of a table. - - You are to create trigger (BEFORE INSERT OR UPDATE) using this -function on a table referencing another table. You are to specify -as function arguments: triggered table column names which correspond -to foreign key, referenced table name and column names in referenced -table which correspond to primary/unique key. - You may create as many triggers as you need - one trigger for -one reference. - -check_foreign_key () is to used for primary/unique keys of a table. - - You are to create trigger (BEFORE DELETE OR UPDATE) using this -function on a table referenced by another table(s). You are to specify -as function arguments: number of references for which function has to -performe checking, action if referencing key found ('cascade' - to delete -corresponding foreign key, 'restrict' - to abort transaction if foreign keys -exist, 'setnull' - to set foreign key referencing primary/unique key -being deleted to null), triggered table column names which correspond -to primary/unique key, referencing table name and column names corresponding -to foreign key (, ... - as many referencing tables/keys as specified -by first argument). - Note, that NOT NULL constraint and unique index have to be defined by -youself. - - There are examples in refint.example and regression tests -(sql/triggers.sql). - - To CREATE FUNCTIONs use refint.sql (will be made by gmake from -refint.source). - - -2. timetravel.c - functions for implementing time travel feature. - - Old internally supported time-travel (TT) used insert/delete -transaction commit times. To get the same feature using triggers -you are to add to a table two columns of abstime type to store -date when a tuple was inserted (start_date) and changed/deleted -(stop_date): - -CREATE TABLE XXX ( - ... ... - date_on abstime default currabstime(), - date_off abstime default 'infinity' - ... ... -); - -- so, tuples being inserted with NULLs in date_on/date_off will get -_current_date_ in date_on (name of start_date column in XXX) and INFINITY in -date_off (name of stop_date column in XXX). - - Tuples with stop_date equal INFINITY are "valid now": when trigger will -be fired for UPDATE/DELETE of a tuple with stop_date NOT equal INFINITY then -this tuple will not be changed/deleted! - - If stop_date equal INFINITY then on - -UPDATE: only stop_date in tuple being updated will be changed to current -date and new tuple with new data (coming from SET ... in UPDATE) will be -inserted. Start_date in this new tuple will be setted to current date and -stop_date - to INFINITY. - -DELETE: new tuple will be inserted with stop_date setted to current date -(and with the same data in other columns as in tuple being deleted). - - NOTE: -1. To get tuples "valid now" you are to add _stop_date_ = 'infinity' - to WHERE. Internally supported TT allowed to avoid this... - Fixed rewriting RULEs could help here... - As work arround you may use VIEWs... -2. You can't change start/stop date columns with UPDATE! - Use set_timetravel (below) if you need in this. - - FUNCTIONs: - -timetravel() is general trigger function. - - You are to create trigger BEFORE (!!!) UPDATE OR DELETE using this -function on a time-traveled table. You are to specify two arguments: name of -start_date column and name of stop_date column in triggered table. - -currabstime() may be used in DEFAULT for start_date column to get -current date. - -set_timetravel() allows you turn time-travel ON/OFF for a table: - - set_timetravel('XXX', 1) will turn TT ON for table XXX (and report -old status). - set_timetravel('XXX', 0) will turn TT OFF for table XXX (-"-). - -Turning TT OFF allows you do with a table ALL what you want. - - There is example in timetravel.example. - - To CREATE FUNCTIONs use timetravel.sql (will be made by gmake from -timetravel.source). diff --git a/contrib/spi/README.timetravel b/contrib/spi/README.timetravel deleted file mode 100755 index 0b4727fb248c..000000000000 --- a/contrib/spi/README.timetravel +++ /dev/null @@ -1,116 +0,0 @@ -2. timetravel.c - functions for implementing time travel feature. - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -I rewritten this, because: - -on original version of postgresql 7.3.2-7.3.3: - -the UPDATE not work on timetravel.example if I added ->create unique index tttest_idx on tttest (price_id,price_off); ->update tttest set price_val = 30 where price_id = 3; -ERROR: Cannot insert a duplicate key into unique index tttest_idx - -And UPDATE not work on table tttest after ->alter table tttest add column q1 text; ->alter table tttest add column q2 int; ->alter table tttest drop column q1; ->update tttest set price_val = 30 where price_id = 3; -ERROR: Parameter '$5' is out of range - -And I add a new optional feature: my new timetravel have +3 optional parameters: -inserter_user, updater_user, deleter_user. - -And I add a new function: get_timetravel for get timetravel status -without change it. - -A big difference: -the old version on UPDATE changed oid on active ('infinity') record, -the new version UPDATE keep oid, and the overdued record have a new oid. -I sign with '!!!' my comment in this file. -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - Old internally supported time-travel (TT) used insert/delete -transaction commit times. To get the same feature using triggers -you are to add to a table two columns of abstime type to store -date when a tuple was inserted (start_date) and changed/deleted -(stop_date): - -CREATE TABLE XXX ( - ... ... - date_on abstime default currabstime(), - date_off abstime default 'infinity' - ... ... -/* !!! and (if have) */ - ins_user text /* user, who insert this record */ - upd_user text /* user, who updated this record */ - del_user text /* user, who deleted this record */ - ... ... -); - -!!! on INSERT my new version: - ... and optionally set ins_user to current user, upd_user and del_user to null. - -- so, tuples being inserted with NULLs in date_on/date_off will get -_current_date_ in date_on (name of start_date column in XXX) and INFINITY in -date_off (name of stop_date column in XXX). - - Tuples with stop_date equal INFINITY are "valid now": when trigger will -be fired for UPDATE/DELETE of a tuple with stop_date NOT equal INFINITY then -this tuple will not be changed/deleted! - - If stop_date equal INFINITY then on - -UPDATE: -original version was: - only stop_date in tuple being updated will be changed to current - date and new tuple with new data (coming from SET ... in UPDATE) will be - inserted. Start_date in this new tuple will be setted to current date and - stop_date - to INFINITY. -On my new version: - insert a new tuple with old values, but stop_date changed to current date; - and update original tuple with new data, and update start_date to current date - and optionally set upd_user to current user and clear ins_user,del_user. - -DELETE: new tuple will be inserted with stop_date setted to current date -(and with the same data in other columns as in tuple being deleted). -On my new version: - ... and optionally set del_user to current user. - - NOTE: -1. To get tuples "valid now" you are to add _stop_date_ = 'infinity' - to WHERE. Internally supported TT allowed to avoid this... - Fixed rewriting RULEs could help here... - As work arround you may use VIEWs... -2. You can't change start/stop date columns with UPDATE! - Use set_timetravel (below) if you need in this. - - FUNCTIONs: - -timetravel() is general trigger function. - - You are to create trigger BEFORE UPDATE OR DELETE using this -function on a time-traveled table. You are to specify two arguments: name of -start_date column and name of stop_date column in triggered table. -Or add +3 arguments: - name of insert_user column, name of update_user column, name of delete_user column - -currabstime() may be used in DEFAULT for start_date column to get -current date. -!!! I deleted this function, because I newer used this. - -set_timetravel() allows you turn time-travel ON/OFF for a table: - - set_timetravel('XXX', 1) will turn TT ON for table XXX (and report -old status). - set_timetravel('XXX', 0) will turn TT OFF for table XXX (-"-). - -Turning TT OFF allows you do with a table ALL what you want. - -get_timetravel() reports time-travel status ON(1)/OFF(0) for a table. -get_timetravel() and set_timetravel() not checking existing of table and -existing of timetravel trigger on specified table. - - There is example in timetravel.example. - - To CREATE FUNCTIONs use timetravel.sql (will be made by gmake from -timetravel.source). diff --git a/contrib/spi/preprocessor/README.MAX b/contrib/spi/preprocessor/README.MAX deleted file mode 100755 index 7969438c6026..000000000000 --- a/contrib/spi/preprocessor/README.MAX +++ /dev/null @@ -1,76 +0,0 @@ - -Here are general trigger functions provided as workable examples -of using SPI and triggers. "General" means that functions may be -used for defining triggers for any tables but you have to specify -table/field names (as described below) while creating a trigger. - -1. refint.c - functions for implementing referential integrity. - -check_primary_key () is to used for foreign keys of a table. - - You are to create trigger (BEFORE INSERT OR UPDATE) using this -function on a table referencing another table. You are to specify -as function arguments: triggered table column names which correspond -to foreign key, referenced table name and column names in referenced -table which correspond to primary/unique key. - You may create as many triggers as you need - one trigger for -one reference. - -check_foreign_key () is to used for primary/unique keys of a table. - - You are to create trigger (BEFORE DELETE OR UPDATE) using this -function on a table referenced by another table(s). You are to specify -as function arguments: number of references for which function has to -performe checking, action if referencing key found ('cascade' - to delete -corresponding foreign key, 'restrict' - to abort transaction if foreign keys -exist, 'setnull' - to set foreign key referencing primary/unique key -being deleted to null), triggered table column names which correspond -to primary/unique key, referencing table name and column names corresponding -to foreign key (, ... - as many referencing tables/keys as specified -by first argument). - Note, that NOT NULL constraint and unique index have to be defined by -youself. - - There are examples in refint.example and regression tests -(sql/triggers.sql). - - To CREATE FUNCTIONs use refint.sql (will be made by gmake from -refint.source). - -# Excuse me for my bad english. Massimo Lambertini -# -# -# New check foreign key -# -I think that cascade mode is to be considered like that the operation over -main table is to be made also in referenced table . -When i Delete , i must delete from referenced table , -but when i update , i update referenced table and not delete like unmodified refint.c . - -I made a patch that when i update it check the type of modified key ( if is a text , char() i -added '') and then create a update query that do the right think . - -For my point of view that policy is helpfull because i do not have in referenced table -loss of information . - - -In preprocessor subdir i have placed a little utility that from a SQL92 table definition, -it create all trigger for foreign key . - - -the schema that i use to analyze the problem is this - -create table -A -( key int4 not null primary key ,...) ; -create table -REFERENCED_B -( key int 4 , ... , -foreign key ( key ) references A -- -); - - - - - - diff --git a/contrib/spi/preprocessor/example.sql b/contrib/spi/preprocessor/example.sql deleted file mode 100644 index e53c84a1517b..000000000000 --- a/contrib/spi/preprocessor/example.sql +++ /dev/null @@ -1,37 +0,0 @@ --- Note the syntax is strict because i have no time to write better perl filter. --- --- [blank] is 1 blank --- at the end of an interesting line must be a [,] or [--] --- [ending] must be a , or -- --- --- foreign[blank]key[blank]([blank]keyname,..,keyname[blank])[blank]references[blank]table[blank][ending] --- --- step1 < example.sql | step2.pl > foreign_key_triggers.sql --- --- step1.c is a simple program that UPPERCASE ALL . I know that is simple implementing in Perl --- bu i haven't time - - -CREATE TABLE -gruppo -( -codice_gruppo int4 NOT NULL, -descrizione varchar(32) NOT NULL -primary key ( codice_gruppo ) - -) ; - --- --- fa_parte : Appartenenza di una Azienda Conatto o Cliente ad un certo GRUPPO --- - -CREATE TABLE -fa_parte -( -codice_gruppo int4 NOT NULL, -codice_contatto int4 NOT NULL, - -primary key ( codice_gruppo,codice_contatto ) , -foreign key ( codice_gruppo ) references gruppo -- -); - diff --git a/contrib/spi/preprocessor/step1.c b/contrib/spi/preprocessor/step1.c deleted file mode 100644 index 8a5379e8e040..000000000000 --- a/contrib/spi/preprocessor/step1.c +++ /dev/null @@ -1,27 +0,0 @@ -#include - -char * -strtoupper(char *string) -{ - int i; - - for (i = 0; i < strlen(string); i++) - string[i] = toupper((unsigned char) string[i]); - return string; -} - - - -void -main(char argc, char **argv) -{ - char str[250]; - int sw = 0; - - while (fgets(str, 240, stdin)) - { - if (sw == 0) - printf("%s", strtoupper(str)); - } - -} diff --git a/contrib/spi/preprocessor/step2.pl b/contrib/spi/preprocessor/step2.pl deleted file mode 100755 index 76ce7944cc2e..000000000000 --- a/contrib/spi/preprocessor/step2.pl +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/perl - -## -## MAIN -## -$table_name=""; -$old_name=""; -$references_table=""; -$references_column=""; -$is_create=0; - - - -while ( <> ) -{ - chop; - $str=$_ ; - - if ($is_create == 1) { - $table_name=$str; - $is_create=2; - } - if ( $str =~ /^CREATE TABLE/ ){ - $is_create=1; - } - if ($is_create == 2) { - if ($str =~ /^FOREIGN KEY/){ - ($d1,$d2,$d3,$columns,$d4,$d5,$references_table,$d6) = split (/ /,$str,8); - #printf "Table $table_name $columns $references_table\n"; - - if ($table_name ne $old_name ){ - printf "--\n-- Trigger for $table_name\n--\n\n"; - } - - foreach $i ( split(/,/ , $columns ) ){ - print "CREATE INDEX I_$table_name"; - print "_$i ON $table_name ( $i ) ;\n"; - } - - printf "\nCREATE TRIGGER T_P_$table_name"; - printf "_$references_table BEFORE INSERT OR UPDATE ON $table_name FOR EACH ROW\n" ; - printf "EXECUTE PROCEDURE\n"; - printf "check_primary_key("; - $val=0; - foreach $i ( split(/,/ , $columns ) ){ - print "'$i',"; - $val=$val+1 ; - } - print "'$references_table',"; - - $t=1; - foreach $i ( split(/,/,$columns ) ){ - print "'$i'"; - if ( $t < $val ) { - printf ","; - } - $t=$t+1; - } - print " );\n\n"; - - printf "CREATE TRIGGER T_F_D_$references_table"; - printf "_$table_name BEFORE DELETE ON $references_table FOR EACH ROW\n" ; - printf "EXECUTE PROCEDURE\n"; - printf "check_foreign_key(1,'cascade',"; - $val=0; - foreach $i ( split(/,/ , $columns ) ){ - print "'$i',"; - $val=$val+1 ; - } - print "'$table_name',"; - - $t=1; - foreach $i ( split(/,/,$columns ) ){ - print "'$i'"; - if ( $t < $val ) { - printf ","; - } - $t=$t+1; - } - print " );\n\n"; - - printf "CREATE TRIGGER T_F_U_$references_table"; - printf "_$table_name AFTER UPDATE ON $references_table FOR EACH ROW\n" ; - printf "EXECUTE PROCEDURE\n"; - printf "check_foreign_key(1,'cascade',"; - $val=0; - foreach $i ( split(/,/ , $columns ) ){ - print "'$i',"; - $val=$val+1 ; - } - print "'$table_name',"; - - $t=1; - foreach $i ( split(/,/,$columns ) ){ - print "'$i'"; - if ( $t < $val ) { - printf ","; - } - $t=$t+1; - } - print " );\n\n"; - - if ($table_name ne $old_name ){ - printf "-- ********************************\n\n\n"; - } - $old_name=$table_name ; - - - - - } - } - if ($str =~ /^\)\;/ ) { - $is_create = 0 ; - } - -} - - - - - - diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c index 5a446c37098c..f7694ee92558 100644 --- a/contrib/spi/refint.c +++ b/contrib/spi/refint.c @@ -24,7 +24,7 @@ typedef struct char *ident; int nplans; SPIPlanPtr *splan; -} EPlan; +} EPlan; static EPlan *FPlans = NULL; static int nFPlans = 0; diff --git a/contrib/sslinfo/.gitignore b/contrib/sslinfo/.gitignore index bf8cb0f3a168..6ed45c8ce5a1 100644 --- a/contrib/sslinfo/.gitignore +++ b/contrib/sslinfo/.gitignore @@ -1 +1 @@ -sslinfo.sql +/sslinfo.sql diff --git a/contrib/sslinfo/README.sslinfo b/contrib/sslinfo/README.sslinfo deleted file mode 100755 index 5ce13f54f5c7..000000000000 --- a/contrib/sslinfo/README.sslinfo +++ /dev/null @@ -1,120 +0,0 @@ -sslinfo - information about current SSL certificate for PostgreSQL -================================================================== -Author: Victor Wagner , Cryptocom LTD -E-Mail of Cryptocom OpenSSL development group: - - -1. Notes --------- -This extension won't build unless your PostgreSQL server is configured -with --with-openssl. Information provided with these functions would -be completely useless if you don't use SSL to connect to database. - - -2. Functions Description ------------------------- - -2.1. ssl_is_used() -~~~~~~~~~~~~~~~~~~ - - ssl_is_used() RETURNS boolean; - -Returns TRUE, if current connection to server uses SSL and FALSE -otherwise. - -2.2. ssl_client_cert_present() -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - ssl_client_cert_present() RETURNS boolean - -Returns TRUE if current client have presented valid SSL client -certificate to the server and FALSE otherwise (e.g., no SSL, -certificate hadn't be requested by server). - -2.3. ssl_client_serial() -~~~~~~~~~~~~~~~~~~~~~~~~ - - ssl_client_serial() RETURNS numeric - -Returns serial number of current client certificate. The combination -of certificate serial number and certificate issuer is guaranteed to -uniquely identify certificate (but not its owner -- the owner ought to -regularily change his keys, and get new certificates from the issuer). - -So, if you run you own CA and allow only certificates from this CA to -be accepted by server, the serial number is the most reliable (albeit -not very mnemonic) means to indentify user. - -2.4. ssl_client_dn() -~~~~~~~~~~~~~~~~~~~~ - - ssl_client_dn() RETURNS text - -Returns the full subject of current client certificate, converting -character data into the current database encoding. It is assumed that -if you use non-Latin characters in the certificate names, your -database is able to represent these characters, too. If your database -uses the SQL_ASCII encoding, non-Latin characters in the name will be -represented as UTF-8 sequences. - -The result looks like '/CN=Somebody /C=Some country/O=Some organization'. - -2.5. ssl_issuer_dn() -~~~~~~~~~~~~~~~~~~~~ - -Returns the full issuer name of the client certificate, converting -character data into current database encoding. - -The combination of the return value of this function with the -certificate serial number uniquely identifies the certificate. - -The result of this function is really useful only if you have more -than one trusted CA certificate in your server's root.crt file, or if -this CA has issued some intermediate certificate authority -certificates. - -2.6. ssl_client_dn_field() -~~~~~~~~~~~~~~~~~~~~~~~~~~ - - ssl_client_dn_field(fieldName text) RETURNS text - -This function returns the value of the specified field in the -certificate subject. Field names are string constants that are -converted into ASN1 object identificators using the OpenSSL object -database. The following values are acceptable: - - commonName (alias CN) - surname (alias SN) - name - givenName (alias GN) - countryName (alias C) - localityName (alias L) - stateOrProvinceName (alias ST) - organizationName (alias O) - organizationUnitName (alias OU) - title - description - initials - postalCode - streetAddress - generationQualifier - description - dnQualifier - x500UniqueIdentifier - pseudonim - role - emailAddress - -All of these fields are optional, except commonName. It depends -entirely on your CA policy which of them would be included and which -wouldn't. The meaning of these fields, howeer, is strictly defined by -the X.500 and X.509 standards, so you cannot just assign arbitrary -meaning to them. - -2.7 ssl_issuer_field() -~~~~~~~~~~~~~~~~~~~ - - ssl_issuer_field(fieldName text) RETURNS text; - -Does same as ssl_client_dn_field, but for the certificate issuer -rather than the certificate subject. diff --git a/contrib/start-scripts/osx/PostgreSQL b/contrib/start-scripts/osx/PostgreSQL new file mode 100755 index 000000000000..dc826837a320 --- /dev/null +++ b/contrib/start-scripts/osx/PostgreSQL @@ -0,0 +1,116 @@ +#!/bin/sh + +## +# PostgreSQL RDBMS Server +## + +# PostgreSQL boot time startup script for Darwin/Mac OS X. To install, change +# the "prefix", "PGDATA", "PGUSER", and "PGLOG" variables below as +# necessary. Next, create a new directory, "/Library/StartupItems/PostgreSQL". +# Then copy this script and the accompanying "StartupParameters.plist" file +# into that directory. The name of this script file *must* be the same as the +# directory it is in. So you'll end up with these two files: +# +# /Library/StartupItems/PostgreSQL/PostgreSQL +# /Library/StartupItems/PostgreSQL/StartupParameters.plist +# +# Next, add this line to the /etc/hostconfig file: +# +# POSTGRESQL=-YES- +# +# The startup bundle will now be ready to go. To prevent this script from +# starting PostgreSQL at system startup, simply change that line in +# /etc/hostconfig back to: +# +# POSTGRESQL=-NO- +# +# For more information on Darwin/Mac OS X startup bundles, see this article: +# +# http://www.opensource.apple.com/projects/documentation/howto/html/SystemStarter_HOWTO.html +# +# Created by David Wheeler, 2002. + +# modified by Ray Aspeitia 12-03-2003 : +# added log rotation script to db startup +# modified StartupParameters.plist "Provides" parameter to make it easier to +# start and stop with the SystemStarter utitlity + +# use the below command in order to correctly start/stop/restart PG with log rotation script: +# SystemStarter [start|stop|restart] PostgreSQL + +################################################################################ +## EDIT FROM HERE +################################################################################ + +# Installation prefix +prefix="/usr/local/pgsql" + +# Data directory +PGDATA="/usr/local/pgsql/data" + +# Who to run the postmaster as, usually "postgres". (NOT "root") +PGUSER="postgres" + +# the logfile path and name (NEEDS to be writeable by PGUSER) +PGLOG="${PGDATA}/logs/logfile" + +# do you want to rotate the log files, 1=true 0=false +ROTATELOGS=1 + +# logfile rotate in seconds +ROTATESEC="604800" + + +################################################################################ +## STOP EDITING HERE +################################################################################ + +# The path that is to be used for the script +PATH="$prefix/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin" + +# What to use to start up the postmaster (we do NOT use pg_ctl for this, +# as it adds no value and can cause the postmaster to misrecognize a stale +# lock file) +DAEMON="$prefix/bin/postmaster" + +# What to use to shut down the postmaster +PGCTL="$prefix/bin/pg_ctl" + +# The apache log rotation utility +LOGUTIL="/usr/sbin/rotatelogs" + +. /etc/rc.common + +StartService () { + if [ "${POSTGRESQL:=-NO-}" = "-YES-" ]; then + ConsoleMessage "Starting PostgreSQL database server" + if [ "${ROTATELOGS}" = "1" ]; then + sudo -u $PGUSER sh -c "${DAEMON} -D '${PGDATA}' 2>&1 | ${LOGUTIL} '${PGLOG}' ${ROTATESEC} &" + else + sudo -u $PGUSER sh -c "${DAEMON} -D '${PGDATA}' &" >>$PGLOG 2>&1 + fi + fi +} + +StopService () { + ConsoleMessage "Stopping PostgreSQL database server" + sudo -u $PGUSER $PGCTL stop -D "$PGDATA" -s -m fast +} + +RestartService () { + if [ "${POSTGRESQL:=-NO-}" = "-YES-" ]; then + ConsoleMessage "Restarting PostgreSQL database server" + # should match StopService: + sudo -u $PGUSER $PGCTL stop -D "$PGDATA" -s -m fast + # should match StartService: + if [ "${ROTATELOGS}" = "1" ]; then + sudo -u $PGUSER sh -c "${DAEMON} -D '${PGDATA}' 2>&1 | ${LOGUTIL} '${PGLOG}' ${ROTATESEC} &" + else + sudo -u $PGUSER sh -c "${DAEMON} -D '${PGDATA}' &" >>$PGLOG 2>&1 + fi + else + StopService + fi +} + +RunService "$1" diff --git a/contrib/start-scripts/osx/README b/contrib/start-scripts/osx/README new file mode 100644 index 000000000000..97e299f7da6d --- /dev/null +++ b/contrib/start-scripts/osx/README @@ -0,0 +1,3 @@ +To install execute the following: + +sudo /bin/sh ./install.sh diff --git a/contrib/start-scripts/osx/StartupParameters.plist b/contrib/start-scripts/osx/StartupParameters.plist new file mode 100644 index 000000000000..6c788d0dda77 --- /dev/null +++ b/contrib/start-scripts/osx/StartupParameters.plist @@ -0,0 +1,33 @@ + + + + + Description + PostgreSQL Database Server + Messages + + start + Starting PostgreSQL database server + stop + Stopping PostgreSQL database server + restart + Restarting PostgreSQL database server + + OrderPreference + Late + Provides + + PostgreSQL + + Requires + + Disks + Resolver + + Uses + + NFS + NetworkTime + + + diff --git a/contrib/start-scripts/osx/install.sh b/contrib/start-scripts/osx/install.sh new file mode 100755 index 000000000000..bbc5ee392651 --- /dev/null +++ b/contrib/start-scripts/osx/install.sh @@ -0,0 +1,10 @@ +sudo sh -c 'echo "POSTGRESQL=-YES-" >> /etc/hostconfig' +sudo mkdir /Library/StartupItems/PostgreSQL +sudo cp PostgreSQL /Library/StartupItems/PostgreSQL +sudo cp StartupParameters.plist /Library/StartupItems/PostgreSQL +if [ -e /Library/StartupItems/PostgreSQL/PostgreSQL ] +then + echo "Startup Item Installed Successfully . . . " + echo "Starting PostgreSQL Server . . . " + SystemStarter restart PostgreSQL +fi diff --git a/contrib/tablefunc/.gitignore b/contrib/tablefunc/.gitignore new file mode 100644 index 000000000000..b28639637bcf --- /dev/null +++ b/contrib/tablefunc/.gitignore @@ -0,0 +1,3 @@ +/tablefunc.sql +# Generated subdirectories +/results/ diff --git a/contrib/test_parser/.gitignore b/contrib/test_parser/.gitignore new file mode 100644 index 000000000000..c07f518855cc --- /dev/null +++ b/contrib/test_parser/.gitignore @@ -0,0 +1,3 @@ +/test_parser.sql +# Generated subdirectories +/results/ diff --git a/contrib/test_parser/Makefile b/contrib/test_parser/Makefile new file mode 100644 index 000000000000..449906954872 --- /dev/null +++ b/contrib/test_parser/Makefile @@ -0,0 +1,18 @@ +# $PostgreSQL: pgsql/contrib/test_parser/Makefile,v 1.2 2007/12/03 04:22:54 tgl Exp $ + +MODULE_big = test_parser +OBJS = test_parser.o +DATA_built = test_parser.sql +DATA = uninstall_test_parser.sql +REGRESS = test_parser + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/test_parser +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/test_parser/expected/test_parser.out b/contrib/test_parser/expected/test_parser.out new file mode 100644 index 000000000000..ec4e3b2bb4e7 --- /dev/null +++ b/contrib/test_parser/expected/test_parser.out @@ -0,0 +1,50 @@ +-- +-- first, define the parser. Turn off echoing so that expected file +-- does not depend on contents of this file. +-- +SET client_min_messages = warning; +\set ECHO none +RESET client_min_messages; +-- make test configuration using parser +CREATE TEXT SEARCH CONFIGURATION testcfg (PARSER = testparser); +ALTER TEXT SEARCH CONFIGURATION testcfg ADD MAPPING FOR word WITH simple; +-- ts_parse +SELECT * FROM ts_parse('testparser', 'That''s simple parser can''t parse urls like http://some.url/here/'); + tokid | token +-------+----------------------- + 3 | That's + 12 | + 3 | simple + 12 | + 3 | parser + 12 | + 3 | can't + 12 | + 3 | parse + 12 | + 3 | urls + 12 | + 3 | like + 12 | + 3 | http://some.url/here/ +(15 rows) + +SELECT to_tsvector('testcfg','That''s my first own parser'); + to_tsvector +------------------------------------------------- + 'my':2 'own':4 'first':3 'parser':5 'that''s':1 +(1 row) + +SELECT to_tsquery('testcfg', 'star'); + to_tsquery +------------ + 'star' +(1 row) + +SELECT ts_headline('testcfg','Supernovae stars are the brightest phenomena in galaxies', + to_tsquery('testcfg', 'stars')); + ts_headline +----------------------------------------------------------------- + Supernovae stars are the brightest phenomena in galaxies +(1 row) + diff --git a/contrib/test_parser/sql/test_parser.sql b/contrib/test_parser/sql/test_parser.sql new file mode 100644 index 000000000000..f43d4c7e09ba --- /dev/null +++ b/contrib/test_parser/sql/test_parser.sql @@ -0,0 +1,26 @@ +-- +-- first, define the parser. Turn off echoing so that expected file +-- does not depend on contents of this file. +-- +SET client_min_messages = warning; +\set ECHO none +\i test_parser.sql +\set ECHO all +RESET client_min_messages; + +-- make test configuration using parser + +CREATE TEXT SEARCH CONFIGURATION testcfg (PARSER = testparser); + +ALTER TEXT SEARCH CONFIGURATION testcfg ADD MAPPING FOR word WITH simple; + +-- ts_parse + +SELECT * FROM ts_parse('testparser', 'That''s simple parser can''t parse urls like http://some.url/here/'); + +SELECT to_tsvector('testcfg','That''s my first own parser'); + +SELECT to_tsquery('testcfg', 'star'); + +SELECT ts_headline('testcfg','Supernovae stars are the brightest phenomena in galaxies', + to_tsquery('testcfg', 'stars')); diff --git a/contrib/test_parser/test_parser.c b/contrib/test_parser/test_parser.c new file mode 100644 index 000000000000..4acbbc00c92d --- /dev/null +++ b/contrib/test_parser/test_parser.c @@ -0,0 +1,139 @@ +/*------------------------------------------------------------------------- + * + * test_parser.c + * Simple example of a text search parser + * + * Copyright (c) 2007-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/contrib/test_parser/test_parser.c,v 1.4 2008/01/01 20:31:21 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "fmgr.h" + +PG_MODULE_MAGIC; + + +/* + * types + */ + +/* self-defined type */ +typedef struct +{ + char *buffer; /* text to parse */ + int len; /* length of the text in buffer */ + int pos; /* position of the parser */ +} ParserState; + +/* copy-paste from wparser.h of tsearch2 */ +typedef struct +{ + int lexid; + char *alias; + char *descr; +} LexDescr; + +/* + * prototypes + */ +PG_FUNCTION_INFO_V1(testprs_start); +Datum testprs_start(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(testprs_getlexeme); +Datum testprs_getlexeme(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(testprs_end); +Datum testprs_end(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(testprs_lextype); +Datum testprs_lextype(PG_FUNCTION_ARGS); + +/* + * functions + */ +Datum +testprs_start(PG_FUNCTION_ARGS) +{ + ParserState *pst = (ParserState *) palloc0(sizeof(ParserState)); + + pst->buffer = (char *) PG_GETARG_POINTER(0); + pst->len = PG_GETARG_INT32(1); + pst->pos = 0; + + PG_RETURN_POINTER(pst); +} + +Datum +testprs_getlexeme(PG_FUNCTION_ARGS) +{ + ParserState *pst = (ParserState *) PG_GETARG_POINTER(0); + char **t = (char **) PG_GETARG_POINTER(1); + int *tlen = (int *) PG_GETARG_POINTER(2); + int startpos = pst->pos; + int type; + + *t = pst->buffer + pst->pos; + + if (pst->pos < pst->len && + (pst->buffer)[pst->pos] == ' ') + { + /* blank type */ + type = 12; + /* go to the next non-space character */ + while (pst->pos < pst->len && + (pst->buffer)[pst->pos] == ' ') + (pst->pos)++; + } + else + { + /* word type */ + type = 3; + /* go to the next space character */ + while (pst->pos < pst->len && + (pst->buffer)[pst->pos] != ' ') + (pst->pos)++; + } + + *tlen = pst->pos - startpos; + + /* we are finished if (*tlen == 0) */ + if (*tlen == 0) + type = 0; + + PG_RETURN_INT32(type); +} + +Datum +testprs_end(PG_FUNCTION_ARGS) +{ + ParserState *pst = (ParserState *) PG_GETARG_POINTER(0); + + pfree(pst); + PG_RETURN_VOID(); +} + +Datum +testprs_lextype(PG_FUNCTION_ARGS) +{ + /* + * Remarks: - we have to return the blanks for headline reason - we use + * the same lexids like Teodor in the default word parser; in this way we + * can reuse the headline function of the default word parser. + */ + LexDescr *descr = (LexDescr *) palloc(sizeof(LexDescr) * (2 + 1)); + + /* there are only two types in this parser */ + descr[0].lexid = 3; + descr[0].alias = pstrdup("word"); + descr[0].descr = pstrdup("Word"); + descr[1].lexid = 12; + descr[1].alias = pstrdup("blank"); + descr[1].descr = pstrdup("Space symbols"); + descr[2].lexid = 0; + + PG_RETURN_POINTER(descr); +} diff --git a/contrib/test_parser/test_parser.sql.in b/contrib/test_parser/test_parser.sql.in new file mode 100644 index 000000000000..4fd9b0796e22 --- /dev/null +++ b/contrib/test_parser/test_parser.sql.in @@ -0,0 +1,32 @@ +/* $PostgreSQL: pgsql/contrib/test_parser/test_parser.sql.in,v 1.3 2007/11/13 04:24:29 momjian Exp $ */ + +-- Adjust this setting to control where the objects get created. +SET search_path = public; + +CREATE OR REPLACE FUNCTION testprs_start(internal, int4) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +CREATE OR REPLACE FUNCTION testprs_getlexeme(internal, internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +CREATE OR REPLACE FUNCTION testprs_end(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +CREATE OR REPLACE FUNCTION testprs_lextype(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +CREATE TEXT SEARCH PARSER testparser ( + START = testprs_start, + GETTOKEN = testprs_getlexeme, + END = testprs_end, + HEADLINE = pg_catalog.prsd_headline, + LEXTYPES = testprs_lextype +); diff --git a/contrib/test_parser/uninstall_test_parser.sql b/contrib/test_parser/uninstall_test_parser.sql new file mode 100644 index 000000000000..66686d20042b --- /dev/null +++ b/contrib/test_parser/uninstall_test_parser.sql @@ -0,0 +1,14 @@ +/* $PostgreSQL: pgsql/contrib/test_parser/uninstall_test_parser.sql,v 1.3 2007/11/13 04:24:29 momjian Exp $ */ + +-- Adjust this setting to control where the objects get dropped. +SET search_path = public; + +DROP TEXT SEARCH PARSER testparser; + +DROP FUNCTION testprs_start(internal, int4); + +DROP FUNCTION testprs_getlexeme(internal, internal, internal); + +DROP FUNCTION testprs_end(internal); + +DROP FUNCTION testprs_lextype(internal); diff --git a/contrib/tsearch2/.gitignore b/contrib/tsearch2/.gitignore index 11187a7fbeca..2f6a90856527 100644 --- a/contrib/tsearch2/.gitignore +++ b/contrib/tsearch2/.gitignore @@ -1,2 +1,4 @@ -tsearch2.sql uninstall_tsearch2.sql +/tsearch2.sql +# Generated subdirectories +/results/ diff --git a/contrib/tsearch2/Makefile b/contrib/tsearch2/Makefile index b4dbec036fbb..8ab634323f23 100644 --- a/contrib/tsearch2/Makefile +++ b/contrib/tsearch2/Makefile @@ -1,29 +1,10 @@ -# $PostgreSQL: pgsql/contrib/tsearch2/Makefile,v 1.19 2007/06/26 22:05:03 tgl Exp $ +# $PostgreSQL: pgsql/contrib/tsearch2/Makefile,v 1.20 2007/11/13 21:02:28 tgl Exp $ -MODULE_big = tsearch2 -OBJS = dict_ex.o dict.o snmap.o stopword.o common.o prs_dcfg.o \ - dict_snowball.o dict_ispell.o dict_syn.o dict_thesaurus.o \ - wparser.o wparser_def.o \ - ts_cfg.o tsvector.o query_cleanup.o crc32.o query.o gistidx.o \ - tsvector_op.o rank.o ts_stat.o \ - query_util.o query_support.o query_rewrite.o query_gist.o \ - ts_locale.o ts_lexize.o ginidx.o - -SUBDIRS = snowball ispell wordparser -SUBDIROBJS = $(SUBDIRS:%=%/SUBSYS.o) - -OBJS += $(SUBDIROBJS) - -PG_CPPFLAGS = -I$(srcdir)/snowball -I$(srcdir)/ispell -I$(srcdir)/wordparser - -DATA = stopword/english.stop stopword/russian.stop stopword/russian.stop.utf8 thesaurus -DATA_built = tsearch2.sql uninstall_tsearch2.sql -DOCS = README.tsearch2 +MODULES = tsearch2 +DATA_built = tsearch2.sql +DATA = uninstall_tsearch2.sql REGRESS = tsearch2 -SHLIB_LINK += $(filter -lm, $(LIBS)) - - ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) @@ -34,23 +15,3 @@ top_builddir = ../.. include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif - - -$(SUBDIROBJS): $(SUBDIRS:%=%-recursive) ; - -.PHONY: $(SUBDIRS:%=%-recursive) - -$(SUBDIRS:%=%-recursive): - $(MAKE) -C $(subst -recursive,,$@) SUBSYS.o - -tsearch2.sql: tsearch.sql.in - sed -e 's,MODULE_PATHNAME,$$libdir/$(MODULE_big),g' $< >$@ - -uninstall_tsearch2.sql: untsearch.sql.in - cp $< $@ - -.PHONY: subclean -clean: subclean - -subclean: - for dir in $(SUBDIRS); do $(MAKE) -C $$dir clean || exit; done diff --git a/contrib/tsearch2/README.tsearch2 b/contrib/tsearch2/README.tsearch2 deleted file mode 100755 index 5d7b19d55f6e..000000000000 --- a/contrib/tsearch2/README.tsearch2 +++ /dev/null @@ -1,210 +0,0 @@ -Tsearch2 - full text search extension for PostgreSQL - - [1]Online version of this document is available - - Tsearch2 - is the full text engine, fully integrated into PostgreSQL - RDBMS. - -Main features - - * Full online update - * Supports multiple table driven configurations - * flexible and rich linguistic support (dictionaries, stop words), - thesaurus - * full multibyte (UTF-8) support - * Sophisticated ranking functions with support of proximity and - structure information (rank, rank_cd) - * Index support (GiST and Gin) with concurrency and recovery support - * Rich query language with query rewriting support - * Headline support (text fragments with highlighted search terms) - * Ability to plug-in custom dictionaries and parsers - * Template generator for tsearch2 dictionaries with [2]snowball - stemmer support - * It is mature (5 years of development) - - Tsearch2, in a nutshell, provides FTS operator (contains) for the new - data types, representing document (tsvector) and query (tsquery). - Table driven configuration allows creation of custom searches using - standard SQL commands. - - tsvector is a searchable data type, representing document. It is a set - of unique words along with their positional information in the - document, organized in a special structure optimized for fast access - and lookup. Each entry could be labelled to reflect its importance in - document. - - tsquery is a data type for textual queries with support of boolean - operators. It consists of lexemes (optionally labelled) with boolean - operators between. - - Table driven configuration allows to specify: - * parser, which used to break document onto lexemes - * what lexemes to index and the way they are processed - * dictionaries to be used along with stop words recognition. - -OpenFTS vs Tsearch2 - - [3]OpenFTS is a middleware between application and database. OpenFTS - uses tsearch2 as a storage and database engine as a query executor - (searching). Everything else, i.e. parsing of documents, query - processing, linguistics, carry outs on client side. That's why OpenFTS - has its own configuration table (fts_conf) and works with its own set - of dictionaries. OpenFTS is more flexible, because it could be used in - multi-server architecture with separate machines for repository of - documents (documents could be stored in filesystem), database and - query engine. - - See [4]Documentation Roadmap for links to documentation. - -Authors - - * Oleg Bartunov , Moscow, Moscow University, Russia - * Teodor Sigaev , Moscow,Moscow University,Russia - -Contributors - - * Robert John Shepherd and Andrew J. Kopciuch submitted - "Introduction to tsearch" (Robert - tsearch v1, Andrew - tsearch - v2) - * Brandon Craig Rhodes wrote "Tsearch2 Guide" and "Tsearch2 - Reference" and proposed new naming convention for tsearch V2 - -Sponsors - - * ABC Startsiden - compound words support - * University of Mannheim for UTF-8 support (in 8.2) - * jfg:networks ([5]http:www.jfg-networks.com/) for Gin - Generalized - Inverted index (in 8.2) - * Georgia Public Library Service and LibLime, Inc. for Thesaurus - dictionary - * PostGIS community - GiST Concurrency and Recovery - - The authors are grateful to the Russian Foundation for Basic Research - and Delta-Soft Ltd., Moscow, Russia for support. - -Limitations - - * Length of lexeme < 2K - * Length of tsvector (lexemes + positions) < 1Mb - * The number of lexemes < 4^32 - * 0< Positional information < 16383 - * No more than 256 positions per lexeme - * The number of nodes ( lexemes + operations) in tsquery < 32768 - -References - - * GiST development site - - [6]http://www.sai.msu.su/~megera/postgres/gist - * GiN development - [7]http://www.sigaev.ru/gin/ - * OpenFTS home page - [8]http://openfts.sourceforge.net/ - * Mailing list - - [9]http://sourceforge.net/mailarchive/forum.php?forum=openfts-gene - ral - -Documentation Roadmap - - * Several docs are available from docs/ subdirectory - + "Tsearch V2 Introduction" by Andrew Kopciuch - + "Tsearch2 Guide" by Brandon Rhodes - + "Tsearch2 Reference" by Brandon Rhodes - * Readme.gendict in gendict/ subdirectory - + Also, check [10]Gendict tutorial - * Check [11]tsearch2 Wiki pages for various documentation - -Support - - Authors urgently recommend people to use [12]openfts-general or - [13]pgsql-general mailing lists for questions and discussions. - -Development History - - Latest news - - To the PostgreSQL 8.2 release we added: - * multibyte (UTF-8) support - * Thesaurus dictionary - * Query rewriting - * rank_cd relevation function now support different weights of - lexemes - * GiN support adds scalability of tsearch2 - - Pre-tsearch era - Development of OpenFTS began in 2000 after realizing that we - need a search engine optimized for online updates with access - to metadata from the database. This is essential for online - news agencies, web portals, digital libraries, etc. Most search - engines available utilize an inverted index which is very fast - for searching but very slow for online updates. Incremental - updates of an inverted index is a complex engineering task - while we needed something light, free and with the ability to - access metadata from the database. The last requirement was - very important because in a real life application search engine - should always consult metadata ( topic, permissions, date - range, version, etc.). We extensively use PostgreSQL as a - database backend and have no intention to move from it, so the - problem was to find a data structure and a fast way to access - it. PostgreSQL has rather unique data type for storing sets - (think about words) - arrays, but lacks index access to them. - During our research we found a paper of Joseph Hellerstein, who - introduced an interesting data structure suitable for sets - - RD-tree (Russian Doll tree). Further research lead us to the - idea to use GiST for implementing RD-tree, but at that time the - GiST code was untouched for a long time and contained several - bugs. After work on improving GiST for version 7.0.3 of - PostgreSQL was done, we were able to implement RD-Tree and use - it for index access to arrays of integers. This implementation - was ideally suited for small arrays and eliminated complex - joins, but was practically useless for indexing large arrays. - The next improvement came from an idea to represent a document - by a single bit-signature, a so-called superimposed signature - (see "Index Structures for Databases Containing Data Items with - Set-valued Attributes", 1997, Sven Helmer for details). We - developed the contrib/intarray module and used it for full - text indexing. - - tsearch v1 - It was inconvenient to use integer id's instead of words, so we - introduced a new data type called 'txtidx' - a searchable data - type (textual) with indexed access. This was a first step of - our work on an implementation of a built-in PostgreSQL full - text search engine. Even though tsearch v1 had many features of - a search engine it lacked configuration support and relevance - ranking. People were encouraged to use OpenFTS, which provided - relevance ranking based on positional information and flexible - configuration. OpenFTS v.0.34 is the last version based on - tsearch v1. - - tsearch V2 - People recognized tsearch as a powerful tool for full text - searching and insisted on adding ranking support, better - configurability, etc. We already thought about moving most of - the features of OpenFTS to tsearch, and in the early 2003 we - decided to work on a new version of tsearch. We abandoned - auxiliary index tables which were used by OpenFTS to store - positional information and modified the txtidx type to store - them internally. We added table-driven configuration, support - of ispell dictionaries, snowball stemmers and the ability to - specify which types of lexemes to index. Now, it's possible to - generate headlines of documents with highlighted search terms. - These changes make tsearch more user friendly and turn it into - a really powerful full text search engine. Brandon Rhodes - proposed to rename tsearch functions for consistency and we - renamed txtidx type to tsvector and other things as well. To - allow users of tsearch v1 smooth upgrade, we named the module - as tsearch2. Since version 0.35 OpenFTS uses tsearch2. - -References - - 1. http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/docs/Tsearch_V2_Readme.html - 2. http://snowball.tartarus.org/ - 3. http://openfts.sourceforge.net/ - 4. file://localhost/u/megera/WWW/postgres/gist/tsearch/V2/docs/Tsearch_V2_Readme82.html#dm - 5. http:www.jfg-networks.com/ - 6. http://www.sai.msu.su/~megera/postgres/gist - 7. http://www.sigaev.ru/gin/ - 8. http://openfts.sourceforge.net/ - 9. http://sourceforge.net/mailarchive/forum.php?forum=openfts-general - 10. http://www.sai.msu.su/~megera/wiki/Gendict - 11. http://www.sai.msu.su/~megera/wiki/Tsearch2 - 12. http://sourceforge.net/mailarchive/forum.php?forum=openfts-general - 13. http://archives.postgresql.org/pgsql-general/ diff --git a/contrib/tsearch2/common.c b/contrib/tsearch2/common.c deleted file mode 100644 index 2a2e0fd7750d..000000000000 --- a/contrib/tsearch2/common.c +++ /dev/null @@ -1,188 +0,0 @@ -#include "postgres.h" - -#include "fmgr.h" -#include "catalog/pg_namespace.h" -#include "catalog/pg_proc.h" -#include "utils/syscache.h" -#include "miscadmin.h" - -#include "ts_cfg.h" -#include "dict.h" -#include "wparser.h" -#include "snmap.h" -#include "common.h" -#include "tsvector.h" - - - -#include "common.h" -#include "wparser.h" -#include "ts_cfg.h" -#include "dict.h" - - -Oid TSNSP_FunctionOid = InvalidOid; - - -text * -char2text(char *in) -{ - return charl2text(in, strlen(in)); -} - -text * -charl2text(char *in, int len) -{ - text *out = (text *) palloc(len + VARHDRSZ); - - memcpy(VARDATA(out), in, len); - SET_VARSIZE(out, len + VARHDRSZ); - return out; -} - -char - * -text2char(text *in) -{ - char *out = palloc(VARSIZE(in)); - - memcpy(out, VARDATA(in), VARSIZE(in) - VARHDRSZ); - out[VARSIZE(in) - VARHDRSZ] = '\0'; - return out; -} - -char - * -pnstrdup(const char *in, Size len) -{ - char *out = palloc(len + 1); - - memcpy(out, in, len); - out[len] = '\0'; - return out; -} - -text - * -ptextdup(text *in) -{ - text *out = (text *) palloc(VARSIZE(in)); - - memcpy(out, in, VARSIZE(in)); - return out; -} - -text - * -mtextdup(text *in) -{ - text *out = (text *) malloc(VARSIZE(in)); - - if (!out) - ts_error(ERROR, "No memory"); - memcpy(out, in, VARSIZE(in)); - return out; -} - -void -ts_error(int state, const char *format,...) -{ - va_list args; - int tlen = 128, - len = 0; - char *buf; - - reset_cfg(); - reset_dict(); - reset_prs(); - - va_start(args, format); - buf = palloc(tlen); - len = vsnprintf(buf, tlen - 1, format, args); - if (len >= tlen) - { - tlen = len + 1; - buf = repalloc(buf, tlen); - vsnprintf(buf, tlen - 1, format, args); - } - va_end(args); - - /* ?? internal error ?? */ - elog(state, "%s", buf); - pfree(buf); -} - -int -text_cmp(text *a, text *b) -{ - if (VARSIZE(a) == VARSIZE(b)) - return strncmp(VARDATA(a), VARDATA(b), VARSIZE(a) - VARHDRSZ); - return (int) VARSIZE(a) - (int) VARSIZE(b); - -} - -char * -get_namespace(Oid funcoid) -{ - HeapTuple tuple; - Form_pg_proc proc; - Form_pg_namespace nsp; - Oid nspoid; - char *txt; - - tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcoid), 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for proc oid %u", funcoid); - proc = (Form_pg_proc) GETSTRUCT(tuple); - nspoid = proc->pronamespace; - ReleaseSysCache(tuple); - - tuple = SearchSysCache(NAMESPACEOID, ObjectIdGetDatum(nspoid), 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for namespace oid %u", nspoid); - nsp = (Form_pg_namespace) GETSTRUCT(tuple); - txt = pstrdup(NameStr((nsp->nspname))); - ReleaseSysCache(tuple); - - return txt; -} - -Oid -get_oidnamespace(Oid funcoid) -{ - HeapTuple tuple; - Form_pg_proc proc; - Oid nspoid; - - tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcoid), 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for proc oid %u", funcoid); - proc = (Form_pg_proc) GETSTRUCT(tuple); - nspoid = proc->pronamespace; - ReleaseSysCache(tuple); - - return nspoid; -} - - /* if path is relative, take it as relative to share dir */ -char * -to_absfilename(char *filename) -{ - if (!is_absolute_path(filename)) - { - char sharepath[MAXPGPATH]; - char *absfn; - -#ifdef WIN32 - char delim = '\\'; -#else - char delim = '/'; -#endif - get_share_path(my_exec_path, sharepath); - absfn = palloc(strlen(sharepath) + strlen(filename) + 2); - sprintf(absfn, "%s%c%s", sharepath, delim, filename); - filename = absfn; - } - - return filename; -} diff --git a/contrib/tsearch2/common.h b/contrib/tsearch2/common.h deleted file mode 100644 index c0d9f6763f01..000000000000 --- a/contrib/tsearch2/common.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __TS_COMMON_H__ -#define __TS_COMMON_H__ - -#include "postgres.h" -#include "fmgr.h" -#include "utils/array.h" - -text *char2text(char *in); -text *charl2text(char *in, int len); -char *text2char(text *in); -char *pnstrdup(const char *in, Size len); -text *ptextdup(text *in); -text *mtextdup(text *in); - -int text_cmp(text *a, text *b); - -char *to_absfilename(char *filename); - -#define NEXTVAL(x) ( (text*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) ) -#define ARRNELEMS(x) ArrayGetNItems( ARR_NDIM(x), ARR_DIMS(x)) - -void ts_error(int state, const char *format,...); - -extern Oid TSNSP_FunctionOid; /* oid of called function, needed only for - * determ namespace, no more */ -char *get_namespace(Oid funcoid); -Oid get_oidnamespace(Oid funcoid); - -#define SET_FUNCOID() do { \ - if ( fcinfo->flinfo && fcinfo->flinfo->fn_oid != InvalidOid ) \ - TSNSP_FunctionOid = fcinfo->flinfo->fn_oid; \ -} while(0) - -#endif diff --git a/contrib/tsearch2/crc32.c b/contrib/tsearch2/crc32.c deleted file mode 100644 index bc5c7605dd34..000000000000 --- a/contrib/tsearch2/crc32.c +++ /dev/null @@ -1,105 +0,0 @@ -/* Both POSIX and CRC32 checksums */ - -/* $PostgreSQL: pgsql/contrib/tsearch2/crc32.c,v 1.4 2007/07/15 22:40:28 tgl Exp $ */ - -#include -#include -#include - -#include "crc32.h" - -/* - * This code implements the AUTODIN II polynomial - * The variable corresponding to the macro argument "crc" should - * be an unsigned long. - * Oroginal code by Spencer Garrett - */ - -#define _CRC32_(crc, ch) ((crc) = ((crc) >> 8) ^ crc32tab[((crc) ^ (ch)) & 0xff]) - -/* generated using the AUTODIN II polynomial - * x^32 + x^26 + x^23 + x^22 + x^16 + - * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 - */ - -static const unsigned int crc32tab[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -unsigned int -crc32_sz(char *buf, int size) -{ - unsigned int crc = ~((unsigned int) 0); - char *p; - int len, - nr; - - len = 0; - nr = size; - for (len += nr, p = buf; nr--; ++p) - _CRC32_(crc, *p); - return ~crc; -} diff --git a/contrib/tsearch2/crc32.h b/contrib/tsearch2/crc32.h deleted file mode 100644 index 420c9594ac20..000000000000 --- a/contrib/tsearch2/crc32.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _CRC32_H -#define _CRC32_H - -/* $PostgreSQL: pgsql/contrib/tsearch2/crc32.h,v 1.2 2006/03/11 04:38:30 momjian Exp $ */ - -/* Returns crc32 of data block */ -extern unsigned int crc32_sz(char *buf, int size); - -/* Returns crc32 of null-terminated string */ -#define crc32(buf) crc32_sz((buf),strlen(buf)) - -#endif diff --git a/contrib/tsearch2/dict.c b/contrib/tsearch2/dict.c deleted file mode 100644 index 182f5c329104..000000000000 --- a/contrib/tsearch2/dict.c +++ /dev/null @@ -1,349 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/dict.c,v 1.13 2006/10/04 00:29:46 momjian Exp $ */ - -/* - * interface functions to dictionary - * Teodor Sigaev - */ -#include "postgres.h" - -#include - -#include "catalog/pg_type.h" -#include "executor/spi.h" -#include "fmgr.h" -#include "utils/array.h" -#include "utils/memutils.h" - -#include "dict.h" -#include "common.h" -#include "snmap.h" - -/*********top interface**********/ - -void -init_dict(Oid id, DictInfo * dict) -{ - Oid arg[1]; - bool isnull; - Datum pars[1]; - int stat; - void *plan; - char buf[1024]; - char *nsp = get_namespace(TSNSP_FunctionOid); - - arg[0] = OIDOID; - pars[0] = ObjectIdGetDatum(id); - - memset(dict, 0, sizeof(DictInfo)); - SPI_connect(); - sprintf(buf, "select dict_init, dict_initoption, dict_lexize from %s.pg_ts_dict where oid = $1", nsp); - pfree(nsp); - plan = SPI_prepare(buf, 1, arg); - if (!plan) - ts_error(ERROR, "SPI_prepare() failed"); - - stat = SPI_execp(plan, pars, " ", 1); - if (stat < 0) - ts_error(ERROR, "SPI_execp return %d", stat); - if (SPI_processed > 0) - { - Datum opt; - Oid oid = InvalidOid; - - /* setup dictlexize method */ - oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3, &isnull)); - if (isnull || oid == InvalidOid) - ts_error(ERROR, "Null dict_lexize for dictonary %d", id); - fmgr_info_cxt(oid, &(dict->lexize_info), TopMemoryContext); - - /* setup and call dictinit method, optinally */ - oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); - if (!(isnull || oid == InvalidOid)) - { - opt = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &isnull); - dict->dictionary = (void *) DatumGetPointer(OidFunctionCall1(oid, opt)); - } - dict->dict_id = id; - } - else - ts_error(ERROR, "No dictionary with id %d", id); - SPI_freeplan(plan); - SPI_finish(); -} - -typedef struct -{ - DictInfo *last_dict; - int len; - int reallen; - DictInfo *list; - SNMap name2id_map; -} DictList; - -static DictList DList = {NULL, 0, 0, NULL, {0, 0, NULL}}; - -void -reset_dict(void) -{ - freeSNMap(&(DList.name2id_map)); - /* XXX need to free DList.list[*].dictionary */ - if (DList.list) - free(DList.list); - memset(&DList, 0, sizeof(DictList)); -} - - -static int -comparedict(const void *a, const void *b) -{ - if (((DictInfo *) a)->dict_id == ((DictInfo *) b)->dict_id) - return 0; - return (((DictInfo *) a)->dict_id < ((DictInfo *) b)->dict_id) ? -1 : 1; -} - -static void -insertdict(Oid id) -{ - DictInfo newdict; - - if (DList.len == DList.reallen) - { - DictInfo *tmp; - int reallen = (DList.reallen) ? 2 * DList.reallen : 16; - - tmp = (DictInfo *) realloc(DList.list, sizeof(DictInfo) * reallen); - if (!tmp) - ts_error(ERROR, "No memory"); - DList.reallen = reallen; - DList.list = tmp; - } - init_dict(id, &newdict); - - DList.list[DList.len] = newdict; - DList.len++; - - qsort(DList.list, DList.len, sizeof(DictInfo), comparedict); -} - -DictInfo * -finddict(Oid id) -{ - /* last used dict */ - if (DList.last_dict && DList.last_dict->dict_id == id) - return DList.last_dict; - - - /* already used dict */ - if (DList.len != 0) - { - DictInfo key; - - key.dict_id = id; - DList.last_dict = bsearch(&key, DList.list, DList.len, sizeof(DictInfo), comparedict); - if (DList.last_dict != NULL) - return DList.last_dict; - } - - /* insert new dictionary */ - insertdict(id); - return finddict(id); /* qsort changed order!! */ ; -} - -Oid -name2id_dict(text *name) -{ - Oid arg[1]; - bool isnull; - Datum pars[1]; - int stat; - Oid id = findSNMap_t(&(DList.name2id_map), name); - void *plan; - char buf[1024], - *nsp; - - arg[0] = TEXTOID; - pars[0] = PointerGetDatum(name); - - if (id) - return id; - - nsp = get_namespace(TSNSP_FunctionOid); - SPI_connect(); - sprintf(buf, "select oid from %s.pg_ts_dict where dict_name = $1", nsp); - pfree(nsp); - plan = SPI_prepare(buf, 1, arg); - if (!plan) - ts_error(ERROR, "SPI_prepare() failed"); - - stat = SPI_execp(plan, pars, " ", 1); - if (stat < 0) - ts_error(ERROR, "SPI_execp return %d", stat); - if (SPI_processed > 0) - id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); - else - ts_error(ERROR, "No dictionary with name '%s'", text2char(name)); - SPI_freeplan(plan); - SPI_finish(); - addSNMap_t(&(DList.name2id_map), name, id); - return id; -} - - -/******sql-level interface******/ -PG_FUNCTION_INFO_V1(lexize); -Datum lexize(PG_FUNCTION_ARGS); - -Datum -lexize(PG_FUNCTION_ARGS) -{ - text *in = PG_GETARG_TEXT_P(1); - DictInfo *dict; - TSLexeme *res, - *ptr; - Datum *da; - ArrayType *a; - DictSubState dstate = {false, false, NULL}; - - SET_FUNCOID(); - dict = finddict(PG_GETARG_OID(0)); - - ptr = res = (TSLexeme *) DatumGetPointer( - FunctionCall4(&(dict->lexize_info), - PointerGetDatum(dict->dictionary), - PointerGetDatum(VARDATA(in)), - Int32GetDatum(VARSIZE(in) - VARHDRSZ), - PointerGetDatum(&dstate) - ) - ); - - if (dstate.getnext) - { - dstate.isend = true; - ptr = res = (TSLexeme *) DatumGetPointer( - FunctionCall4(&(dict->lexize_info), - PointerGetDatum(dict->dictionary), - PointerGetDatum(VARDATA(in)), - Int32GetDatum(VARSIZE(in) - VARHDRSZ), - PointerGetDatum(&dstate) - ) - ); - } - - PG_FREE_IF_COPY(in, 1); - if (!res) - { - if (PG_NARGS() > 2) - PG_RETURN_POINTER(NULL); - else - PG_RETURN_NULL(); - } - - while (ptr->lexeme) - ptr++; - da = (Datum *) palloc(sizeof(Datum) * (ptr - res + 1)); - ptr = res; - while (ptr->lexeme) - { - da[ptr - res] = PointerGetDatum(char2text(ptr->lexeme)); - ptr++; - } - - a = construct_array( - da, - ptr - res, - TEXTOID, - -1, - false, - 'i' - ); - - ptr = res; - while (ptr->lexeme) - { - pfree(DatumGetPointer(da[ptr - res])); - pfree(ptr->lexeme); - ptr++; - } - pfree(res); - pfree(da); - - PG_RETURN_POINTER(a); -} - -PG_FUNCTION_INFO_V1(lexize_byname); -Datum lexize_byname(PG_FUNCTION_ARGS); -Datum -lexize_byname(PG_FUNCTION_ARGS) -{ - text *dictname = PG_GETARG_TEXT_P(0); - Datum res; - - SET_FUNCOID(); - - res = DirectFunctionCall3( - lexize, - ObjectIdGetDatum(name2id_dict(dictname)), - PG_GETARG_DATUM(1), - (Datum) 0 - ); - PG_FREE_IF_COPY(dictname, 0); - if (res) - PG_RETURN_DATUM(res); - else - PG_RETURN_NULL(); -} - -static Oid currect_dictionary_id = 0; - -PG_FUNCTION_INFO_V1(set_curdict); -Datum set_curdict(PG_FUNCTION_ARGS); -Datum -set_curdict(PG_FUNCTION_ARGS) -{ - SET_FUNCOID(); - finddict(PG_GETARG_OID(0)); - currect_dictionary_id = PG_GETARG_OID(0); - PG_RETURN_VOID(); -} - -PG_FUNCTION_INFO_V1(set_curdict_byname); -Datum set_curdict_byname(PG_FUNCTION_ARGS); -Datum -set_curdict_byname(PG_FUNCTION_ARGS) -{ - text *dictname = PG_GETARG_TEXT_P(0); - - SET_FUNCOID(); - DirectFunctionCall1( - set_curdict, - ObjectIdGetDatum(name2id_dict(dictname)) - ); - PG_FREE_IF_COPY(dictname, 0); - PG_RETURN_VOID(); -} - -PG_FUNCTION_INFO_V1(lexize_bycurrent); -Datum lexize_bycurrent(PG_FUNCTION_ARGS); -Datum -lexize_bycurrent(PG_FUNCTION_ARGS) -{ - Datum res; - - SET_FUNCOID(); - if (currect_dictionary_id == 0) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("no currect dictionary"), - errhint("Execute select set_curdict()."))); - - res = DirectFunctionCall3( - lexize, - ObjectIdGetDatum(currect_dictionary_id), - PG_GETARG_DATUM(0), - (Datum) 0 - ); - if (res) - PG_RETURN_DATUM(res); - else - PG_RETURN_NULL(); -} diff --git a/contrib/tsearch2/dict.h b/contrib/tsearch2/dict.h deleted file mode 100644 index 78d5111f14f6..000000000000 --- a/contrib/tsearch2/dict.h +++ /dev/null @@ -1,114 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/dict.h,v 1.8 2006/10/04 00:29:46 momjian Exp $ */ - -#ifndef __DICT_H__ -#define __DICT_H__ -#include "postgres.h" -#include "fmgr.h" -#include "ts_cfg.h" - -typedef struct -{ - int len; - char **stop; - char *(*wordop) (char *); -} StopList; - -void sortstoplist(StopList * s); -void freestoplist(StopList * s); -void readstoplist(text *in, StopList * s); -bool searchstoplist(StopList * s, char *key); - -typedef struct -{ - Oid dict_id; - FmgrInfo lexize_info; - void *dictionary; -} DictInfo; - -void init_dict(Oid id, DictInfo * dict); -DictInfo *finddict(Oid id); -Oid name2id_dict(text *name); -void reset_dict(void); - -typedef struct -{ - bool isend; /* in: marks for lexize_info about text end is - * reached */ - bool getnext; /* out: dict wants next lexeme */ - void *private; /* internal dict state between calls with - * getnext == true */ -} DictSubState; - -/* simple parser of cfg string */ -typedef struct -{ - char *key; - char *value; -} Map; - -void parse_cfgdict(text *in, Map ** m); - -/* return struct for any lexize function */ -typedef struct -{ - /* - * number of variant of split word , for example Word 'fotballklubber' - * (norwegian) has two varian to split: ( fotball, klubb ) and ( fot, - * ball, klubb ). So, dictionary should return: nvariant lexeme 1 - * fotball 1 klubb 2 fot 2 ball 2 klubb - */ - uint16 nvariant; - - uint16 flags; - - /* C-string */ - char *lexeme; -} TSLexeme; - -#define TSL_ADDPOS 0x01 - - -/* - * Lexize subsystem - */ - -typedef struct ParsedLex -{ - int type; - char *lemm; - int lenlemm; - bool resfollow; - struct ParsedLex *next; -} ParsedLex; - -typedef struct ListParsedLex -{ - ParsedLex *head; - ParsedLex *tail; -} ListParsedLex; - -typedef struct -{ - TSCfgInfo *cfg; - Oid curDictId; - int posDict; - DictSubState dictState; - ParsedLex *curSub; - ListParsedLex towork; /* current list to work */ - ListParsedLex waste; /* list of lexemes that already lexized */ - - /* - * fields to store last variant to lexize (basically, thesaurus or similar - * to, which wants several lexemes - */ - - ParsedLex *lastRes; - TSLexeme *tmpRes; -} LexizeData; - - -void LexizeInit(LexizeData * ld, TSCfgInfo * cfg); -void LexizeAddLemm(LexizeData * ld, int type, char *lemm, int lenlemm); -TSLexeme *LexizeExec(LexizeData * ld, ParsedLex ** correspondLexem); - -#endif diff --git a/contrib/tsearch2/dict_ex.c b/contrib/tsearch2/dict_ex.c deleted file mode 100644 index 2fd5cbb70093..000000000000 --- a/contrib/tsearch2/dict_ex.c +++ /dev/null @@ -1,70 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/dict_ex.c,v 1.9 2006/11/20 14:03:30 teodor Exp $ */ - -/* - * example of dictionary - * Teodor Sigaev - */ -#include "postgres.h" - -#include "dict.h" -#include "common.h" -#include "ts_locale.h" - -typedef struct -{ - StopList stoplist; -} DictExample; - - -PG_FUNCTION_INFO_V1(dex_init); -Datum dex_init(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(dex_lexize); -Datum dex_lexize(PG_FUNCTION_ARGS); - -Datum -dex_init(PG_FUNCTION_ARGS) -{ - DictExample *d = (DictExample *) malloc(sizeof(DictExample)); - - if (!d) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - memset(d, 0, sizeof(DictExample)); - - d->stoplist.wordop = lowerstr; - - if (!PG_ARGISNULL(0) && PG_GETARG_POINTER(0) != NULL) - { - text *in = PG_GETARG_TEXT_P(0); - - readstoplist(in, &(d->stoplist)); - sortstoplist(&(d->stoplist)); - PG_FREE_IF_COPY(in, 0); - } - - PG_RETURN_POINTER(d); -} - -Datum -dex_lexize(PG_FUNCTION_ARGS) -{ - DictExample *d = (DictExample *) PG_GETARG_POINTER(0); - char *in = (char *) PG_GETARG_POINTER(1); - char *utxt = pnstrdup(in, PG_GETARG_INT32(2)); - TSLexeme *res = palloc(sizeof(TSLexeme) * 2); - char *txt = lowerstr(utxt); - - pfree(utxt); - memset(res, 0, sizeof(TSLexeme) * 2); - - if (*txt == '\0' || searchstoplist(&(d->stoplist), txt)) - { - pfree(txt); - } - else - res[0].lexeme = txt; - - PG_RETURN_POINTER(res); -} diff --git a/contrib/tsearch2/dict_ispell.c b/contrib/tsearch2/dict_ispell.c deleted file mode 100644 index 301b19d59287..000000000000 --- a/contrib/tsearch2/dict_ispell.c +++ /dev/null @@ -1,196 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/dict_ispell.c,v 1.10 2006/03/11 04:38:30 momjian Exp $ */ - -/* - * ISpell interface - * Teodor Sigaev - */ -#include "postgres.h" - -#include - -#include "dict.h" -#include "common.h" -#include "ispell/spell.h" -#include "ts_locale.h" - -typedef struct -{ - StopList stoplist; - IspellDict obj; -} DictISpell; - -PG_FUNCTION_INFO_V1(spell_init); -Datum spell_init(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(spell_lexize); -Datum spell_lexize(PG_FUNCTION_ARGS); - -static void -freeDictISpell(DictISpell * d) -{ - NIFree(&(d->obj)); - freestoplist(&(d->stoplist)); - free(d); -} - -Datum -spell_init(PG_FUNCTION_ARGS) -{ - DictISpell *d; - Map *cfg, - *pcfg; - text *in; - bool affloaded = false, - dictloaded = false, - stoploaded = false; - - if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) - ereport(ERROR, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("ISpell confguration error"))); - - d = (DictISpell *) malloc(sizeof(DictISpell)); - if (!d) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - memset(d, 0, sizeof(DictISpell)); - d->stoplist.wordop = lowerstr; - - in = PG_GETARG_TEXT_P(0); - parse_cfgdict(in, &cfg); - PG_FREE_IF_COPY(in, 0); - pcfg = cfg; - while (pcfg->key) - { - if (pg_strcasecmp("DictFile", pcfg->key) == 0) - { - if (dictloaded) - { - freeDictISpell(d); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("dictionary already loaded"))); - } - if (NIImportDictionary(&(d->obj), pcfg->value)) - { - freeDictISpell(d); - ereport(ERROR, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("could not load dictionary file \"%s\"", - pcfg->value))); - } - dictloaded = true; - } - else if (pg_strcasecmp("AffFile", pcfg->key) == 0) - { - if (affloaded) - { - freeDictISpell(d); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("affixes already loaded"))); - } - if (NIImportAffixes(&(d->obj), pcfg->value)) - { - freeDictISpell(d); - ereport(ERROR, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("could not load affix file \"%s\"", - pcfg->value))); - } - affloaded = true; - } - else if (pg_strcasecmp("StopFile", pcfg->key) == 0) - { - text *tmp = char2text(pcfg->value); - - if (stoploaded) - { - freeDictISpell(d); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("stop words already loaded"))); - } - readstoplist(tmp, &(d->stoplist)); - sortstoplist(&(d->stoplist)); - pfree(tmp); - stoploaded = true; - } - else - { - freeDictISpell(d); - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unrecognized option: %s => %s", - pcfg->key, pcfg->value))); - } - pfree(pcfg->key); - pfree(pcfg->value); - pcfg++; - } - pfree(cfg); - - if (affloaded && dictloaded) - { - NISortDictionary(&(d->obj)); - NISortAffixes(&(d->obj)); - } - else if (!affloaded) - { - freeDictISpell(d); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("no affixes"))); - } - else - { - freeDictISpell(d); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("no dictionary"))); - } - - PG_RETURN_POINTER(d); -} - -Datum -spell_lexize(PG_FUNCTION_ARGS) -{ - DictISpell *d = (DictISpell *) PG_GETARG_POINTER(0); - char *in = (char *) PG_GETARG_POINTER(1); - char *txt; - TSLexeme *res; - TSLexeme *ptr, - *cptr; - - if (!PG_GETARG_INT32(2)) - PG_RETURN_POINTER(NULL); - - txt = pnstrdup(in, PG_GETARG_INT32(2)); - res = NINormalizeWord(&(d->obj), txt); - pfree(txt); - - if (res == NULL) - PG_RETURN_POINTER(NULL); - - ptr = cptr = res; - while (ptr->lexeme) - { - if (searchstoplist(&(d->stoplist), ptr->lexeme)) - { - pfree(ptr->lexeme); - ptr->lexeme = NULL; - ptr++; - } - else - { - memcpy(cptr, ptr, sizeof(TSLexeme)); - cptr++; - ptr++; - } - } - cptr->lexeme = NULL; - - PG_RETURN_POINTER(res); -} diff --git a/contrib/tsearch2/dict_snowball.c b/contrib/tsearch2/dict_snowball.c deleted file mode 100644 index 666774482490..000000000000 --- a/contrib/tsearch2/dict_snowball.c +++ /dev/null @@ -1,169 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/dict_snowball.c,v 1.13 2006/11/20 14:03:30 teodor Exp $ */ - -/* - * example of Snowball dictionary - * http://snowball.tartarus.org/ - * Teodor Sigaev - */ -#include "postgres.h" - -#include "dict.h" -#include "common.h" -#include "snowball/english_stem.h" -#include "snowball/header.h" -#include "snowball/russian_stem.h" -#include "snowball/russian_stem_UTF8.h" -#include "ts_locale.h" - -typedef struct -{ - struct SN_env *z; - StopList stoplist; - int (*stem) (struct SN_env * z); -} DictSnowball; - - -PG_FUNCTION_INFO_V1(snb_en_init); -Datum snb_en_init(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(snb_ru_init_koi8); -Datum snb_ru_init_koi8(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(snb_ru_init_utf8); -Datum snb_ru_init_utf8(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(snb_lexize); -Datum snb_lexize(PG_FUNCTION_ARGS); - -Datum -snb_en_init(PG_FUNCTION_ARGS) -{ - DictSnowball *d = (DictSnowball *) malloc(sizeof(DictSnowball)); - - if (!d) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - memset(d, 0, sizeof(DictSnowball)); - d->stoplist.wordop = lowerstr; - - if (!PG_ARGISNULL(0) && PG_GETARG_POINTER(0) != NULL) - { - text *in = PG_GETARG_TEXT_P(0); - - readstoplist(in, &(d->stoplist)); - sortstoplist(&(d->stoplist)); - PG_FREE_IF_COPY(in, 0); - } - - d->z = english_ISO_8859_1_create_env(); - if (!d->z) - { - freestoplist(&(d->stoplist)); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - d->stem = english_ISO_8859_1_stem; - - PG_RETURN_POINTER(d); -} - -Datum -snb_ru_init_koi8(PG_FUNCTION_ARGS) -{ - DictSnowball *d = (DictSnowball *) malloc(sizeof(DictSnowball)); - - if (!d) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - memset(d, 0, sizeof(DictSnowball)); - d->stoplist.wordop = lowerstr; - - if (!PG_ARGISNULL(0) && PG_GETARG_POINTER(0) != NULL) - { - text *in = PG_GETARG_TEXT_P(0); - - readstoplist(in, &(d->stoplist)); - sortstoplist(&(d->stoplist)); - PG_FREE_IF_COPY(in, 0); - } - - d->z = russian_KOI8_R_create_env(); - if (!d->z) - { - freestoplist(&(d->stoplist)); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - d->stem = russian_KOI8_R_stem; - - PG_RETURN_POINTER(d); -} - -Datum -snb_ru_init_utf8(PG_FUNCTION_ARGS) -{ - DictSnowball *d = (DictSnowball *) malloc(sizeof(DictSnowball)); - - if (!d) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - memset(d, 0, sizeof(DictSnowball)); - d->stoplist.wordop = lowerstr; - - if (!PG_ARGISNULL(0) && PG_GETARG_POINTER(0) != NULL) - { - text *in = PG_GETARG_TEXT_P(0); - - readstoplist(in, &(d->stoplist)); - sortstoplist(&(d->stoplist)); - PG_FREE_IF_COPY(in, 0); - } - - d->z = russian_UTF_8_create_env(); - if (!d->z) - { - freestoplist(&(d->stoplist)); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - d->stem = russian_UTF_8_stem; - - PG_RETURN_POINTER(d); -} - -Datum -snb_lexize(PG_FUNCTION_ARGS) -{ - DictSnowball *d = (DictSnowball *) PG_GETARG_POINTER(0); - char *in = (char *) PG_GETARG_POINTER(1); - char *utxt = pnstrdup(in, PG_GETARG_INT32(2)); - TSLexeme *res = palloc(sizeof(TSLexeme) * 2); - char *txt = lowerstr(utxt); - - pfree(utxt); - memset(res, 0, sizeof(TSLexeme) * 2); - if (*txt == '\0' || searchstoplist(&(d->stoplist), txt)) - { - pfree(txt); - } - else - { - SN_set_current(d->z, strlen(txt), (symbol *) txt); - (d->stem) (d->z); - if (d->z->p && d->z->l) - { - txt = repalloc(txt, d->z->l + 1); - memcpy(txt, d->z->p, d->z->l); - txt[d->z->l] = '\0'; - } - res->lexeme = txt; - } - - PG_RETURN_POINTER(res); -} diff --git a/contrib/tsearch2/dict_syn.c b/contrib/tsearch2/dict_syn.c deleted file mode 100644 index 158e49b01586..000000000000 --- a/contrib/tsearch2/dict_syn.c +++ /dev/null @@ -1,185 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/dict_syn.c,v 1.12 2007/02/08 11:10:26 petere Exp $ */ - -/* - * ISpell interface - * Teodor Sigaev - */ -#include "postgres.h" - -#include - -#include "dict.h" -#include "common.h" -#include "ts_locale.h" - -#define SYNBUFLEN 4096 -typedef struct -{ - char *in; - char *out; -} Syn; - -typedef struct -{ - int len; - Syn *syn; -} DictSyn; - -PG_FUNCTION_INFO_V1(syn_init); -Datum syn_init(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(syn_lexize); -Datum syn_lexize(PG_FUNCTION_ARGS); - -static char * -findwrd(char *in, char **end) -{ - char *start; - - *end = NULL; - while (*in && isspace((unsigned char) *in)) - in++; - - if (*in=='\0') - return NULL; - start = in; - - while (*in && !isspace((unsigned char) *in)) - in++; - - *end = in; - return start; -} - -static int -compareSyn(const void *a, const void *b) -{ - return strcmp(((Syn *) a)->in, ((Syn *) b)->in); -} - - -Datum -syn_init(PG_FUNCTION_ARGS) -{ - text *in; - DictSyn *d; - int cur = 0; - FILE *fin; - char *filename; - char buf[SYNBUFLEN]; - char *starti, - *starto, - *end = NULL; - int slen; - - if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("NULL config"))); - - in = PG_GETARG_TEXT_P(0); - if (VARSIZE(in) - VARHDRSZ == 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("VOID config"))); - - filename = text2char(in); - PG_FREE_IF_COPY(in, 0); - if ((fin = fopen(filename, "r")) == NULL) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not open file \"%s\": %m", - filename))); - - d = (DictSyn *) malloc(sizeof(DictSyn)); - if (!d) - { - fclose(fin); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - memset(d, 0, sizeof(DictSyn)); - - while (fgets(buf, sizeof(buf), fin)) - { - slen = strlen(buf); - pg_verifymbstr(buf, slen, false); - if (cur == d->len) - { - d->len = (d->len) ? 2 * d->len : 16; - d->syn = (Syn *) realloc(d->syn, sizeof(Syn) * d->len); - if (!d->syn) - { - fclose(fin); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - } - - starti = findwrd(buf, &end); - if (!starti) - continue; - *end = '\0'; - if (end >= buf + slen) - continue; - - starto = findwrd(end + 1, &end); - if (!starto) - continue; - *end = '\0'; - - d->syn[cur].in = strdup(lowerstr(starti)); - d->syn[cur].out = strdup(lowerstr(starto)); - if (!(d->syn[cur].in && d->syn[cur].out)) - { - fclose(fin); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - - cur++; - } - - fclose(fin); - - d->len = cur; - if (cur > 1) - qsort(d->syn, d->len, sizeof(Syn), compareSyn); - - pfree(filename); - PG_RETURN_POINTER(d); -} - -Datum -syn_lexize(PG_FUNCTION_ARGS) -{ - DictSyn *d = (DictSyn *) PG_GETARG_POINTER(0); - char *in = (char *) PG_GETARG_POINTER(1); - Syn key, - *found; - TSLexeme *res = NULL; - char *wrd; - - if (!PG_GETARG_INT32(2)) - PG_RETURN_POINTER(NULL); - - key.out = NULL; - wrd = pnstrdup(in, PG_GETARG_INT32(2)); - key.in = lowerstr(wrd); - pfree(wrd); - - found = (Syn *) bsearch(&key, d->syn, d->len, sizeof(Syn), compareSyn); - pfree(key.in); - - if (!found) - PG_RETURN_POINTER(NULL); - - res = palloc(sizeof(TSLexeme) * 2); - memset(res, 0, sizeof(TSLexeme) * 2); - res[0].lexeme = pstrdup(found->out); - - PG_RETURN_POINTER(res); -} diff --git a/contrib/tsearch2/docs/tsearch-V2-intro.html b/contrib/tsearch2/docs/tsearch-V2-intro.html deleted file mode 100644 index 8b2514e5bec0..000000000000 --- a/contrib/tsearch2/docs/tsearch-V2-intro.html +++ /dev/null @@ -1,1073 +0,0 @@ - - - - tsearch-v2-intro - - -
-

Tsearch2 - Introduction

-

-[Online version] of this document is available.

- -

The tsearch2 module is available to add as an extension to the -PostgreSQL database to allow for Full Text Indexing. This document -is an introduction to installing, configuring, using and -maintaining the database with the tsearch2 module activated.

-

Please, note, tsearch2 module is fully incompatible with old -tsearch, which is deprecated in 7.4 and will be obsoleted in -8.0.

-

USING TSEARCH2 AND POSTGRESQL FOR A WEB BASED SEARCH -ENGINE

-

This documentation is provided as a short guide on how to -quickly get up and running with tsearch2 and PostgreSQL, for those -who want to implement a full text indexed based search engine. It -is not meant to be a complete in-depth guide into the full ins and -outs of the contrib/tsearch2 module, and is primarily aimed at -beginners who want to speed up searching of large text fields, or -those migrating from other database systems such as MS-SQL.

-

The README.tsearch2 file included in the contrib/tsearch2 -directory contains a brief overview and history behind tsearch. -This can also be found online [right -here].

-

Further in depth documentation such as a full function -reference, and user guide can be found online at the [tsearch -documentation home].

-

ACKNOWLEDGEMENTS

- -

Robert John Shepherd originally wrote this documentation for the -previous version of tsearch module (v1) included with the postgres -release. I took his documentation and updated it to comply with the -tsearch2 modifications.

-

Robert's original acknowledgements:

-

"Thanks to Oleg Bartunov for taking the time to answer many of -my questions regarding this module, and also to Teodor Sigaev for -clearing up the process of making your own dictionaries. Plus of -course a big thanks to the pair of them for writing this module in -the first place!"

-

I would also like to extend my thanks to the developers, and -Oleg Bartunov for all of his direction and help with the new -features of tsearch2.

-

OVERVIEW

-

MS-SQL provides a full text indexing (FTI) system which enables -the fast searching of text based fields, very useful for websites -(and other applications) that require a results set based on key -words. PostgreSQL ships with a contributed module called tsearch2, -which implements a special type of index that can also be used for -full text indexing. Further more, unlike MS' offering which -requires regular incremental rebuilds of the text indexes -themselves, tsearch2 indexes are always up-to-date and keeping them -so induces very little overhead.

-

Before we get into the details, it is recommended that you have -installed and tested PostgreSQL, are reasonably familiar with -databases, the SQL query language and also understand the basics of -connecting to PostgreSQL from the local shell. This document isn't -intended for the complete PostgreSQL newbie, but anyone with a -reasonable grasp of the basics should be able to follow it.

-

INSTALLATION

-

Starting with PostgreSQL version 7.4 tsearch2 is now included in -the contrib directory with the PostgreSQL sources. contrib/tsearch2 -is where you will find everything needed to install and use -tsearch2. Please note that tsearch2 will also work with PostgreSQL -version 7.3.x, but it is not the module included with the source -distribution. You will have to download the module separately and -install it in the same fashion.

- -

I installed the tsearch2 module to a PostgreSQL 7.3 database -from the contrib directory without squashing the original (old) -tsearch module. What I did was move the modules tsearch src -driectory into the contrib tree under the name tsearchV2.

-

Step one is to download the tsearch V2 module :

-

[http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/] -(check Development History for latest stable version !)

-
-        tar -zxvf tsearch-v2.tar.gz
-        mv tsearch2 $PGSQL_SRC/contrib/
-        cd $PGSQL_SRC/contrib/tsearch2
-
-

If you are installing from PostgreSQL version 7.4 or higher, you -can skip those steps and just change to the contrib/tsearch2 -directory in the source tree and continue from there.

-

As of May 9, 2004 there is a source patch available for -tsearch2. The patch provides changes to the pg_ts_ configuration -tables to allow for easy dump and restore of a database containing -tsearch2. The patch is available here : -[http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/regprocedure_7.4.patch.gz]

- -

To apply this patch, download the mentioned file and place it in -your postgreSQL source tree ($PGSQL_SRC). This patch is not -required for tsearch2 to work. I would however, highly recommend it -as it makes the backup and restore procedures very simple.

-
-      cd $PGSQL_SRC
-        gunzip regprocedure_7.4.patch.gz
-        patch -b -p1 < regprocedure_7.4.patch
-
-

If you have a working version of tsearch2 in your database, you -do not need to re-install the tsearch2 module. Just apply the patch -and run make. This patch only affects the tsearch2.sql file. You -can run the SQL script found : -[right here] This script will make the modifications found in -the patch, and update the fields from the existing data. From this -point on, you can dump and restore the database in a normal -fashion. Without this patch, you must follow the instructions later -in this document for backup and restore.

-

This patch is only needed for tsearch2 in PostgreSQL versions -7.3.x and 7.4.x. The patch has been applied to the sources for -8.0.x.

-

When you have your source tree for tsearch2 ready, you can -continue with the regular building and installation process

- -
-        gmake
-        gmake install
-        gmake installcheck
-
-

That is pretty much all you have to do, unless of course you get -errors. However if you get those, you better go check with the -mailing lists over at http://www.postgresql.org or -http://openfts.sourceforge.net/ -since its never failed for me.

-

If you ever need to revert this patch, and go back to the -unpatched version of tsearch2, it is simple if you followed the -above patch command. The -b option creates a backup of the original -file, so we can just copy it back.

-
-     cd $PGSQL_SRC/contrib/tsearch2
-        cp tsearch.sql.in.orig tsearch.sql.in
-        make
-
-
-

If you need the patched version again, just follow the patch -instructions again.

-

The directory in the contib/ and the directory from the archive -is called tsearch2. Tsearch2 is completely incompatible with the -previous version of tsearch. This means that both versions can be -installed into a single database, and migration the new version may -be much easier.

-

NOTE: the previous version of tsearch found in the -contrib/tsearch directory is depricated. Although it is still -available and included within PostgreSQL version 7.4. It will be -removed in version 8.0.

-

ADDING TSEARCH2 FUNCTIONALITY TO A DATABASE

-

We should create a database to use as an example for the -remainder of this file. We can call the database "ftstest". You can -create it from the command line like this:

-
-        #createdb ftstest
-
-

If you thought installation was easy, this next bit is even -easier. Change to the PGSQL_SRC/contrib/tsearch2 directory and -type:

- -
-        psql ftstest < tsearch2.sql
-
-

The file "tsearch2.sql" holds all the wonderful little goodies -you need to do full text indexing. It defines numerous functions -and operators, and creates the needed tables in the database. There -will be 4 new tables created after running the tsearch2.sql file : -pg_ts_dict, pg_ts_parser, pg_ts_cfg, pg_ts_cfgmap are added.

-

You can check out the tables if you like:

-
-        #psql ftstest
-        ftstest=# \d
-                    List of relations
-         Schema |     Name     | Type  |  Owner
-        --------+--------------+-------+----------
-         public | pg_ts_cfg    | table | kopciuch
-         public | pg_ts_cfgmap | table | kopciuch
-         public | pg_ts_dict   | table | kopciuch
-         public | pg_ts_parser | table | kopciuch
-        (4 rows)
-
-

You may need to grant permissions to use on pg_ts_dict, pg_ts_parser, pg_ts_cfg, pg_ts_cfgmap tables to let non-superuser works with tsearch2. GRANT SELECT should be enough for search-only access.

-

TYPES AND FUNCTIONS PROVIDED BY TSEARCH2

-

The first thing we can do is try out some of the types that are -provided for us. Lets look at the tsvector type provided for -us:

- -
-        SELECT 'Our first string used today'::tsvector;
-                        tsvector
-        ---------------------------------------
-         'Our' 'used' 'first' 'today' 'string'
-        (1 row)
-
-

The results are the words used within our string. Notice they -are not in any particular order. The tsvector type returns a string -of space separated words.

-
-        SELECT 'Our first string used today first string'::tsvector;
-                            tsvector
-        -----------------------------------------------
-         'Our' 'used' 'first' 'today' 'string'
-        (1 row)
-
-

Notice the results string has each unique word ('first' and -'string' only appear once in the tsvector value). Which of course -makes sense if you are searching the full text ... you only need to -know each unique word in the text.

-

Those examples were just casting a text field to that of type -tsvector. Lets check out one of the new functions created by the -tsearch2 module.

-

The function to_tsvector has 3 possible signatures:

-
-
-        to_tsvector(oid, text);
-        to_tsvector(text, text);
-        to_tsvector(text);
-
-

We will use the second method using two text fields. The -overloaded methods provide us with a way to specifiy the way the -searchable text is broken up into words (Stemming process). Right -now we will specify the 'default' configuration. See the section on -TSEARCH2 CONFIGURATION to learn more about this.

-
-        SELECT to_tsvector('default',
-                           'Our first string used today first string');
-                        to_tsvector
-        --------------------------------------------
-         'use':4 'first':2,6 'today':5 'string':3,7
-        (1 row)
-
-

The result returned from this function is of type tsvector. The -results came about by this reasoning: All of the words in the text -passed in are stemmed, or not used because they are stop words -defined in our configuration. Each lower case morphed word is -returned with all of the positons in the text.

-

In this case the word "Our" is a stop word in the default -configuration. That means it will not be included in the result. -The word "first" is found at positions 2 and 6 (although "Our" is a -stop word, it's position is maintained). The word(s) positioning is -maintained exactly as in the original string. The word "used" is -morphed to the word "use" based on the default configuration for -word stemming, and is found at position 4. The rest of the results -follow the same logic. Just a reminder again ... the order of the -'word' position in the output is not in any kind of order. (ie -'use':4 appears first)

-

If you want to view the output of the tsvector fields without -their positions, you can do so with the function -"strip(tsvector)".

-
-        SELECT strip(to_tsvector('default',
-                     'Our first string used today first string'));
-                    strip
-        --------------------------------
-         'use' 'first' 'today' 'string'
-
-
-

If you wish to know the number of unique words returned in the -tsvector you can do so by using the function "length(tsvector)"

-
-        SELECT length(to_tsvector('default',
-                      'Our first string used today first string'));
-         length
-        --------
-              4
-        (1 row)
-
-

Lets take a look at the function to_tsquery. It also has 3 -signatures which follow the same rational as the to_tsvector -function:

-
-        to_tsquery(oid, text);
-        to_tsquery(text, text);
-        to_tsquery(text);
-
-

Lets try using the function with a single word :

-
-        SELECT to_tsquery('default', 'word');
-         to_tsquery
-        -----------
-         'word'
-         (1 row)
-
-
-

I call the function the same way I would a to_tsvector function, -specifying the 'default' configuration for morphing, and the result -is the stemmed output 'word'.

-

Lets attempt to use the function with a string of multiple -words:

-
-        SELECT to_tsquery('default', 'this is many words');
-        ERROR:  Syntax error
-
-

The function can not accept a space separated string. The -intention of the to_tsquery function is to return a type of -"tsquery" used for searching a tsvector field. What we need to do -is search for one to many words with some kind of logic (for now -simple boolean).

-
-        SELECT to_tsquery('default', 'searching|sentence');
-              to_tsquery
-        ----------------------
-         'search' | 'sentenc'
-        (1 row)
-
-

Notice that the words are separated by the boolean logic "OR", -the text could contain boolean operators &,|,!,() with their -usual meaning.

- -

You can not use words defined as being a stop word in your -configuration. The function will not fail ... you will just get no -result, and a NOTICE like this:

-
-        SELECT to_tsquery('default', 'a|is&not|!the');
-        NOTICE:  Query contains only stopword(s)
-                 or doesn't contain lexeme(s), ignored
-         to_tsquery
-        -----------
-        (1 row)
-
-

That is a beginning to using the types, and functions defined in -the tsearch2 module. There are numerous more functions that I have -not touched on. You can read through the tsearch2.sql file built -when compiling to get more familiar with what is included.

-

INDEXING FIELDS IN A TABLE

-

The next stage is to add a full text index to an existing table. -In this example we already have a table defined as follows:

-
-        CREATE TABLE tblMessages
-        (
-                intIndex        int4,
-                strTopic        varchar(100),
-                strMessage      text
-        );
-
- -

We are assuming there are several rows with some kind of data in -them. Any data will do, just do several inserts with test strings -for a topic, and a message. here is some test data I inserted. (yes -I know it's completely useless stuff ;-) but it will serve our -purpose right now).

-
-        INSERT INTO tblMessages
-               VALUES ('1', 'Testing Topic', 'Testing message data input');
-        INSERT INTO tblMessages
-               VALUES ('2', 'Movie', 'Breakfast at Tiffany\'s');
-        INSERT INTO tblMessages
-               VALUES ('3', 'Famous Author', 'Stephen King');
-        INSERT INTO tblMessages
-               VALUES ('4', 'Political Topic',
-                            'Nelson Mandella is released from prison');
-        INSERT INTO tblMessages
-               VALUES ('5', 'Nursery rhyme phrase',
-                            'Little jack horner sat in a corner');
-        INSERT INTO tblMessages
-               VALUES ('6', 'Gettysburg address quotation',
-                            'Four score and seven years ago'
-                            ' our fathers brought forth on this'
-                            ' continent a new nation, conceived in'
-                            ' liberty and dedicated to the proposition'
-                            ' that all men are created equal');
-        INSERT INTO tblMessages
-               VALUES ('7', 'Classic Rock Bands',
-                            'Led Zeppelin Grateful Dead and The Sex Pistols');
-        INSERT INTO tblMessages
-               VALUES ('8', 'My birth address',
-                            '18 Sommervile road, Regina, Saskatchewan');
-        INSERT INTO tblMessages
-               VALUES ('9', 'Joke', 'knock knock : who\'s there?'
-                                    ' I will not finish this joke');
-        INSERT INTO tblMessages
-               VALUES ('10', 'Computer information',
-                             'My computer is a pentium III 400 mHz'
-                             ' with 192 megabytes of RAM');
-
-

The next stage is to create a special text index which we will -use for FTI, so we can search our table of messages for words or a -phrase. We do this using the SQL command:

-
-        ALTER TABLE tblMessages ADD COLUMN idxFTI tsvector;
-
-

Note that unlike traditional indexes, this is actually a new -field in the same table, which is then used (through the magic of -the tsearch2 operators and functions) by a special index we will -create in a moment.

-

The general rule for the initial insertion of data will follow -four steps:

-
-
-    1. update table
-    2. vacuum full analyze
-    3. create index
-    4. vacuum full analyze
-
-

The data can be updated into the table, the vacuum full analyze -will reclaim unused space. The index can be created on the table -after the data has been inserted. Having the index created prior to -the update will slow down the process. It can be done in that -manner, this way is just more efficient. After the index has been -created on the table, vacuum full analyze is run again to update -postgres's statistics (ie having the index take effect).

-
-        UPDATE tblMessages SET idxFTI=to_tsvector('default', strMessage);
-        VACUUM FULL ANALYZE;
-
-

Note that this only inserts the field strMessage as a tsvector, -so if you want to also add strTopic to the information stored, you -should instead do the following, which effectively concatenates the -two fields into one before being inserted into the table:

-
-        UPDATE tblMessages
-            SET idxFTI=to_tsvector('default',coalesce(strTopic,'') ||' '|| coalesce(strMessage,''));
-        VACUUM FULL ANALYZE;
-
-

Using the coalesce function makes sure this -concatenation also works with NULL fields.

- -

We need to create the index on the column idxFTI. Keep in mind -that the database will update the index when some action is taken. -In this case we _need_ the index (The whole point of Full Text -INDEXING ;-)), so don't worry about any indexing overhead. We will -create an index based on the gist or gin function. GiST is an index -structure for Generalized Search Tree, GIN is a inverted index (see The tsearch2 Reference: Indexes).

-
-        CREATE INDEX idxFTI_idx ON tblMessages USING gist(idxFTI);
-        VACUUM FULL ANALYZE;
-
-

After you have converted all of your data and indexed the -column, you can select some rows to see what actually happened. I -will not display output here but you can play around yourselves and -see what happened.

-

The last thing to do is set up a trigger so every time a row in -this table is changed, the text index is automatically updated. -This is easily done using:

-
-        CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON tblMessages
-            FOR EACH ROW EXECUTE PROCEDURE tsearch2(idxFTI, strMessage);
-
-

Or if you are indexing both strMessage and strTopic you should -instead do:

-
-
-        CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON tblMessages
-            FOR EACH ROW EXECUTE PROCEDURE
-                tsearch2(idxFTI, strTopic, strMessage);
-
-

Before you ask, the tsearch2 function accepts multiple fields as -arguments so there is no need to concatenate the two into one like -we did before.

-

If you want to do something specific with columns, you may write -your very own trigger function using plpgsql or other procedural -languages (but not SQL, unfortunately) and use it instead of -tsearch2 trigger.

-

You could however call other stored procedures from within the -tsearch2 function. Lets say we want to create a function to remove -certain characters (like the @ symbol from all text).

-
-       CREATE FUNCTION dropatsymbol(text) 
-                     RETURNS text AS 'select replace($1, \'@\', \' \');' LANGUAGE SQL;
-
-

Now we can use this function within the tsearch2 function on the -trigger.

- -
-      DROP TRIGGER tsvectorupdate ON tblmessages;
-        CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON tblMessages
-            FOR EACH ROW EXECUTE PROCEDURE tsearch2(idxFTI, dropatsymbol, strMessage);
-        INSERT INTO tblmessages VALUES (69, 'Attempt for dropatsymbol', 'Test@test.com');
-
-

If at this point you receive an error stating: ERROR: Can't find -tsearch config by locale

-

Do not worry. You have done nothing wrong. And tsearch2 is not -broken. All that has happened here is that the configuration is -setup to use a configuration based on the locale of the server. All -you have to do is change your default configuration, or add a new -one for your specific locale. See the section on TSEARCH2 -CONFIGURATION.

-
-   SELECT * FROM tblmessages WHERE intindex = 69;
-
-         intindex |         strtopic         |  strmessage   |        idxfti
-        ----------+--------------------------+---------------+-----------------------   
-                69 | Attempt for dropatsymbol | Test@test.com | 'test':1 'test.com':2
-        (1 row)
-
-Notice that the string content was passed throught the stored -procedure dropatsymbol. The '@' character was replaced with a -single space ... and the output from the procedure was then stored -in the tsvector column. -

This could be useful for removing other characters from indexed -text, or any kind of preprocessing needed to be done on the text -prior to insertion into the index.

-

QUERYING A TABLE

- -

There are some examples in the README.tsearch2 file for querying -a table. One major difference between tsearch and tsearch2 is the -operator ## is no longer available. Only the operator @@ is -defined, using the types tsvector on one side and tsquery on the -other side.

-

Lets search the indexed data for the word "Test". I indexed -based on the the concatenation of the strTopic, and the -strMessage:

-
-        SELECT intindex, strtopic FROM tblmessages
-                                  WHERE idxfti @@ 'test'::tsquery;
-         intindex |   strtopic
-        ----------+---------------
-                1 | Testing Topic
-        (1 row)
-
-

The only result that matched was the row with a topic "Testing -Topic". Notice that the word I search for was all lowercase. Let's -see what happens when I query for uppercase "Test".

-
-        SELECT intindex, strtopic FROM tblmessages
-                                  WHERE idxfti @@ 'Test'::tsquery;
-         intindex | strtopic
-        ----------+----------
-        (0 rows)
-
-

We get zero rows returned. The reason is because when the text -was inserted, it was morphed to my default configuration (because -of the call to to_tsvector in the UPDATE statement). If there was -no morphing done, and the tsvector field(s) contained the word -'Text', a match would have been found.

-

Most likely the best way to query the field is to use the -to_tsquery function on the right hand side of the @@ operator like -this:

- -
-        SELECT intindex, strtopic FROM tblmessages
-               WHERE idxfti @@ to_tsquery('default', 'Test | Zeppelin');
-         intindex |      strtopic
-        ----------+--------------------
-                1 | Testing Topic
-                7 | Classic Rock Bands
-        (2 rows)
-
-

That query searched for all instances of "Test" OR "Zeppelin". -It returned two rows: the "Testing Topic" row, and the "Classic -Rock Bands" row. The to_tsquery function performed the correct -morphology upon the parameters, and searched the tsvector field -appropriately.

-

The last example here relates to searching for a phrase, for -example "minority report". This poses a problem with regard to -tsearch2, as it doesn't index phrases, only words. But there is a -way around which doesn't appear to have a significant impact on -query time, and that is to use a query such as the following:

-
-        SELECT intindex, strTopic FROM tblmessages
-                WHERE idxfti @@ to_tsquery('default', 'gettysburg & address')
-                AND strMessage ~* '.*men are created equal.*';
-         intindex |           strtopic
-        ----------+------------------------------
-                6 | Gettysburg address quotation
-        (1 row)
-        SELECT intindex, strTopic FROM tblmessages
-                WHERE idxfti @@ to_tsquery('default', 'gettysburg & address')
-                AND strMessage ~* '.*something that does not exist.*';
-         intindex | strtopic
-        ----------+----------
-        (0 rows)
-
-

Of course if your indexing both strTopic and strMessage, and -want to search for this phrase on both, then you will have to get -out the brackets and extend this query a little more.

- -

TSEARCH2 CONFIGURATION

-

Some words such as "and", "the", and "who" are automatically not -indexed, since they belong to a pre-existing dictionary of "Stop -Words" which tsearch2 does not perform indexing on. If someone -needs to search for "The Who" in your database, they are going to -have a tough time coming up with any results, since both are -ignored in the indexes. But there is a solution.

-

Lets say we want to add a word into the stop word list for -english stemming. We could edit the file -:'/usr/local/pgsql/share/english.stop' and add a word to the list. -I edited mine to exclude my name from indexing:

-
-    - Edit /usr/local/pgsql/share/english.stop
-    - Add 'andy' to the list
-    - Save the file.
-
-

When you connect to the database, the dict_init procedure is run -during initialization. And in my configuration it will read the -stop words from the file I just edited. If you were connected to -the DB while editing the stop words, you will need to end the -current session and re-connect. When you re-connect to the -database, 'andy' is no longer indexed:

-
-        SELECT to_tsvector('default', 'Andy');
-         to_tsvector
-        ------------
-        (1 row)
-
-

Originally I would get the result :

- -
-        SELECT to_tsvector('default', 'Andy');
-         to_tsvector
-        ------------
-         'andi':1
-        (1 row)
-
-

But since I added it as a stop word, it would be ingnored on the -indexing. The stop word added was used in the dictionary "en_stem". -If I were to use a different configuration such as 'simple', the -results would be different. There are no stop words for the simple -dictionary. It will just convert to lower case, and index every -unique word.

-
-        SELECT to_tsvector('simple', 'Andy andy The the in out');
-                     to_tsvector
-        -------------------------------------
-         'in':5 'out':6 'the':3,4 'andy':1,2
-        (1 row)
-
-

All this talk about which configuration to use is leading us -into the actual configuration of tsearch2. In the examples in this -document the configuration has always been specified when using the -tsearch2 functions:

-
-        SELECT to_tsvector('default', 'Testing the default config');
-        SELECT to_tsvector('simple', 'Example of simple Config');
-
-

The pg_ts_cfg table holds each configuration you can use with -the tsearch2 functions. As you can see the ts_name column contains -both the 'default' configurations based on the 'C' locale. And the -'simple' configuration which is not based on any locale.

- -
-        SELECT * from pg_ts_cfg;
-             ts_name     | prs_name |    locale
-        -----------------+----------+--------------
-         default         | default  | C
-         default_russian | default  | ru_RU.KOI8-R
-         simple          | default  |
-        (3 rows)
-
-

Each row in the pg_ts_cfg table contains the name of the -tsearch2 configuration, the name of the parser to use, and the -locale mapped to the configuration. There is only one parser to -choose from the table pg_ts_parser called 'default'. More parsers -could be written, but for our needs we will use the default.

-

There are 3 configurations installed by tsearch2 initially. If -your locale is set to 'en_US' for example (like my laptop), then as -you can see there is currently no dictionary configured to use with -that locale. You can either set up a new configuration or just use -one that already exists. If I do not specify which configuration to -use in the to_tsvector function, I receive the following error.

-
-        SELECT to_tsvector('learning tsearch is like going to school');
-        ERROR:  Can't find tsearch config by locale
-
-

We will create a new configuration for use with the server -encoding 'en_US'. The first step is to add a new configuration into -the pg_ts_cfg table. We will call the configuration -'default_english', with the default parser and use the locale -'en_US'.

-
-        INSERT INTO pg_ts_cfg (ts_name, prs_name, locale)
-               VALUES ('default_english', 'default', 'en_US');
-
-
-

We have only declared that there is a configuration called -'default_english'. We need to set the configuration of how -'default_english' will work. The next step is creating a new -dictionary to use. The configuration of the dictionary is -completlely different in tsearch2. In the prior versions to make -changes, you would have to re-compile your changes into the -tsearch.so. All of the configuration has now been moved into the -system tables created by executing the SQL code from -tsearch2.sql

-

Lets take a first look at the pg_ts_dict table

-
-  ftstest=# \d pg_ts_dict
-         Table "public.pg_ts_dict"
-             Column      |     Type     | Modifiers
-        -----------------+--------------+-----------
-         dict_name       | text         | not null
-         dict_init       | regprocedure |
-         dict_initoption | text         |
-         dict_lexize     | regprocedure | not null
-         dict_comment    | text         |
-        Indexes: pg_ts_dict_pkey primary key btree (dict_name)
-
-

The dict_name column is the name of the dictionary, for example -'simple', 'en_stem' or 'ru_stem'. The dict_init column is a text -representation of a stored procedure to run for initialization of -that dictionary, for example 'snb_en_init(text)' or -'snb_ru_init(text)'. The initial configuration of tsearch2 had the -dict_init and dict_lexize columns as type oid. The patch mentioned -in the Installation Notes changes these types to regprocedure. The -data inserted, or updated can still be the oid of the stored -procedure. The representation is just different. This makes backup -and restore procedures much easier for tsearch2. The dict_init -option is used for options passed to the init function for the -stored procedure. In the cases of 'en_stem' or 'ru_stem' it is a -path to a stopword file for that dictionary, for example -'/usr/local/pgsql/share/english.stop'. This is however dictated by -the dictionary. ISpell dictionaries may require different options. -The dict_lexize column is another OID of a stored procedure to the -function used to lexize, for example 'snb_lexize(internal, -internal, integer)'. The dict_comment column is just a comment.

-

Next we will configure the use of a new dictionary based on -ISpell. We will assume you have ISpell installed on you machine. -(in /usr/local/lib)

-

There has been some confusion in the past as to which files are -used from ISpell. ISpell operates using a hash file. This is a -binary file created by the ISpell command line utility "buildhash". -This utility accepts a file containing the words from the -dictionary, and the affixes file and the output is the hash file. -The default installation of ISPell installs the english hash file -english.hash, which is the exact same file as american.hash. ISpell -uses this as the fallback dictionary to use.

-

This hash file is not what tsearch2 requires as the ISpell -interface. The file(s) needed are those used to create the hash. -Tsearch uses the dictionary words for morphology, so the listing is -needed not spellchecking. Regardless, these files are included in -the ISpell sources, and you can use them to integrate into -tsearch2. This is not complicated, but is not very obvious to begin -with. The tsearch2 ISpell interface needs only the listing of -dictionary words, it will parse and load those words, and use the -ISpell dictionary for lexeme processing.

- -

I found the ISPell make system to be very finicky. Their -documentation actually states this to be the case. So I just did -things the command line way. In the ISpell source tree under -languages/english there are several files in this directory. For a -complete description, please read the ISpell README. Basically for -the english dictionary there is the option to create the small, -medium, large and extra large dictionaries. The medium dictionary -is recommended. If the make system is configured correctly, it -would build and install the english.has file from the medium size -dictionary. Since we are only concerned with the dictionary word -listing ... it can be created from the /languages/english directory -with the following command:

-
-       sort -u -t/ +0f -1 +0 -T /usr/tmp -o english.med english.0 english.1
-
-

This will create a file called english.med. You can copy this -file to whever you like. I placed mine in /usr/local/lib so it -coincides with the ISpell hash files. You can now add the tsearch2 -configuration entry for the ISpell english dictionary. We will also -continue to use the english word stop file that was installed for -the en_stem dictionary. You could use a different one if you like. -The ISpell configuration is based on the "ispell_template" -dictionary installed by default with tsearch2. We will use the OIDs -to the stored procedures from the row where the dict_name = -'ispell_template'.

-
-        INSERT INTO pg_ts_dict
-               (SELECT 'en_ispell',
-                       dict_init,
-                       'DictFile="/usr/local/lib/english.med",'
-                       'AffFile="/usr/local/lib/english.aff",'
-                       'StopFile="/usr/local/pgsql/share/contrib/english.stop"',
-                       dict_lexize
-                FROM pg_ts_dict
-                WHERE dict_name = 'ispell_template');
-
-

Now that we have a dictionary we can specify it's use in a query -to get a lexeme. For this we will use the lexize function. The -lexize function takes the name of the dictionary to use as an -argument. Just as the other tsearch2 functions operate. You will -need to stop your psql session and start it again in order for this -modification to take place.

-
-  SELECT lexize('en_ispell', 'program');
-          lexize
-        -----------
-         {program}
-        (1 row)
-
-
-

If you wanted to always use the ISpell english dictionary you -have installed, you can configure tsearch2 to always use a specific -dictionary.

-
-  SELECT set_curdict('en_ispell');
-
-

Lexize is meant to turn a word into a lexeme. It is possible to -receive more than one lexeme returned for a single word.

-
- SELECT lexize('en_ispell', 'conditionally');
-           lexize
-        -----------------------------
-         {conditionally,conditional}
-        (1 row)
-
-

The lexize function is not meant to take a full string as an -argument to return lexemes for. If you passed in an entire sentence, -it attempts to find that entire sentence in the dictionary. Since -the dictionary contains only words, you will receive an empty -result set back.

-
-      SELECT lexize('en_ispell', 'This is a senctece to lexize');
-         lexize
-        --------
-        
-        (1 row)
-        
-If you parse a lexeme from a word not in the dictionary, then you will receive an empty result. This makes sense because the word "tsearch" is not in the english dictionary. You can create your own additions to the dictionary if you like. This may be useful for scientific or technical glossaries that need to be indexed. SELECT lexize('en_ispell', 'tsearch'); lexize -------- (1 row)
-
-
-

This is not to say that tsearch will be ignored when adding text -information to the the tsvector index column. This will be -explained in greater detail with the table pg_ts_cfgmap.

-

Next we need to set up the configuration for mapping the -dictionay use to the lexxem parsings. This will be done by altering -the pg_ts_cfgmap table. We will insert several rows, specifying to -use the new dictionary we installed and configured for lexizing -within tsearch2. There are several type of lexims we would be -concerned with forcing the use of the ISpell dictionary.

-
-        INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
-               VALUES ('default_english', 'lhword', '{en_ispell,en_stem}');
-        INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
-               VALUES ('default_english', 'lpart_hword', '{en_ispell,en_stem}');
-        INSERT INTO pg_ts_cfgmap (ts_name, tok_alias, dict_name)
-               VALUES ('default_english', 'lword', '{en_ispell,en_stem}');
-
-

We have just inserted 3 records to the configuration mapping, -specifying that the lexeme types for "lhword, lpart_hword and lword" -are to be stemmed using the 'en_ispell' dictionary we added into -pg_ts_dict, when using the configuration ' default_english' which -we added to pg_ts_cfg.

-

There are several other lexeme types used that we do not need to -specify as using the ISpell dictionary. We can simply insert values -using the 'simple' stemming process dictionary.

-
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'url', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'host', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'sfloat', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'uri', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'int', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'float', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'email', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'word', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'hword', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'nlword', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'nlpart_hword', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'part_hword', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'nlhword', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'file', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'uint', '{simple}');
-        INSERT INTO pg_ts_cfgmap
-               VALUES ('default_english', 'version', '{simple}');
-
- -

Our addition of a configuration for 'default_english' is now -complete. We have successfully created a new tsearch2 -configuration. At the same time we have also set the new -configuration to be our default for en_US locale.

-
-        SELECT to_tsvector('default_english',
-                           'learning tsearch is like going to school');
-                           to_tsvector
-        --------------------------------------------------
-         'go':5 'like':4 'learn':1 'school':7 'tsearch':2
-        SELECT to_tsvector('learning tsearch is like going to school');
-                            to_tsvector
-        --------------------------------------------------
-         'go':5 'like':4 'learn':1 'school':7 'tsearch':2
-        (1 row)
-
-

Notice here that words like "tsearch" are still parsed and -indexed in the tsvector column. There is a lexeme returned for the -word becuase in the configuration mapping table, we specify words -to be used from the 'en_ispell' dictionary first, but as a fallback -to use the 'en_stem' dictionary. Therefore a lexeme is not returned -from en_ispell, but is returned from en_stem, and added to the -tsvector.

-
- SELECT to_tsvector('learning tsearch is like going to computer school');
-                                to_tsvector
-        ---------------------------------------------------------------------------
-         'go':5 'like':4 'learn':1 'school':8 'compute':7 'tsearch':2 'computer':7
-        (1 row)
-
-

Notice in this last example I added the word "computer" to the -text to be converted into a tsvector. Because we have setup our -default configuration to use the ISpell english dictionary, the -words are lexized, and computer returns 2 lexemes at the same -position. 'compute':7 and 'computer':7 are now both indexed for the -word computer.

-

You can create additional dictionary lists, or use the extra -large dictionary from ISpell. You can read through the ISpell -documents, and source tree to make modifications as you see -fit.

-

In the case that you already have a configuration set for the -locale, and you are changing it to your new dictionary -configuration. You will have to set the old locale to NULL. If we -are using the 'C' locale then we would do this:

- -
-        UPDATE pg_ts_cfg SET locale=NULL WHERE locale = 'C';
-
-

That about wraps up the configuration of tsearch2. There is much -more you can do with the tables provided. This was just an -introduction to get things working rather quickly.

-

ADDING NEW DICTIONARIES TO TSEARCH2

-

To aid in the addition of new dictionaries to the tsearch2 -module you can use another additional module in combination with -tsearch2. The gendict module is included into tsearch2 distribution -and is available from gendict/ subdirectory.

-

I will not go into detail about installation and instructions on -how to use gendict to it's fullest extent right now. You can read -the README.gendict ... it has all of the instructions and -information you will need.

-

BACKING UP AND RESTORING DATABASES THAT FEATURE TSEARCH2

-

Never rely on anyone elses instructions to backup and -restore a database system, always develop and understand your own -methodology, and test it numerous times before you need to do it -for real.

-

The backup and restore procedure has changed over time. This is -not meant to be the bible for tsearch2 back up and restore. Please -read all sections so you have a complete understanding of some -backup and restore issues. Please test your own procedures, and do -not rely on these instructions solely.

- -

If you come accross some issues in your own procedures, please -feel free to bring the question up on the Open-FTS, and PostgreSQL -mailing lists.

-

ORIGINAL BACKUP PROCEDURES

-

Originally, tsearch2 had problems when using the pg_dump, and or -the pg_dumpall utilities. The problem lies within the original use -of OIDs for column types. Since OIDs are not consistent accross -pg_dumps, when you reload the data values into the pg_ts_dict -table, for example, those oids no longer point to anything. You -would then end up trying to use a "broken" tsearch2 -configuration.

-

The solution was to backup and restore a database using the -tsearch2 module into small unique parts, and then load them in the -correct order. You would have to edit the schema and remove the -tsearch stored procedure references in the sql file. You would have -to load your global objects, then the tsearch2 objects. You had to -re-create the tsearch module before restoring your schema so no -conflicts would arise. Then you could restore your data (all -schemas, and types needed for the data were now available).

-

The original backup instructions were as -follows

-

1) Backup any global database objects such as users and groups -(this step is usually only necessary when you will be restoring to -a virgin system)

-
-        pg_dumpall -g > GLOBALobjects.sql
-
-
-

2) Backup the full database schema using pg_dump

-
-        pg_dump -s DATABASE > DATABASEschema.sql
-
-

3) Backup the full database using pg_dump

-
-        pg_dump -Fc DATABASE > DATABASEdata.tar
-
- -

The original restore procedures were as -follows

-

1) Create the blank database

-
-        createdb DATABASE
-
-

2) Restore any global database objects such as users and groups -(this step is usually only necessary when you will be restoring to -a virgin system)

-
-        psql DATABASE < GLOBALobjects.sql
-
-

3) Create the tsearch2 objects, functions and operators

- -
-        psql DATABASE < tsearch2.sql
-
-

4) Edit the backed up database schema and delete all SQL -commands which create tsearch2 related functions, operators and -data types, BUT NOT fields in table definitions that specify -tsvector types. If your not sure what these are, they are the ones -listed in tsearch2.sql. Then restore the edited schema to the -database

-
-        psql DATABASE < DATABASEschema.sql
-
-

5) Restore the data for the database

-
-
-        pg_restore -N -a -d DATABASE DATABASEdata.tar
-
-

If you get any errors in step 4, it will most likely be because -you forgot to remove an object that was created in tsearch2.sql. -Any errors in step 5 will mean the database schema was probably -restored wrongly.

-

Issues with this procedure

-

As I mentioned before, it is vital that you test out your own -backup and restore procedures. These procedures were originally -adopted from this document's orignal author. Robert John Shepherd. -It makes use of the pg_dump custom archive functionality. I am not -that familiar with the formatting output of pg_dump, and using -pg_restore. I have always had the luxury of using text files -(Everything is DATABASE.sql).

-

One issue not forseen in the case of using a binary dump is the -when you have added more than the default tsearch2 configurations. -Upon reload of the data it will fail due to duplicate primary keys. -If you load the tsearch2 module, and then delete the data loaded by -tsearch2 into the configuration tables, the data will restore. The -configurations are incorrect because you can not remove the data -using OID references from the custom archive.

-

It would be very simple to fix this problem if the data was not -in an archive format. I do believe all of your data would have been -restored properly and you can get things working fairly easy. All -one would have to do is create the configurations as in the -tsearch2.sql file. And then create your custom configurations -again.

-

I have read in the pg_dump man page that if the tar archive -format is used, it is possible to limit which data is restored -using pg_restore. If anyone has more experience with pg_dump -archives, and pg_restore. Please feel free to test and contribute -your procedure(s).

-

CURRENT BACKUP AND RESTORE PROCEDURES

- -

Currently a backup and restore of a database using the tsearch2 -module can be quite simple. If you have applied the patch mentioned -in the installation instructions prior to tsearch2 installation. -This patch removes the use of the oid column. The text -representation for the stored procedures used are dumped with the -data and the restoration of the data works seemlessly.

-

1) to backup the database

-
-  pg_dump DATABASE > DATABASE.sql
-
-

1) to restore the database

-
-     createdb DATABASE
-        psql -d DATABASE -f DATABASE.sql
-
-

This procedure is now like any normal backup and restore -procedure. I can say whether this has been proven using the pg_dump -archive, and restoring with pg_restore. In theory there should be -no problems with any format after the patch is applied.

- -

This restoration procedure should never be an issue with the -patch applied to version 8.0 of PostgreSQL. Only versions 7.3 and -7.4 are affected. You can avoid any troubles by applying the patch -prior to installation, or running the SQL script provided to live -database before backup and restoring is done.

-
- - diff --git a/contrib/tsearch2/docs/tsearch2-guide.html b/contrib/tsearch2/docs/tsearch2-guide.html deleted file mode 100644 index d2d764580c7b..000000000000 --- a/contrib/tsearch2/docs/tsearch2-guide.html +++ /dev/null @@ -1,1085 +0,0 @@ - - - -tsearch2 guide - - -

The tsearch2 Guide

- -

-Brandon Craig Rhodes
30 June 2003 -
Updated to 8.2 release by Oleg Bartunov, October 2006
-

-This Guide introduces the reader to the PostgreSQL tsearch2 module, -version 2. -More formal descriptions of the module's types and functions -are provided in the tsearch2 Reference, -which is a companion to this document. -

-First we will examine the tsvector and tsquery types -and how they are used to search documents; -next, we will use them to build a simple search engine in SQL; -and finally, we will study the internals of document conversion -and how you might tune the internals to accommodate various searching needs. -

-Once you have tsearch2 working with PostgreSQL, -you should be able to run the examples here exactly as they are typed. -

-


-

Table of Contents

-
-Introduction to FTS with tsearch2
-Vectors and Queries
-A Simple Search Engine
-Ranking and Position Weights
-Casting Vectors and Queries
-Parsing and Lexing
-Additional information -
- -
- - -

Introduction to FTS with tsearch2

-The purpose of FTS is to -find documents, which satisfy query and optionally return -them in some order. -Most common case: Find documents containing all query terms and return them in order -of their similarity to the query. Document in database can be -any text attribute, or combination of text attributes from one or many tables -(using joins). -Text search operators existed for years, in PostgreSQL they are -~,~*, LIKE, ILIKE, but they lack linguistic support, -tends to be slow and have no relevance ranking. The idea behind tsearch2 is -is rather simple - preprocess document at index time to save time at search stage. -Preprocessing includes -
    -
  • document parsing onto words -
  • linguistic - normalize words to obtain lexemes -
  • store document in optimized for searching way -
-Tsearch2, in a nutshell, provides FTS operator (contains) for two new data types, -which represent document and query - tsquery @@ tsvector. - -

-

Vectors and Queries

- -
-This section introduces -the two data types upon which tsearch2 search engines are based, -and illustrates their interaction using the simplest possible case. -The complex examples we present later on -are merely variations and elaborations of this basic mechanism. -
-

-The tsearch2 module allows you to index documents by the words they contain, -and then perform very efficient searches -for documents that contain a given combination of words. -Preparing your document index involves two steps: -

    -
  • Making a list of the words each document contains. - You must reduce each document to a tsvector - which lists each word that appears in the document. - This process offers many options, - because there is no requirement - that you must copy words into the vector - exactly as they appear in the document. - For example, - many developers omit frequent and content-free stop words - like the to reduce the size of their index; - others reduce different forms of the same word - (forked, forking, forks) - to a common form (fork) - to make search results independent of tense and case. - Because words are very often stored in a modified form, - we use the special term lexemes - for the word forms we actually store in the vector. -
  • Creating an index of the documents by lexeme. - This is managed automatically by tsearch2 - when you creat a gist() index - on the tsvector column of a table, - which implements a form of the Berkeley - Generalized Search Tree. - Since PostgreSQL 8.2 tsearch2 supports Gin index, - which is an inverted index, commonly used in search engines. It adds scalability to tsearch2. -
-Once your documents are indexed, -performing a search involves: -
    -
  • Reducing the search terms to lexemes. - You must express each search you want to perform - as a tsquery specifying a boolean combination of lexemes. - Note that tsearch2 only finds exact matches - between the lexemes in your query and the ones in each vector — - even capitalization counts as a difference - (which is why all lexemes are usually kept lowercase). - So you must process search words the same way you processed document words; - if forking became fork in the document's tsvector, - then the search term forking must also become fork - or the search will not find the document. -
  • Retrieving the documents that match the query. - Running a SELECT ... WHERE - query @@ vector - on the table with the vector column - will return the documents that match your query. -
  • Presenting your results. - This final stage offers as many options - as turning documents into vectors. - You can order documents by how well they matched the search terms; - create a headline for each document - showing some of the phrases in which it uses the search terms; - and restrict the number of results retrieved. - You will of course want some way to identify each document, - so the user can ask for the full text of the ones he wants to read. -
-And beyond deciding upon rules for turning documents into vectors -and for presenting search results to users, -you have to decide where to perform these operations — -whether one database server -will parse documents, perform searches, and prepare search results, -or whether to spread the load of these operations across several machines. -These are complicated design issues -which we will explore later; -in this section and the next, -we will illustrate what can be accomplished -using a single database server. -

-The default tsearch2 configuration, -which we will learn more about later, -provides a good example of a process for reducing documents to vectors: - -

-=# SELECT set_curcfg('default')
-=# SELECT to_tsvector('The air smells of sea water.')
-             to_tsvector             
--------------------------------------
- 'air':2 'sea':5 'smell':3 'water':6
-(1 row)
-
- -Note the complex relationship between this document and its vector. -The vector lists only words from the document — -spaces and punctuation have disappeared. -Common words like the and of have been eliminated. -The -s that makes smells a plural has been removed, -leaving a lexeme that represents the word in its simplest form. -And finally, -though the vector remembers the positions in which each word appeared, -it does not store the lexemes in that order. -

-Keeping word positions in your vectors is optional, by the way. -The positions are necessary for the tsearch2 ranking functions, -which you can use to prioritize documents -based on how often each document uses the search terms -and whether they appear in close proximity. -But if you do not perform ranking, -or use your own process that ignores the word positions stored in the vector, -then you can save space by stripping them from your vectors: - -

-=# SELECT strip(to_tsvector('The air smells of sea water.'))
-            strip            
------------------------------
- 'air' 'sea' 'smell' 'water'
-(1 row)
-
- -Now that we have a procedure for creating vectors, -we can build an indexed table of vectors very simply: - -
-=# CREATE TABLE vectors ( vector tsvector )
-=# CREATE INDEX vector_index ON vectors USING gist(vector)
-=# INSERT INTO vectors VALUES (to_tsvector('The path forks here'))
-=# INSERT INTO vectors VALUES (to_tsvector('A crawl leads west'))
-=# INSERT INTO vectors VALUES (to_tsvector('The left fork leads northeast'))
-=# SELECT * FROM vectors
-                  vector                  
-------------------------------------------
- 'fork':3 'path':2
- 'lead':3 'west':4 'crawl':2
- 'fork':3 'lead':4 'left':2 'northeast':5
-(3 rows)
-
- -Now we can search this collection of document vectors -using the @@ operator and a tsquery -that specifies the combination of lexemes we are looking for. -Note that while vectors simply list lexemes, -queries always combine them with the operators -‘&’ and, -‘|’ or, -and  ‘!’ not, -plus parentheses for grouping. -Some examples of the query syntax: - - - - - -
‘find documents with the word forks in them’
-
'forks' -
‘... with both forks and leads
-
'forks & leads' -
‘... with either forks or leads
-
'forks | leads' -
‘... with either forks or leads, - but without crawl
-
'(forks|leads) & !crawl' -
-The tsearch2 module -provides a to_tsquery() function for creating queries -that uses the same process as to_tsvector() uses -to reduce words to lexemes. -For instance, -it will remove the -s from the plurals in the last example above: - -
-=# SELECT to_tsquery('(leads|forks) & !crawl')
-           to_tsquery           
---------------------------------
- ( 'lead' | 'fork' ) & !'crawl'
-(1 row)
-
- -Again, -this is critically important because the search operator @@ -only finds exact matches -between the words in a query and the words in a vector; -if the document vector lists the lexeme fork -but the query looks for the plural form forks, -the query would not match that document. -Thanks to the symmetry between our process -for producing vectors and queries, however, -the above searches return correct results: - -
-=# SELECT * FROM vectors WHERE vector @@ to_tsquery('(leads|forks) & !crawl')
-                  vector                  
-------------------------------------------
- 'fork':3 'path':2
- 'fork':3 'lead':4 'left':2 'northeast':5
-(2 rows)
-
- -You may want to try the other queries shown above, -and perhaps invent some of your own. -

-You should not include stop words in a query, -since you cannot search for words you have discarded. -If you throw out the word the when building vectors, for example, -your index will obviously not know which documents included it. -The to_tsquery() function will automatically detect this -and give you an error to prevent this mistake: - -

-=# SELECT to_tsquery('the')
-NOTICE:  Query contains only stopword(s) or doesn't contain lexem(s), ignored
- to_tsquery 
-------------
- 
-(1 row)
-
- -But if you every build vectors and queries using your own routines, -a possibility we will discuss later, -then you will need to enforce this rule yourself. - -
-Now that you understand how vectors and queries work together, -you are prepared to tackle many additional topics: -how to distribute searching across many servers; -how to customize the process -by which tsearch2 turns documents and queries into lexemes, -or use a process of your own; -and how to sort and display search results to your users. -But before discussing these detailed questions, -we will build a simple search engine -to see how easily its basic features work together. -
- -

A Simple Search Engine

- -
-In this section we build a simple search engine out of SQL functions -that use the vector and query types described in the previous section. -While this example is simpler -than a search engine that has to interface with the outside world, -it will illustrate the basic principles of building a search engine, -and better prepare you for developing your own. -
-Building a search engine involves only a few improvements -upon the rudimentary vector searches described in the last section. -
    -
  • Because the user wants to read documents, not vectors, - you must provide some way - for the full text of each document to be accessed — - either by storing the entire text of each document in the database, - or storing an identifier - like a URL, file name, or document routing number - that lets you fetch the document from other storage. -
  • You can make it easier for user interface code to refer to each document - by providing a unique identifier for each document, - perhaps with a SERIAL column. -
  • Search results should be ordered by relevance. - If you leave word positions in your vectors, - you can either have PostgreSQL ORDER your results - BY a ranking function, - or you can fetch the vectors yourself and perform your own sort. - If you choose to ignore word positions or strip them from your vectors, - you will have to determine relevance yourself, - using either the full text of the document - or other information about each document you may possess. -
  • For each document returned by a search, - you will usually want to display a summary called a headline - that shows short excerpts - illustrating how the document uses the query words. - Headlines are usually generated from the full text of the document, - not from position information in the tsvector, - since excerpts lacking stop words, punctuation, and suffixes - would not be comprehensible. - If you store the full text of each document in the database, - headlines can be generated very simply by a tsearch2 function. - If you store your documents elsewhere, - then you will either have to transmit each document to the database - every time you want to run the headline function on it, - or use your own headline code outside of the database. -
-

-We can easily construct a simple search engine -that accomplishes these goals. -First we build a table that, for each document, -stores a unique identifier, the full text of the document, -and its tsvector: - -

-=# CREATE TABLE docs ( id SERIAL, doc TEXT, vector tsvector )
-=# CREATE INDEX docs_index ON docs USING gist(vector);
-
- -Note that although searches will still work -on tables where you have neglected -to create a gist() index over your vectors, -they will run much more slowly -since they will have to compare the query -against every document vector in the table. -

-Because the table we have created -stores each document in two different ways — -both as text and as a vector — -our INSERT statements must provide the document in both forms. -While more advanced PostgreSQL programmers -might accomplish this with a database trigger or rule, -for this simple example we will use a small SQL function: - -

-=# CREATE FUNCTION insdoc(text) RETURNS void LANGUAGE sql AS
-  'INSERT INTO docs (doc, vector) VALUES ($1, to_tsvector($1));'
-
- -Now, by calling insdoc() several times, -we can populate our table with documents: - -
-=# SELECT insdoc('A low crawl over cobbles leads inward to the west.')
-=# SELECT insdoc('The canyon runs into a mass of boulders -- dead end.')
-=# SELECT insdoc('You are crawling over cobbles in a low passage.')
-=# SELECT insdoc('Cavernous passages lead east, north, and south.')
-=# SELECT insdoc('To the east a low wide crawl slants up.')
-=# SELECT insdoc('You are in the south side chamber.')
-=# SELECT insdoc('The passage here is blocked by a recent cave-in.')
-=# SELECT insdoc('You are in a splendid chamber thirty feet high.')
-
- -Now we can build a search function. -Its SELECT statement is based upon -the same @@ operation illustrated in the previous section. -But instead of returning matching vectors, -we return for each document -its SERIAL identifier, so the user can retrieve it later; -a headline that illustrates its use of the search terms; -and a ranking with which we also order the results. -Our search operation can be coded as a single SELECT statement -returning its own kind of table row, -which we call a finddoc_t: - -
-=# CREATE TYPE finddoc_t AS (id INTEGER, headline TEXT, rank REAL)
-=# CREATE FUNCTION finddoc(text) RETURNS SETOF finddoc_t LANGUAGE sql AS '
-   SELECT id, headline(doc, q), rank(vector, q)
-     FROM docs, to_tsquery($1) AS q
-     WHERE vector @@ q ORDER BY rank(vector, q) DESC'
-
- -This function is a rather satisfactory search engine. -Here is one example search, -after which the user fetches the top-ranking document itself; -with similar commands you can try queries of your own: - -
-=# SELECT * FROM finddoc('passage|crawl')
- id |                       headline                        | rank 
-----+-------------------------------------------------------+------
-  3 | <b>crawling</b> over cobbles in a low <b>passage</b>. | 0.19
-  1 | <b>crawl</b> over cobbles leads inward to the west.   |  0.1
-  4 | <b>passages</b> lead east, north, and south.          |  0.1
-  5 | <b>crawl</b> slants up.                               |  0.1
-  7 | <b>passage</b> here is blocked by a recent  cave-in.  |  0.1
-(5 rows)
-=# SELECT doc FROM docs WHERE id = 3
-                       doc                       
--------------------------------------------------
- You are crawling over cobbles in a low passage.
-(1 row)
-
- -While by default the headline() function -surrounds matching words with <b> and </b> -in order to distinguish them from the surrounding text, -you can provide options that change its behavior; -consult the tsearch2 Reference for more details about -Headline Functions. -

-Though a search may match hundreds or thousands of documents, -you will usually present only ten or twenty results to the user at a time. -This can be most easily accomplished -by limiting your query with a LIMIT -and an OFFSET clause — -to display results ten at a time, for example, -your would generate your first page of results -with LIMIT 10 OFFSET 0, -your second page -with LIMIT 10 OFFSET 10, -your third page -with LIMIT 10 OFFSET 20, -and so forth. -There are two problems with this approach, however. -

-The first problem is the strain of running the query over again -for every page of results the user views. -For small document collections or lightly loaded servers, -this may not be a problem; -but the impact can be high -when a search must repeatedly rank and sort -the same ten thousand results -on an already busy server. -So instead of selecting only one page of results, -you will probably use LIMIT and OFFSET -to return a few dozen or few hundred results, -which you can cache and display to the user one page at a time. -Whether a result cache rewards your effort -will depend principally on the behavior of your users — -how often they even view the second page of results, for instance. -

-The second issue solved by caching involves consistency. -If the database is changing while the user browses their results, -then documents might appear and disappear as they page through them. -In some cases the user might even miss a particular result — -perhaps the one they were looking for — -if, say, its rank improves from 31th to 30th -after they load results 21–30 but before they view results 31–40. -While many databases are static or infrequently updated, -and will not present this problem, -users searching very dymanic document collections -might benefit from the stable results that caches yield. - -

-Having seen the features of a search engine -implemented entirely within the database, -we will learn about some specific tsearch2 features. -First we will look in more detail at document ranking. -
- -

Ranking and Position Weights

- -
-When we built our simple search engine, -we used the rank() function to order our results. -Here we describe tsearch2 ranking in more detail. -
- -There are two functions with which tsearch2 can rank search results. -They both use the lexeme positions listed in the tsvector, -so you cannot rank vectors -from which these have been removed with strip(). -The rank() function existed in older versions of OpenFTS, -and has the feature that you can assign different weights -to words from different sections of your document. -The rank_cd() uses a recent technique for weighting results -and also allows different weight to be given -to different sections of your document (since 8.2). -

-Both ranking functions allow you to specify, -as an optional last argument, -whether you want their results normalized — -whether the rank returned should be adjusted for document length. -Specifying a last argument of 0 (zero) makes no adjustment; -1 (one) divides the document rank -by the logarithm of the document length; -and 2 divides it by the plain length. -In all of these examples we omit this optional argument, -which is the same as specifying zero — -we are making no adjustment for document length. -

-The rank_cd() function uses an experimental measurement -called cover density ranking that rewards documents -when they make frequent use of the search terms -that are close together in the document. -You can read about the algorithm in more detail -in Clarke et al., - “Relevance Ranking for One to Three Term Queries.” -An optional first argument allows you to tune their formula; -for details -see the section on ranking -in the Reference. -

-Currently tsearch2 supports four different weight labels: -'D', the default weight; -and 'A', 'B', and 'C'. -All vectors created with to_tsvector() -assign the weight 'D' to each position, -which as the default is not displayed when you print a vector out. -

-If you want positions with weights other than 'D', -you have two options: -either you can author a vector directly through the ::tsvector -casting operation, -as described in the following section, -which lets you give each position whichever weight you want; -or you can pass a vector through the setweight() function -which sets all of its position weights to a single value. -An example of the latter: - - -

-=# SELECT vector FROM docs WHERE id = 3
-                 vector                 
-----------------------------------------
- 'low':8 'cobbl':5 'crawl':3 'passag':9
-(1 row)
-=# SELECT setweight(vector, 'A') FROM docs WHERE id = 3
-                 setweight                  
---------------------------------------------
- 'low':8A 'cobbl':5A 'crawl':3A 'passag':9A
-(1 row)
-
- - -Merely changing all of the weights in a vector is not very useful, -of course, -since this results still in all words having the same weight. -But if we parse different parts of a document separately, -giving each section its own weight, -and then concatenate the vectors of each part into a single vector, -the result can be very useful. -We can construct a simple example -in which document titles are given greater weight -that text in the body of the document: - - -
-=# CREATE TABLE tdocs ( id SERIAL, title TEXT, doc TEXT, vector tsvector )
-=# CREATE INDEX tdocs_index ON tdocs USING gist(vector);
-=# CREATE FUNCTION instdoc(text, text) RETURNS void LANGUAGE sql AS
-  'INSERT INTO tdocs (title, doc, vector)
-   VALUES ($1, $2, setweight(to_tsvector($1), ''A'') || to_tsvector($2));'
-
- - -Now words from a document title will be weighted differently -than those in the main text -if we provide the title and body as separate arguments: - - -
-=# SELECT instdoc('Spendid Chamber',
- 'The walls are frozen rivers of orange stone.')
- instdoc 
----------
- 
-(1 row)
-=# SELECT vector FROM tdocs
-                                    vector                                    
-------------------------------------------------------------------------------
- 'wall':4 'orang':9 'river':7 'stone':10 'frozen':6 'chamber':2A 'spendid':1A
-(1 row)
-
- - -Note that although the necessity is unusual, -you can constrain search terms -to only match words from certain sections -by following them with a colon -and a list of the sections in which the word can occur; -by default this list is 'ABCD' -so that search terms match words from all sections. -For example, -here we search for a word both generally, -and then looking only for specific weights: - - -
-=# SELECT title, doc FROM tdocs WHERE vector @@ to_tsquery('spendid')
-      title      |                     doc                      
------------------+----------------------------------------------
- Spendid Chamber | The walls are frozen rivers of orange stone.
-(1 row)
-=# SELECT title, doc FROM tdocs WHERE vector @@ to_tsquery('spendid:A')
-      title      |                     doc                      
------------------+----------------------------------------------
- Spendid Chamber | The walls are frozen rivers of orange stone.
-(1 row)
-=# SELECT title, doc FROM tdocs WHERE vector @@ to_tsquery('spendid:D')
- title | doc 
--------+-----
-(0 rows)
-
- - - - -
-Our examples so far use tsearch2 to parse our documents into vectors. -When your application needs absolute control over vector content, -you will want to use direct type casting, -which is described in the next section. -
- -

Casting Vectors and Queries

- -
-While tsearch2 has powerful and flexible ways -to process documents and turn them into document vectors, -you will sometimes want to parse documents on your own -and place the results directly in vectors. -Here we show you how. -
- -In the preceding examples, -we used the to_tsvector() function -when we needed a document's text reduced to a document vector. -We saw that the function stripped whitespace and punctuation, -eliminated common words, -and altered suffixes to reduce words to a common form. -While these operations are often desirable, -and while in the sections below -we will gain precise control over this process, -there are occasions on which -you want to avoid the changes that to_tsvector() makes to text -and specify explicitly the words that you want in your vectors. -Or you may want to create queries directly -rather than through to_tsquery(). -

-For example, -you may have already developed your own routine -for reducing your documents to searchable lexemes, -and do not want your carefully generated terms altered -by passing them through to_tsvector(). -Or you might be developing and debugging parsing routines of your own -that you are not ready to load into the database. -In either case, -you will find that direct insertion is easily accomplished -if you simply follow some simple rules. -

-Vectors are created directly -when you cast a string of whitespace separated lexemes -to the tsvector type: - - -

-=# select 'the only exit is the way you came in'::tsvector
-                     tsvector                     
---------------------------------------------------
- 'in' 'is' 'the' 'way' 'you' 'came' 'exit' 'only'
-(1 row)
-
- - -Notice that the conversion interpreted the string -simply as a list of lexemes to be included in the vector. -Their order was lost, -as was the number of times each lexeme appeared. -You must keep in mind that directly creating vectors with casting -is not an alternate means of parsing; -it is a way of directly entering lexemes into a vector without parsing. -

-Queries can also be created through casting, -if you separate lexemes with boolean operators -rather than with whitespace. -When creating your own vectors and queries, -remember that the search operator @@ -finds only exact matches between query lexemes and vector lexemes -— -if they are not exactly the same string, -they will not be considered a match. -

-To include lexeme positions in your vector, -write the positions exactly the way tsearch2 displays them -when it prints vectors: -by following each lexeme with a colon -and a comma-separated list of integer positions. -If you list a lexeme more than once, -then all the positions listed for it are combined into a single list. -For example, -here are two ways of writing the same vector, -depending on whether you mention ‘the’ twice -or combine its positions into a list yourself: - - -

-=# select 'the:1 only:2 exit:3 is:4 the:5 way:6 you:7 came:8 in:9'::tsvector
-                              tsvector                              
---------------------------------------------------------------------
- 'in':9 'is':4 'the':1,5 'way':6 'you':7 'came':8 'exit':3 'only':2
-(1 row)
-=# select 'the:1,5 only:2 exit:3 is:4 way:6 you:7 came:8 in:9'::tsvector
-                              tsvector                              
---------------------------------------------------------------------
- 'in':9 'is':4 'the':1,5 'way':6 'you':7 'came':8 'exit':3 'only':2
-(1 row)
-
- - -Things can get slightly tricky -if you want to include apostrophes, backslashes, or spaces -inside your lexemes -(wanting to include either of the latter would be unusual, -but they can be included if you follow the rules). -The main problem is that the apostrophe and backslash -are important both to PostgreSQL when it is interpreting a string, -and to the tsvector conversion function. -You may want to review section - -“String Constants” -in the PostgreSQL documentation before proceeding. -

-When you cast strings directly into vectors: -

    -
  • The string is interpreted as a whitespace-separated list of lexemes, - any of which can be suffixed with a colon and a list of positions. -
  • A lexeme can be quoted by preceding it with an apostrophe, - in which case it runs until the next apostrophe; - otherwise a lexeme ends with the first whitespace or colon encountered. -
  • Any character preceded by a backslash, - including whitespace, the apostrophe, the colon, and the backslash itself, - loses its normal meaning and is treated as a letter. - Backslashes are effective - both inside and outside of apostrophe-quoted lexemes. -
  • A lexeme can be suffixed with a list of positions - by appending a colon and a comma-separated list of integers, - each of which can itself be followed by a letter - to designate a position weight - (position weights are described below). -
- -Here are some example strings, -showing the lexeme you want to insert -together with the string that the ::tsvector operator -needs to see, -and how you would type that string at the PostgreSQL prompt: - - - - - - - - - - - - -
For the lexeme... -you need the string... -which you can type as: -
nugget -nugget -'nugget' -
won't -won't -'won''t' -
pinin' -pinin' -'pinin''' -
'bout -\'bout -'\\''bout' -
white mist -white\ mist -'white\\ mist' -
or: -'white mist' -'''white mist''' -
won't budge -won\'t\ budge -'won\\''t\\ budge' -
or: -'won\'t budge' -'''won\\''t budge''' -
back\slashed -back\\slashed -'back\\\\slashed' -
- -Remember to use the quoted quoting shown at the right -only when typing in strings as part of a PostgreSQL query. -If you are providing strings through a library -that automatically quotes them -or provides them in binary form to PostgreSQL, -then you can use the strings in the middle instead — -suitably quoted in the language you are using, of course. -

-Position weights are described below -and can be written exactly as they will be displayed -when you select a weighted vector: - -

-=# select 'weighty:1,3A trivial:2B,4'::tsvector
-           tsvector            
--------------------------------
- 'trivial':2B,4 'weighty':1,3A
-(1 row)
-
- -

-Note that if you are composing SQL queries -in a scripting language like Perl or Python, -that itself considers quotes and backslashes special, -then you may have another quoting layer to deal with -on top of the two layers already shown above. -In such cases you may want to write a function -that performs the necessary quoting for you. - -

-Having seen how to create vectors of your own, -it is time to learn how the native tsearch2 parser -reduces documents to vectors. -
- -

Parsing and Lexing

- -
-The previous section -described how you can bypass the parser provided by tsearch2 -and populate your table of documents -with vectors of your own devising. -But for those interested in the native tsearch2 facilities, -we present here an overview of how it goes about -reducing documents to vectors. -
- -The to_tsvector() function reduces documents to vectors -in two stages. -First, a parser breaks the input document -into short sequences of text called tokens. -Each token is usually a word, space, or piece of punctuation, -though some parsers return larger and more exotic items -like HTML tags as single tokens. -Each token returned by the parser -is either discarded -or passed to a dictionary that converts it into a lexeme. -The resulting lexemes are collected into a vector and returned. -

-The choice of which parser and dictionaries to_tsvector() should use -is controlled by your choice of configuration. -The tsearch2 module comes with several configurations, -and you can define more of your own; -in fact the creation of a new configuration is illustrated below, -in the section on position weights. -

-To learn about parsing in more detail, -we will study this example: - -

-=# select to_tsvector('default',
-     'The walls extend upward for well over 100 feet.')
-                       to_tsvector                        
-----------------------------------------------------------
- '100':8 'feet':9 'wall':2 'well':6 'extend':3 'upward':4
-(1 row)
-
- -Unlike the to_tsvector() calls used in the above examples, -this one specifies the 'default' configuration explicitly. -When we called to_tsvector() in earlier examples -with only one argument, -it used the current configuration, -which is chosen automatically based on your LOCALE -if that locale is mentioned in the pg_ts_cfg table -(which is shown under the first bullet in the description below). -If your locale is not listed in the table, -your attempts to use the current configuration will return: - -
-ERROR:  Can't find tsearch2 config by locale
-
- -You can always change the current configuration manually -by calling the set_curcfg() function -described in the section on -Configurations -in the Reference. -

-Each configuration serves as an index into two different tables: -in pg_ts_cfg it determines -which parser will break our text into tokens, -and in pg_ts_cfgmap -it directs each token to a dictionary for processing. -The steps in detail are: - -

    -
  • -

    First, our text is parsed, -using the parser listed for our configuration in the pg_ts_cfg table. -We are using the 'default' configuration, -so the table tells us to use the 'default' parser: - -

    -=# SELECT * FROM pg_ts_cfg WHERE ts_name = 'default'
    - ts_name | prs_name | locale 
    ----------+----------+--------
    - default | default  | C
    -(1 row)
    -
    - -So our text will be parsed as though we had called: - -
    -=# select * from parse('default',
    -     'The walls extend upward for well over 100 feet.')
    -
    - -This breaks the text into a list of tokens -which are each labelled with an integer type: -

    -The112walls112extend112upward112for112well112over1121002212feet1.12 -

    -Each word has been assigned type 1; -each space (represented here by a diamond) and the period, type 12; -and the number one hundred, type 22. -We can retrieve the alias for each type -through the token_type function: - -

    -=# select * from token_type('default')
    -     where tokid = 1 or tokid = 12 or tokid = 22
    - tokid | alias |      descr       
    --------+-------+------------------
    -     1 | lword | Latin word
    -    12 | blank | Space symbols
    -    22 | uint  | Unsigned integer
    -(3 rows)
    -
    - - -
  • -Next, the tokens are assigned to dictionaries -by looking up their type aliases in pg_ts_cfgmap -to determine which dictionary should process each token. -Since we are using the 'default' configuration: - -
    -=# select * from pg_ts_cfgmap where ts_name = 'default' and
    -      (tok_alias = 'lword' or tok_alias = 'blank' or tok_alias = 'uint')
    - ts_name | tok_alias | dict_name 
    ----------+-----------+-----------
    - default | lword     | {en_stem}
    - default | uint      | {simple}
    -(2 rows)
    -
    - -Since this map provides no dictionary for blank tokens, -the spaces and period are simply discarded, -leaving nine tokens, -which are then numbered by their position: -

    -The1 -walls2 -extend3 -upward4 -for5 -well6 -over7 -1008 -feet9 - -

  • -Finally, the words are reduced to lexemes by their respective dictionaries. -The 100 is submitted to the simple dictionary, -which returns tokens unaltered except for making them lowercase: - -
    -=# select lexize('simple', '100')
    - lexize 
    ---------
    - {100}
    -(1 row)
    -
    - -The other words are submitted to en_stem -which reduces each English word to a linguistic stem, -and then discards stems which belong to its list of stop words; -you can see the list of stop words -in the file whose path is in the dict_initoption field -of the pg_ts_dict table entry for en_stem. -The first three words of our text illustrate respectively -an en_stem stop word, -a word which en_stem alters by stemming, -and a word which en_stem leaves alone: - -
    -=# select lexize('en_stem', 'The')
    - lexize 
    ---------
    - {}
    -(1 row)
    -=# select lexize('en_stem', 'walls')
    - lexize 
    ---------
    - {wall}
    -(1 row)
    -=# select lexize('en_stem', 'extend')
    -  lexize  
    -----------
    - {extend}
    -(1 row)
    -
    - -Once en_stem is done discarding stop words and stemming the rest, -we are left with: -

    -wall2 -extend3 -upward4 -well6 -1008 -feet9 -

    -Which is precisely the result of the example that began this section. -

-Query words are stemmed by the to_tsquery() function -using the same scheme to determine the dictionary for each token, -with the difference that the query parser recognizes as special -the boolean operators that separate query words. - - -

Additional information

-More information about tsearch2 is available from -tsearch2 page. -Also, it's worth to check -tsearch2 wiki pages. - - - - - - diff --git a/contrib/tsearch2/docs/tsearch2-ref.html b/contrib/tsearch2/docs/tsearch2-ref.html deleted file mode 100644 index 7edcc55a9b87..000000000000 --- a/contrib/tsearch2/docs/tsearch2-ref.html +++ /dev/null @@ -1,842 +0,0 @@ - - - -tsearch2 reference - - -

The tsearch2 Reference

- -

-Brandon Craig Rhodes
30 June 2003 (edited by Oleg Bartunov, 2 Aug 2003). -
Massive update for 8.2 release by Oleg Bartunov, October 2006 -

-

-This Reference documents the user types and functions -of the tsearch2 module for PostgreSQL. -An introduction to the module is provided -by the tsearch2 Guide, -a companion document to this one. -

- -

Table of Contents

-
-Vectors and Queries
-Vector Operations
-Query Operations
-Full Text Search Operator
-Configurations
-Testing
-Parsers
-Dictionaries
-Ranking
-Headlines
-Indexes
-Thesaurus dictionary
-
- - - - -

Vectors and Queries

- -Vectors and queries both store lexemes, -but for different purposes. -A tsvector stores the lexemes -of the words that are parsed out of a document, -and can also remember the position of each word. -A tsquery specifies a boolean condition among lexemes. -

-Any of the following functions with a configuration argument -can use either an integer id or textual ts_name -to select a configuration; -if the option is omitted, then the current configuration is used. -For more information on the current configuration, -read the next section on Configurations. -

- -

Vector Operations

- -
-to_tsvector( [configuration,] - document TEXT) RETURNS TSVECTOR -
- Parses a document into tokens, - reduces the tokens to lexemes, - and returns a tsvector which lists the lexemes - together with their positions in the document. - For the best description of this process, - see the section on Parsing and Stemming - in the accompanying tsearch2 Guide. -
- strip(vector TSVECTOR) RETURNS TSVECTOR -
- Return a vector which lists the same lexemes - as the given vector, - but which lacks any information - about where in the document each lexeme appeared. - While the returned vector is thus useless for relevance ranking, - it will usually be much smaller. -
- setweight(vector TSVECTOR, letter) RETURNS TSVECTOR -
- This function returns a copy of the input vector - in which every location has been labeled - with either the letter - 'A', 'B', or 'C', - or the default label 'D' - (which is the default with which new vectors are created, - and as such is usually not displayed). - These labels are retained when vectors are concatenated, - allowing words from different parts of a document - to be weighted differently by ranking functions. -
-
- vector1 || vector2
- concat(vector1 TSVECTOR, vector2 TSVECTOR) - RETURNS TSVECTOR -
- Returns a vector which combines the lexemes and position information - in the two vectors given as arguments. - Position weight labels (described in the previous paragraph) - are retained intact during the concatenation. - This has at least two uses. - First, - if some sections of your document - need be parsed with different configurations than others, - you can parse them separately - and concatenate the resulting vectors into one. - Second, - you can weight words from some sections of you document - more heavily than those from others by: - parsing the sections into separate vectors; - assigning the vectors different position labels - with the setweight() function; - concatenating them into a single vector; - and then providing a weights argument - to the rank() function - that assigns different weights to positions with different labels. -
- length(vector TSVECTOR) RETURNS INT4 -
- Returns the number of lexemes stored in the vector. -
- text::TSVECTOR RETURNS TSVECTOR -
- Directly casting text to a tsvector - allows you to directly inject lexemes into a vector, - with whatever positions and position weights you choose to specify. - The text should be formatted - like the vector would be printed by the output of a SELECT. - See the Casting - section in the Guide for details. -
- tsearch2(vector_column_name[, (my_filter_name | text_column_name1) [...] ], text_column_nameN) -
-tsearch2() trigger used to automatically update vector_column_name, my_filter_name -is the function name to preprocess text_column_name. There are can be many -functions and text columns specified in tsearch2() trigger. -The following rule used: -function applied to all subsequent text columns until next function occurs. -Example, function dropatsymbol replaces all entries of @ -sign by space. -
-CREATE FUNCTION dropatsymbol(text) RETURNS text 
-AS 'select replace($1, ''@'', '' '');'
-LANGUAGE SQL;
-
-CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT 
-ON tblMessages FOR EACH ROW EXECUTE PROCEDURE 
-tsearch2(tsvector_column,dropatsymbol, strMessage);
-
-
- -
-stat(sqlquery text [, weight text]) RETURNS SETOF statinfo -
-Here statinfo is a type, defined as - -CREATE TYPE statinfo as (word text, ndoc int4, nentry int4) - and sqlquery is a query, which returns column tsvector. -

-This returns statistics (the number of documents ndoc and total number nentry of word -in the collection) about column vector tsvector. -Useful to check how good is your configuration and -to find stop-words candidates.For example, find top 10 most frequent words: -

-=# select * from stat('select vector from apod') order by ndoc desc, nentry desc,word limit 10;
-
-Optionally, one can specify weight to obtain statistics about words with specific weight. -
-=# select * from stat('select vector from apod','a') order by ndoc desc, nentry desc,word limit 10;
-
- -
-
-TSVECTOR < TSVECTOR
-TSVECTOR <= TSVECTOR
-TSVECTOR = TSVECTOR
-TSVECTOR >= TSVECTOR
-TSVECTOR > TSVECTOR -
-All btree operations defined for tsvector type. tsvectors compares -with each other using lexicographical order. -
-
- -

Query Operations

- -
-
- to_tsquery( [configuration,] - querytext text) RETURNS TSQUERY[A -
-
- Parses a query, - which should be single words separated by the boolean operators - "&" and, - "|" or, - and "!" not, - which can be grouped using parenthesis. - Each word is reduced to a lexeme using the current - or specified configuration. - Weight class can be assigned to each lexeme entry - to restrict search region - (see setweight for explanation), for example - "fat:a & rats". -
-
- plainto_tsquery( [configuration,] - querytext text) RETURNS TSQUERY -
-
-Transforms unformatted text to tsquery. It is the same as to_tsquery, -but assumes "&" boolean operator between words and doesn't -recognizes weight classes. -
- - querytree(query TSQUERY) RETURNS text -
-This returns a query which actually used in searching in GiST index. -
- text::TSQUERY RETURNS TSQUERY -
- Directly casting text to a tsquery - allows you to directly inject lexemes into a query, - with whatever positions and position weight flags you choose to specify. - The text should be formatted - like the query would be printed by the output of a SELECT. - See the Casting - section in the Guide for details. -
-
- numnode(query TSQUERY) RETURNS INTEGER -
-This returns the number of nodes in query tree -
- TSQUERY && TSQUERY RETURNS TSQUERY -
-AND-ed TSQUERY -
- TSQUERY || TSQUERY RETURNS TSQUERY -
- OR-ed TSQUERY -
- !! TSQUERY RETURNS TSQUERY -
- negation of TSQUERY -
-
-TSQUERY < TSQUERY
-TSQUERY <= TSQUERY
-TSQUERY = TSQUERY
-TSQUERY >= TSQUERY
-TSQUERY > TSQUERY -
-All btree operations defined for tsquery type. tsqueries compares -with each other using lexicographical order. -
-
- -

Query rewriting

-Query rewriting is a set of functions and operators for tsquery type. -It allows to control search at query time without reindexing (opposite to thesaurus), for example, -expand search using synonyms (new york, big apple, nyc, gotham). -

-rewrite() function changes original query by replacing target by sample. -There are three possibilities to use rewrite() function. Notice, that arguments of rewrite() -function can be column names of type tsquery. -

-create table rw (q TSQUERY, t TSQUERY, s TSQUERY);
-insert into rw values('a & b','a', 'c');
-
-
-
rewrite (query TSQUERY, target TSQUERY, sample TSQUERY) RETURNS TSQUERY -
-
-
-=# select rewrite('a & b'::TSQUERY, 'a'::TSQUERY, 'c'::TSQUERY);
-  rewrite
-  -----------
-   'c' & 'b'
-
-
-
rewrite (ARRAY[query TSQUERY, target TSQUERY, sample TSQUERY]) RETURNS TSQUERY -
-
-
-=# select rewrite(ARRAY['a & b'::TSQUERY, t,s]) from rw;
-  rewrite
-  -----------
-   'c' & 'b'
-
-
-
rewrite (query TSQUERY,'select target ,sample from test'::text) RETURNS TSQUERY -
-
-
-=# select rewrite('a & b'::TSQUERY, 'select t,s from rw'::text);
-  rewrite
-  -----------
-   'c' & 'b'
-
-
-
-Two operators defined for tsquery type: -
-
TSQUERY @ TSQUERY
-
- Returns TRUE if right agrument might contained in left argument. -
-
TSQUERY ~ TSQUERY
-
- Returns TRUE if left agrument might contained in right argument. -
-
-To speed up these operators one can use GiST index with gist_tp_tsquery_ops opclass. -
-create index qq on test_tsquery using gist (keyword gist_tp_tsquery_ops);
-
- -

Full Text Search operator

- -
-TSQUERY @@ TSVECTOR
-TSVECTOR @@ TSQUERY -
-
-Returns TRUE if TSQUERY contained in TSVECTOR and -FALSE otherwise. -
-=# select 'cat & rat':: tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector;
- ?column?
- ----------
-  t
-=# select 'fat & cow':: tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector;
- ?column?
- ----------
-  f
-
-
-
- -

Configurations

- -A configuration specifies all of the equipment necessary -to transform a document into a tsvector: -the parser that breaks its text into tokens, -and the dictionaries which then transform each token into a lexeme. -Every call to to_tsvector(), to_tsquery() (described above) -uses a configuration to perform its processing. -Three configurations come with tsearch2: - -
    -
  • default -- Indexes words and numbers, - using the en_stem English Snowball stemmer for Latin-alphabet words - and the simple dictionary for all others. -
  • default_russian -- Indexes words and numbers, - using the en_stem English Snowball stemmer for Latin-alphabet words - and the ru_stem Russian Snowball dictionary for all others. It's default - for ru_RU.KOI8-R locale. -
  • utf8_russian -- the same as default_russian but -for ru_RU.UTF-8 locale. -
  • simple -- Processes both words and numbers - with the simple dictionary, - which neither discards any stop words nor alters them. -
- -The tsearch2 modules initially chooses your current configuration -by looking for your current locale in the locale field -of the pg_ts_cfg table described below. -You can manipulate the current configuration yourself with these functions: - -
- set_curcfg( id INT | ts_name TEXT - ) RETURNS VOID -
- Set the current configuration used by to_tsvector - and to_tsquery. -
- show_curcfg() RETURNS INT4 -
- Returns the integer id of the current configuration. -
- -

-Each configuration is defined by a record in the pg_ts_cfg table: - -

create table pg_ts_cfg (
-	id		int not  null primary key,
-	ts_name		text not null,
-	prs_name	text not null,
-	locale		text
-);
- -The id and ts_name are unique values -which identify the configuration; -the prs_name specifies which parser the configuration uses. -Once this parser has split document text into tokens, -the type of each resulting token -- -or, more specifically, the type's tok_alias -as specified in the parser's lexem_type() table -- -is searched for together with the configuration's ts_name -in the pg_ts_cfgmap table: - -
create table pg_ts_cfgmap (
-	ts_name		text not null,
-	tok_alias	text not null,
-	dict_name	text[],
-	primary key (ts_name,tok_alias)
-);
- -Those tokens whose types are not listed are discarded. -The remaining tokens are assigned integer positions, -starting with 1 for the first token in the document, -and turned into lexemes with the help of the dictionaries -whose names are given in the dict_name array for their type. -These dictionaries are tried in order, -stopping either with the first one to return a lexeme for the token, -or discarding the token if no dictionary returns a lexeme for it. - -

Testing

- -Function ts_debug allows easy testing of your current configuration. -You may always test another configuration using set_curcfg function. -

-Example: -

apod=# select * from ts_debug('Tsearch module for PostgreSQL 7.3.3');
- ts_name | tok_type | description |   token    | dict_name |  tsvector    
----------+----------+-------------+------------+-----------+--------------
- default | lword    | Latin word  | Tsearch    | {en_stem} | 'tsearch'
- default | lword    | Latin word  | module     | {en_stem} | 'modul'
- default | lword    | Latin word  | for        | {en_stem} | 
- default | lword    | Latin word  | PostgreSQL | {en_stem} | 'postgresql'
- default | version  | VERSION     | 7.3.3      | {simple}  | '7.3.3'
-
-Here: -
-
    -
  • tsname - configuration name -
  • tok_type - token type -
  • description - human readable name of tok_type -
  • token - parser's token -
  • dict_name - dictionary used for the token -
  • tsvector - final result
  • -
- - -

Parsers

- -Each parser is defined by a record in the pg_ts_parser table: - -
create table pg_ts_parser (
-	prs_name	text not null,
-	prs_start	regprocedure not null,
-	prs_nexttoken	regprocedure not null,
-	prs_end		regprocedure not null,
-	prs_headline	regprocedure not null,
-	prs_lextype	regprocedure not null,
-	prs_comment	text
-);
- -The prs_name uniquely identify the parser, -while prs_comment usually describes its name and version -for the reference of users. -The other items identify the low-level functions -which make the parser operate, -and are only of interest to someone writing a parser of their own. -

-The tsearch2 module comes with one parser named default -which is suitable for parsing most plain text and HTML documents. -

-Each parser argument below -must designate a parser with prs_name; -the current parser is used when this argument is omitted. - -

- CREATE FUNCTION set_curprs(parser) RETURNS VOID -
- Selects a current parser - which will be used when any of the following functions - are called without a parser as an argument. -
- CREATE FUNCTION token_type( - [ parser ] - ) RETURNS SETOF tokentype -
- Returns a table which defines and describes - each kind of token the parser may produce as output. - For each token type the table gives the tokid - which the parser will label each token of that type, - the alias which names the token type, - and a short description descr for the user to read. -
- CREATE FUNCTION parse( - [ parser, ] document TEXT - ) RETURNS SETOF tokenout -
- Parses the given document and returns a series of records, - one for each token produced by parsing. - Each token includes a tokid giving its type - and a lexem which gives its content. -
- -

Dictionaries

- -Dictionary is a program, which accepts lexeme(s), usually those produced by a parser, -on input and returns: -
    -
  • array of lexeme(s) if input lexeme is known to the dictionary -
  • void array - dictionary knows lexeme, but it's stop word. -
  • NULL - dictionary doesn't recognized input lexeme -
-Usually, dictionaries used for normalization of words ( ispell, stemmer dictionaries), -but see, for example, intdict dictionary (available from -Tsearch2 home page, -which controls indexing of integers. - -

-Among the dictionaries which come installed with tsearch2 are: - -

    -
  • simple simply folds uppercase letters to lowercase - before returning the word. -
  • -
  • ispell_template - template for ispell dictionaries. -
  • -
  • en_stem runs an English Snowball stemmer on each word - that attempts to reduce the various forms of a verb or noun - to a single recognizable form. -
  • ru_stem_koi8, ru_stem_utf8 runs a Russian Snowball stemmer on each word. -
  • -
  • synonym - simple lexeme-to-lexeme replacement -
  • -
  • thesaurus_template - template for thesaurus dictionary. It's -phrase-to-phrase replacement -
  • -
- -

-Each dictionary is defined by an entry in the pg_ts_dict table: - -

CREATE TABLE pg_ts_dict (
-	dict_name	text not null,
-	dict_init	regprocedure,
-	dict_initoption	text,
-	dict_lexize	regprocedure not null,
-	dict_comment	text
-);
- -The dict_name -serve as unique identifiers for the dictionary. -The meaning of the dict_initoption varies among dictionaries, -but for the built-in Snowball dictionaries -it specifies a file from which stop words should be read. -The dict_comment is a human-readable description of the dictionary. -The other fields are internal function identifiers -useful only to developers trying to implement their own dictionaries. - -
-WARNING: Data files, used by dictionaries, should be in server_encoding to -avoid possible problems ! -
- -

-The argument named dictionary -in each of the following functions -should be dict_name -identifying which dictionary should be used for the operation; -if omitted then the current dictionary is used. - -

- CREATE FUNCTION set_curdict(dictionary) RETURNS VOID -
- Selects a current dictionary for use by functions - that do not select a dictionary explicitly. -
- CREATE FUNCTION lexize( - [ dictionary, ] word text) - RETURNS TEXT[] -
- Reduces a single word to a lexeme. - Note that lexemes are arrays of zero or more strings, - since in some languages there might be several base words - from which an inflected form could arise. -
- -

Using dictionaries template

-Templates used to define new dictionaries, for example, -
-INSERT INTO pg_ts_dict
-               (SELECT 'en_ispell', dict_init,
-                       'DictFile="/usr/local/share/dicts/ispell/english.dict",'
-                       'AffFile="/usr/local/share/dicts/ispell/english.aff",'
-                       'StopFile="/usr/local/share/dicts/english.stop"',
-                       dict_lexize
-               FROM pg_ts_dict
-               WHERE dict_name = 'ispell_template');
-
- -

Working with stop words

-Ispell and snowball stemmers treat stop words differently: -
    -
  • ispell - normalize word and then lookups normalized form in stop-word file -
  • snowball stemmer - first, it lookups word in stop-word file and then does it job. -The reason - to minimize possible 'noise'. -
- -

Ranking

- -Ranking attempts to measure how relevant documents are to particular queries -by inspecting the number of times each search word appears in the document, -and whether different search terms occur near each other. -Note that this information is only available in unstripped vectors -- -ranking functions will only return a useful result -for a tsvector which still has position information! -

-Notice, that ranking functions supplied are just an examples and -doesn't belong to the tsearch2 core, you can -write your very own ranking function and/or combine additional -factors to fit your specific interest. -

- -The two ranking functions currently available are: - -
- CREATE FUNCTION rank(
- [ weights float4[], ] - vector TSVECTOR, query TSQUERY, - [ normalization int4 ]
- ) RETURNS float4
-
- This is the ranking function from the old version of OpenFTS, - and offers the ability to weight word instances more heavily - depending on how you have classified them. - The weights specify how heavily to weight each category of word: -
{D-weight, C-weight, B-weight, A-weight}
- If no weights are provided, then these defaults are used: -
{0.1, 0.2, 0.4, 1.0}
- Often weights are used to mark words from special areas of the document, - like the title or an initial abstract, - and make them more or less important than words in the document body. -
- CREATE FUNCTION rank_cd(
- [ weights float4[], ] - vector TSVECTOR, query TSQUERY, - [ normalization int4 ]
- ) RETURNS float4
-
- This function computes the cover density ranking - for the given document vector and query, - as described in Clarke, Cormack, and Tudhope's - "Relevance Ranking for One to Three Term Queries" - in the 1999 Information Processing and Management. -
-
- CREATE FUNCTION get_covers(vector TSVECTOR, query TSQUERY) RETURNS text -
-
- Returns extents, which are a shortest and non-nested sequences of words, which satisfy a query. - Extents (covers) used in rank_cd algorithm for fast calculation of proximity ranking. - In example below there are two extents - {1...}1 and {2 ...}2. -
-=# select get_covers('1:1,2,10 2:4'::tsvector,'1& 2');
-get_covers
-----------------------
-1 {1 1 {2 2 }1 1 }2
-
-
- -
- -

-Both of these (rank(), rank_cd()) ranking functions -take an integer normalization option -that specifies whether a document's length should impact its rank. -This is often desirable, -since a hundred-word document with five instances of a search word -is probably more relevant than a thousand-word document with five instances. -The option can have the values, which could be combined using "|" ( 2|4) to -take into account several factors: - -

-
    -
  • 0 (the default) ignores document length.
  • -
  • 1 divides the rank by the 1 + logarithm of the length
  • -
  • 2 divides the rank by the length itself.
  • -
  • 4 divides the rank by the mean harmonic distance between extents
  • -
  • 8 divides the rank by the number of unique words in document
  • -
  • 16 divides the rank by 1 + logarithm of the number of unique words in document -
  • -
- -

Headlines

- -
- CREATE FUNCTION headline(
- [ id int4, | ts_name text, ] - document text, query TSQUERY, - [ options text ]
- ) RETURNS text
-
- Every form of the the headline() function - accepts a document along with a query, - and returns one or more ellipse-separated excerpts from the document - in which terms from the query are highlighted. - The configuration with which to parse the document - can be specified by either its id or ts_name; - if none is specified that the current configuration is used instead. -

- An options string if provided should be a comma-separated list - of one or more 'option=value' pairs. - The available options are: -

    -
  • StartSel, StopSel -- - the strings with which query words appearing in the document - should be delimited to distinguish them from other excerpted words. -
  • MaxWords, MinWords -- - limits on the shortest and longest headlines you will accept. -
  • ShortWord -- - this prevents your headline from beginning or ending - with a word which has this many characters or less. - The default value of 3 should eliminate most English - conjunctions and articles. -
  • HighlightAll -- - boolean flag, if TRUE, than the whole document will be highlighted. -
- Any unspecified options receive these defaults: -
StartSel=<b>, StopSel=</b>, MaxWords=35, MinWords=15, ShortWord=3, HighlightAll=FALSE
- 
-
- - -

Indexes

-Tsearch2 supports indexed access to tsvector in order to further speedup FTS. Notice, indexes are not mandatory for FTS ! -
    -
  • RD-Tree (Russian Doll Tree, matryoshka), based on GiST (Generalized Search Tree) -
        
    -    =# create index fts_idx on apod using gist(fts);
    -
    -
  • GIN - Generalized Inverted Index -
           
    -        =# create index fts_idx on apod using gin(fts);
    -
    -
-GiST index is very good for online update, but is not as scalable as GIN index, -which, in turn, isn't good for updates. Both indexes support concurrency and recovery. - -

Thesaurus dictionary

- -

-Thesaurus - is a collection of words with included information about the relationships of words and phrases, -i.e., broader terms (BT), narrower terms (NT), preferred terms, non-preferred, related terms,etc.

-

Basically,thesaurus dictionary replaces all non-preferred terms by one preferred term and, optionally, -preserves them for indexing. Thesaurus used when indexing, so any changes in thesaurus require reindexing. -Tsearch2's thesaurus dictionary (TZ) is an extension of synonym dictionary -with phrase support. Thesaurus is a plain file of the following format: -

-# this is a comment 
-sample word(s) : indexed word(s)
-...............................
-
-
    -
  • Colon (:) symbol used as a delimiter.
  • -
  • Use asterisk (*) at the beginning of indexed word to skip subdictionary. -It's still required, that sample words should be known.
  • -
  • thesaurus dictionary looks for the most longest match
-

-TZ uses subdictionary (should be defined in tsearch2 configuration) -to normalize thesaurus text. It's possible to define only one dictionary. -Notice, that subdictionary produces an error, if it couldn't recognize word. -In that case, you should remove definition line with this word or teach subdictionary to know it. -

-

Stop-words recognized by subdictionary replaced by 'stop-word placeholder', i.e., -important only their position. -To break possible ties thesaurus applies the last definition. For example, consider -thesaurus (with simple subdictionary) rules with pattern 'swsw' -('s' designates stop-word and 'w' - known word):

-
-a one the two : swsw
-the one a two : swsw2
-
-

Words 'a' and 'the' are stop-words defined in the configuration of a subdictionary. -Thesaurus considers texts 'the one the two' and 'that one then two' as equal and will use definition -'swsw2'.

-

As a normal dictionary, it should be assigned to the specific lexeme types. -Since TZ has a capability to recognize phrases it must remember its state and interact with parser. -TZ use these assignments to check if it should handle next word or stop accumulation. -Compiler of TZ should take care about proper configuration to avoid confusion. -For example, if TZ is assigned to handle only lword lexeme, then TZ definition like -' one 1:11' will not works, since lexeme type digit doesn't assigned to the TZ.

- -

Configuration

- -
tsearch2

tsearch2 comes with thesaurus template, which could be used to define new dictionary:

-
INSERT INTO pg_ts_dict
-               (SELECT 'tz_simple', dict_init,
-                        'DictFile="/path/to/tz_simple.txt",'
-                        'Dictionary="en_stem"',
-                       dict_lexize
-                FROM pg_ts_dict
-                WHERE dict_name = 'thesaurus_template');
-
-
-

Here:

-
    -
  • tz_simple - is the dictionary name
  • -
  • DictFile="/path/to/tz_simple.txt" - is the location of thesaurus file
  • -
  • Dictionary="en_stem" defines dictionary (snowball english stemmer) to use for thesaurus normalization. Notice, that en_stem dictionary has it's own configuration (stop-words, for example).
  • -
-

Now, it's possible to use tz_simple in pg_ts_cfgmap, for example:

-
-update pg_ts_cfgmap set dict_name='{tz_simple,en_stem}' where ts_name = 'default_russian' and 
-tok_alias in ('lhword', 'lword', 'lpart_hword');
-
-

Examples

-

tz_simple:

-
-one : 1
-two : 2
-one two : 12
-the one : 1
-one 1 : 11
-
-

To see, how thesaurus works, one could use to_tsvector, to_tsquery or plainto_tsquery functions:

=# select plainto_tsquery('default_russian',' one day is oneday');
-    plainto_tsquery
-------------------------
- '1' & 'day' & 'oneday'
-
-=# select plainto_tsquery('default_russian','one two day is oneday');
-     plainto_tsquery
--------------------------
- '12' & 'day' & 'oneday'
-
-=# select plainto_tsquery('default_russian','the one');
-NOTICE:  Thesaurus: word 'the' is recognized as stop-word, assign any stop-word (rule 3)
- plainto_tsquery
------------------
- '1'
-
- -Additional information about thesaurus dictionary is available from -Wiki page. - diff --git a/contrib/tsearch2/expected/tsearch2.out b/contrib/tsearch2/expected/tsearch2.out index f069fec5121d..4ad999decb79 100755 --- a/contrib/tsearch2/expected/tsearch2.out +++ b/contrib/tsearch2/expected/tsearch2.out @@ -388,9 +388,9 @@ select numnode( 'new & york | qwery'::tsquery ); create table test_tsquery (txtkeyword text, txtsample text); \set ECHO none alter table test_tsquery add column keyword tsquery; -update test_tsquery set keyword = to_tsquery('default', txtkeyword); +update test_tsquery set keyword = to_tsquery('english', txtkeyword); alter table test_tsquery add column sample tsquery; -update test_tsquery set sample = to_tsquery('default', txtsample::text); +update test_tsquery set sample = to_tsquery('english', txtsample::text); create unique index bt_tsq on test_tsquery (keyword); select count(*) from test_tsquery where keyword < 'new & york'; count @@ -457,7 +457,7 @@ set enable_seqscan=on; select rewrite('foo & bar & qq & new & york', 'new & york'::tsquery, 'big & apple | nyc | new & york & city'); rewrite ---------------------------------------------------------------------------------- - 'qq' & 'foo' & 'bar' & ( 'city' & 'york' & 'new' | ( 'nyc' | 'apple' & 'big' ) ) + 'foo' & 'bar' & 'qq' & ( 'city' & 'new' & 'york' | ( 'nyc' | 'big' & 'apple' ) ) (1 row) select rewrite('moscow', 'select keyword, sample from test_tsquery'::text ); @@ -469,13 +469,13 @@ select rewrite('moscow', 'select keyword, sample from test_tsquery'::text ); select rewrite('moscow & hotel', 'select keyword, sample from test_tsquery'::text ); rewrite ----------------------------------- - ( 'moskva' | 'moscow' ) & 'hotel' + 'hotel' & ( 'moskva' | 'moscow' ) (1 row) select rewrite('bar & new & qq & foo & york', 'select keyword, sample from test_tsquery'::text ); rewrite ------------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'qq' | 'bar' ) & ( 'nyc' | ( 'appl' & 'big' | 'york' & 'new' ) ) + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) (1 row) select rewrite( ARRAY['moscow', keyword, sample] ) from test_tsquery; @@ -493,7 +493,7 @@ select rewrite( ARRAY['moscow & hotel', keyword, sample] ) from test_tsquery; select rewrite( ARRAY['bar & new & qq & foo & york', keyword, sample] ) from test_tsquery; rewrite ------------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'qq' | 'bar' ) & ( 'nyc' | ( 'appl' & 'big' | 'york' & 'new' ) ) + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) (1 row) select keyword from test_tsquery where keyword @> 'new'; @@ -519,40 +519,40 @@ select keyword from test_tsquery where keyword <@ 'moscow'; 'moscow' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where keyword <@ query; rewrite --------------------- 'moskva' | 'moscow' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow & hotel') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where keyword <@ query; rewrite ----------------------------------- ( 'moskva' | 'moscow' ) & 'hotel' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'bar & new & qq & foo & york') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where keyword <@ query; rewrite ------------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'qq' | 'bar' ) & ( 'nyc' | ( 'appl' & 'big' | 'york' & 'new' ) ) + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where query @> keyword; rewrite --------------------- 'moskva' | 'moscow' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow & hotel') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where query @> keyword; rewrite ----------------------------------- ( 'moskva' | 'moscow' ) & 'hotel' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'bar & new & qq & foo & york') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where query @> keyword; rewrite ------------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'qq' | 'bar' ) & ( 'nyc' | ( 'appl' & 'big' | 'york' & 'new' ) ) + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) (1 row) create index qq on test_tsquery using gist (keyword gist_tp_tsquery_ops); @@ -580,40 +580,40 @@ select keyword from test_tsquery where keyword <@ 'moscow'; 'moscow' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where keyword <@ query; rewrite --------------------- 'moskva' | 'moscow' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow & hotel') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where keyword <@ query; rewrite ----------------------------------- ( 'moskva' | 'moscow' ) & 'hotel' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'bar & new & qq & foo & york') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where keyword <@ query; rewrite ------------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'qq' | 'bar' ) & ( 'nyc' | ( 'appl' & 'big' | 'york' & 'new' ) ) + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where query @> keyword; rewrite --------------------- 'moskva' | 'moscow' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow & hotel') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where query @> keyword; rewrite ----------------------------------- ( 'moskva' | 'moscow' ) & 'hotel' (1 row) -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'bar & new & qq & foo & york') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where query @> keyword; rewrite ------------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'qq' | 'bar' ) & ( 'nyc' | ( 'appl' & 'big' | 'york' & 'new' ) ) + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) (1 row) set enable_seqscan='on'; @@ -623,38 +623,38 @@ select lexize('simple', 'ASD56 hsdkf'); {"asd56 hsdkf"} (1 row) -select lexize('en_stem', 'SKIES Problems identity'); +select lexize('english_stem', 'SKIES Problems identity'); lexize -------------------------- {"skies problems ident"} (1 row) select * from token_type('default'); - tokid | alias | descr --------+--------------+----------------------------------- - 1 | lword | Latin word - 2 | nlword | Non-latin word - 3 | word | Word - 4 | email | Email - 5 | url | URL - 6 | host | Host - 7 | sfloat | Scientific notation - 8 | version | VERSION - 9 | part_hword | Part of hyphenated word - 10 | nlpart_hword | Non-latin part of hyphenated word - 11 | lpart_hword | Latin part of hyphenated word - 12 | blank | Space symbols - 13 | tag | HTML Tag - 14 | protocol | Protocol head - 15 | hword | Hyphenated word - 16 | lhword | Latin hyphenated word - 17 | nlhword | Non-latin hyphenated word - 18 | uri | URI - 19 | file | File or path name - 20 | float | Decimal notation - 21 | int | Signed integer - 22 | uint | Unsigned integer - 23 | entity | HTML Entity + tokid | alias | descr +-------+-----------------+------------------------------------------ + 1 | asciiword | Word, all ASCII + 2 | word | Word, all letters + 3 | numword | Word, letters and digits + 4 | email | Email address + 5 | url | URL + 6 | host | Host + 7 | sfloat | Scientific notation + 8 | version | Version number + 9 | hword_numpart | Hyphenated word part, letters and digits + 10 | hword_part | Hyphenated word part, all letters + 11 | hword_asciipart | Hyphenated word part, all ASCII + 12 | blank | Space symbols + 13 | tag | XML tag + 14 | protocol | Protocol head + 15 | numhword | Hyphenated word, letters and digits + 16 | asciihword | Hyphenated word, all ASCII + 17 | hword | Hyphenated word, all letters + 18 | url_path | URL path + 19 | file | File or path name + 20 | float | Decimal notation + 21 | int | Signed integer + 22 | uint | Unsigned integer + 23 | entity | XML entity (23 rows) select * from parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 @@ -774,15 +774,11 @@ select * from parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc 12 | . 20 | 4.2 12 | , - 15 | readline-4.2 - 11 | readline - 12 | - - 20 | 4.2 + 1 | readline + 20 | -4.2 12 | - 15 | readline-4.2 - 11 | readline - 12 | - - 20 | 4.2 + 1 | readline + 20 | -4.2 12 | . 22 | 234 12 | @@ -799,23 +795,23 @@ select * from parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc 12 | 12 | <> 1 | qwerty -(135 rows) +(131 rows) -SELECT to_tsvector('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 +SELECT to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty'); - to_tsvector ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - 'ad':17 'dw':19 'jf':39 '234':63 '345':1 '4.2':54,55,56,59,62 '455':31 'jqw':66 'qwe':2,18,27,28,35 'wer':36 'wow':65 'asdf':37 'ewr1':43 'qwer':38 'sdjk':40 '5.005':32 'efd.r':3 'ewri2':44 'hjwer':42 'qwqwe':29 'wefjn':48 'gist.c':52 'gist.h':50 'qwerti':67 '234.435':30 'qwe-wer':34 'readlin':53,58,61 'www.com':4 '+4.0e-10':26 'gist.h.c':51 'rewt/ewr':47 '/?ad=qwe&dw':7,10,14,22 '/wqe-324/ewr':49 'aew.werc.ewr':6 'readline-4.2':57,60 '1aew.werc.ewr':9 '2aew.werc.ewr':11 '3aew.werc.ewr':13 '4aew.werc.ewr':15 '/usr/local/fff':45 '/awdf/dwqe/4325':46 'teodor@stack.net':33 '/?ad=qwe&dw=%20%32':25 '5aew.werc.ewr:8100':16 '6aew.werc.ewr:8100':21 '7aew.werc.ewr:8100':24 'aew.werc.ewr/?ad=qwe&dw':5 '1aew.werc.ewr/?ad=qwe&dw':8 '3aew.werc.ewr/?ad=qwe&dw':12 '6aew.werc.ewr:8100/?ad=qwe&dw':20 '7aew.werc.ewr:8100/?ad=qwe&dw=%20%32':23 + to_tsvector +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 'ad':17 'dw':19 'jf':39 '234':61 '345':1 '4.2':54,55,56 '455':31 'jqw':64 'qwe':2,18,27,28,35 'wer':36 'wow':63 '-4.2':58,60 'asdf':37 'ewr1':43 'qwer':38 'sdjk':40 '5.005':32 'efd.r':3 'ewri2':44 'hjwer':42 'qwqwe':29 'wefjn':48 'gist.c':52 'gist.h':50 'qwerti':65 '234.435':30 'qwe-wer':34 'readlin':53,57,59 'www.com':4 '+4.0e-10':26 'gist.h.c':51 'rewt/ewr':47 '/?ad=qwe&dw':7,10,14,22 '/wqe-324/ewr':49 'aew.werc.ewr':6 '1aew.werc.ewr':9 '2aew.werc.ewr':11 '3aew.werc.ewr':13 '4aew.werc.ewr':15 '/usr/local/fff':45 '/awdf/dwqe/4325':46 'teodor@stack.net':33 '/?ad=qwe&dw=%20%32':25 '5aew.werc.ewr:8100':16 '6aew.werc.ewr:8100':21 '7aew.werc.ewr:8100':24 'aew.werc.ewr/?ad=qwe&dw':5 '1aew.werc.ewr/?ad=qwe&dw':8 '3aew.werc.ewr/?ad=qwe&dw':12 '6aew.werc.ewr:8100/?ad=qwe&dw':20 '7aew.werc.ewr:8100/?ad=qwe&dw=%20%32':23 (1 row) -SELECT length(to_tsvector('default', '345 qw')); +SELECT length(to_tsvector('english', '345 qw')); length -------- 2 (1 row) -SELECT length(to_tsvector('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 +SELECT length(to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty')); length @@ -823,7 +819,7 @@ SELECT length(to_tsvector('default', '345 qwe@efd.r '' http://www.com/ http://ae 51 (1 row) -select to_tsquery('default', 'qwe & sKies '); +select to_tsquery('english', 'qwe & sKies '); to_tsquery --------------- 'qwe' & 'sky' @@ -835,61 +831,61 @@ select to_tsquery('simple', 'qwe & sKies '); 'qwe' & 'skies' (1 row) -select to_tsquery('default', '''the wether'':dc & '' sKies '':BC '); +select to_tsquery('english', '''the wether'':dc & '' sKies '':BC '); to_tsquery ------------------------ 'wether':CD & 'sky':BC (1 row) -select to_tsquery('default', 'asd&(and|fghj)'); +select to_tsquery('english', 'asd&(and|fghj)'); to_tsquery ---------------- 'asd' & 'fghj' (1 row) -select to_tsquery('default', '(asd&and)|fghj'); +select to_tsquery('english', '(asd&and)|fghj'); to_tsquery ---------------- 'asd' | 'fghj' (1 row) -select to_tsquery('default', '(asd&!and)|fghj'); +select to_tsquery('english', '(asd&!and)|fghj'); to_tsquery ---------------- 'asd' | 'fghj' (1 row) -select to_tsquery('default', '(the|and&(i&1))&fghj'); +select to_tsquery('english', '(the|and&(i&1))&fghj'); to_tsquery -------------- '1' & 'fghj' (1 row) -select plainto_tsquery('default', 'the and z 1))& fghj'); +select plainto_tsquery('english', 'the and z 1))& fghj'); plainto_tsquery -------------------- 'z' & '1' & 'fghj' (1 row) -select plainto_tsquery('default', 'foo bar') && plainto_tsquery('default', 'asd'); +select plainto_tsquery('english', 'foo bar') && plainto_tsquery('english', 'asd'); ?column? ----------------------- 'foo' & 'bar' & 'asd' (1 row) -select plainto_tsquery('default', 'foo bar') || plainto_tsquery('default', 'asd fg'); +select plainto_tsquery('english', 'foo bar') || plainto_tsquery('english', 'asd fg'); ?column? ------------------------------ 'foo' & 'bar' | 'asd' & 'fg' (1 row) -select plainto_tsquery('default', 'foo bar') || !!plainto_tsquery('default', 'asd fg'); +select plainto_tsquery('english', 'foo bar') || !!plainto_tsquery('english', 'asd fg'); ?column? ----------------------------------- 'foo' & 'bar' | !( 'asd' & 'fg' ) (1 row) -select plainto_tsquery('default', 'foo bar') && 'asd | fg'; +select plainto_tsquery('english', 'foo bar') && 'asd | fg'; ?column? ---------------------------------- 'foo' & 'bar' & ( 'asd' | 'fg' ) @@ -1001,7 +997,7 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; 39 (1 row) -select set_curcfg('default'); +select set_curcfg('english'); set_curcfg ------------ @@ -1030,11 +1026,7 @@ SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); 0 (1 row) -drop trigger tsvectorupdate on test_tsvector; -create function wow(text) returns text as 'select $1 || '' copyright''; ' language sql; -create trigger tsvectorupdate before update or insert on test_tsvector -for each row execute procedure tsearch2(a, wow, t); -insert into test_tsvector (t) values ('345 qwerty'); +insert into test_tsvector (t) values ('345 qwerty copyright'); select count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); count ------- @@ -2141,7 +2133,6 @@ select * from stat('select a from test_tsvector') order by ndoc desc, nentry des 8w | 2 | 2 9f | 2 | 2 9y | 2 | 2 - copyright | 2 | 2 foo | 1 | 3 bar | 1 | 2 0e | 1 | 1 @@ -2233,6 +2224,7 @@ select * from stat('select a from test_tsvector') order by ndoc desc, nentry des 9h | 1 | 1 9r | 1 | 1 9w | 1 | 1 + copyright | 1 | 1 qwerti | 1 | 1 (1146 rows) @@ -2263,11 +2255,11 @@ select * from stat('select a from test_tsvector','d') order by ndoc desc, nentry word | ndoc | nentry -----------+------+-------- a | 2 | 2 - copyright | 2 | 2 foo | 1 | 3 bar | 1 | 2 345 | 1 | 1 b | 1 | 1 + copyright | 1 | 1 qq | 1 | 1 qwerti | 1 | 1 (8 rows) @@ -2277,22 +2269,15 @@ select * from stat('select a from test_tsvector','ad') order by ndoc desc, nentr -----------+------+-------- a | 2 | 4 b | 2 | 4 - copyright | 2 | 2 foo | 1 | 3 bar | 1 | 2 345 | 1 | 1 + copyright | 1 | 1 qq | 1 | 1 qwerti | 1 | 1 (8 rows) -select reset_tsearch(); -NOTICE: TSearch cache cleaned - reset_tsearch ---------------- - -(1 row) - -select to_tsquery('default', 'skies & books'); +select to_tsquery('english', 'skies & books'); to_tsquery ---------------- 'sky' & 'book' @@ -2340,48 +2325,6 @@ Upon a woman s face. E. J. Pratt (1882 1964) 0.2 (1 row) -select get_covers(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('sea&thousand&years')); - get_covers ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - eros took {1 sea thousand year }1 {2 thousand year trace granit featur cliff crag scarp base took sea }2 hour one night hour storm place sculptur granit seam upon woman face e j pratt 1882 1964 -(1 row) - -select get_covers(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('granite&sea')); - get_covers ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - eros took {1 sea thousand year thousand year trace {2 granit }1 featur cliff crag scarp base took {3 sea }2 hour one night hour storm place sculptur granit }3 seam upon woman face e j pratt 1882 1964 -(1 row) - -select get_covers(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('sea')); - get_covers ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - eros took {1 sea }1 thousand year thousand year trace granit featur cliff crag scarp base took {2 sea }2 hour one night hour storm place sculptur granit seam upon woman face e j pratt 1882 1964 -(1 row) - select headline('Erosion It took the sea a thousand years, A thousand years to trace The granite features of this cliff @@ -2462,15 +2405,19 @@ to_tsquery('sea&foo'), 'HighlightAll=true'); (1 row) --check debug -select * from ts_debug('Tsearch module for PostgreSQL 7.3.3'); - ts_name | tok_type | description | token | dict_name | tsvector ----------+----------+-------------+------------+-----------+-------------- - default | lword | Latin word | Tsearch | {en_stem} | 'tsearch' - default | lword | Latin word | module | {en_stem} | 'modul' - default | lword | Latin word | for | {en_stem} | - default | lword | Latin word | PostgreSQL | {en_stem} | 'postgresql' - default | version | VERSION | 7.3.3 | {simple} | '7.3.3' -(5 rows) +select * from public.ts_debug('Tsearch module for PostgreSQL 7.3.3'); + ts_name | tok_type | description | token | dict_name | tsvector +---------+-----------+-----------------+------------+----------------+-------------- + english | asciiword | Word, all ASCII | Tsearch | {english_stem} | 'tsearch' + english | blank | Space symbols | | {} | + english | asciiword | Word, all ASCII | module | {english_stem} | 'modul' + english | blank | Space symbols | | {} | + english | asciiword | Word, all ASCII | for | {english_stem} | + english | blank | Space symbols | | {} | + english | asciiword | Word, all ASCII | PostgreSQL | {english_stem} | 'postgresql' + english | blank | Space symbols | | {} | + english | version | Version number | 7.3.3 | {simple} | '7.3.3' +(9 rows) --check ordering insert into test_tsvector values (null, null); @@ -2486,10 +2433,10 @@ select a is null, a from test_tsvector order by a; f | f | f | - f | '345':1 'qwerti':2 'copyright':3 - f | 'qq':7 'bar':2,8 'foo':1,3,6 'copyright':9 f | 'a':1A,2,3B 'b':5A,6A,7C,8 f | 'a':1A,2,3C 'b':5A,6B,7C,8B + f | 'qq':7 'bar':2,8 'foo':1,3,6 + f | '345':1 'qwerti':2 'copyright':3 f | '7w' 'ch' 'd7' 'eo' 'gw' 'i4' 'lq' 'o6' 'qt' 'y0' f | 'ar' 'ei' 'kq' 'ma' 'qa' 'qh' 'qq' 'qz' 'rx' 'st' f | 'gs' 'i6' 'i9' 'j2' 'l0' 'oq' 'qx' 'sc' 'xe' 'yu' diff --git a/contrib/tsearch2/gendict/Makefile.IN b/contrib/tsearch2/gendict/Makefile.IN deleted file mode 100644 index c13e496d06b7..000000000000 --- a/contrib/tsearch2/gendict/Makefile.IN +++ /dev/null @@ -1,12 +0,0 @@ -subdir = contrib/CFG_DIR -top_builddir = ../.. -include $(top_builddir)/src/Makefile.global - -MODULE_big = dict_CFG_MODNAME -OBJS = CFG_OFILE -DATA_built = dict_CFG_MODNAME.sql -DOCS = README.CFG_MODNAME -PG_CPPFLAGS = -SHLIB_LINK = ../tsearch2/libtsearch2.a - -include $(top_srcdir)/contrib/contrib-global.mk diff --git a/contrib/tsearch2/gendict/README.gendict b/contrib/tsearch2/gendict/README.gendict deleted file mode 100755 index e5bd010b038d..000000000000 --- a/contrib/tsearch2/gendict/README.gendict +++ /dev/null @@ -1,131 +0,0 @@ -Gendict - generate dictionary templates for contrib/tsearch2 module. - -This utility aims to help people creating dictionary for contrib/tsearch v2 -module. Particularly, it has built-in support for snowball stemmers. - -Programming API to tsearch2 dictionaries is described in tsearch v2 -documentation. - - -Prerequisities: - -* PostgreSQL 7.3 and above. - -* You need tsearch2 module sources already compiled - -* Rights to install contrib modules - -Usage: - - run config.sh without parameters to see options and arguments - -Usage: -./config.sh -n DICTNAME ( [ -s [ -p PREFIX ] ] | [ -c CFILES ] [ -h HFILES ] [ -i ] ) [ -v ] [ -d DIR ] [ -C COMMENT ] - -v - be verbose - -d DIR - name of directory in PGSQL_SRC/contrib (default dict_DICTNAME) - -C COMMENT - dictionary comment -Generate Snowball stemmer: -./config.sh -n DICTNAME -s [ -p PREFIX ] [ -v ] [ -d DIR ] [ -C COMMENT ] - -s - generate Snowball wrapper - -p - prefix of Snowball's function, (default DICTNAME) -Generate template dictionary: -./config.sh -n DICTNAME [ -c CFILES ] [ -h HFILES ] [ -i ] [ -v ] [ -d DIR ] [ -C COMMENT ] - -c CFILES - source files, must be placed in contrib/tsearch2/gendict directory. - These files will be used in Makefile. - -h HFILES - header files, must be placed in contrib/tsearch2/gendict directory. - These files will be used in Makefile and subinclude.h - -i - dictionary has init method - - -Example 1: - - Create Portuguese stemmer - - 0. cd PGSQL_SRC/contrib/tsearch2/gendict - - 1. Obtain stem.{c,h} files for Portuguese - - wget http://snowball.tartarus.org/portuguese/stem.c - wget http://snowball.tartarus.org/portuguese/stem.h - - 2. Create template files for Portuguese - - ./config.sh -n pt -s -p portuguese_ISO_8859_1 -v -C'Snowball stemmer for Portuguese' - - Note, that argument for -p option should be *the same* as name of stemming - function in stem.c (without _stem) - - A bunch of files will be generated and placed in PGSQL_SRC/contrib/dict_pt - directory. - - 3. Compile and install dictionary - - cd PGSQL_SRC/contrib/dict_pt - make - make install - - 4. Test it - - Sample portuguese words with the stemmed forms are available - from http://snowball.tartarus.org/portuguese/stemmer.html - - createdb testdict - psql testdict < /usr/local/pgsql/share/contrib/tsearch2.sql - psql testdict < /usr/local/pgsql/share/contrib/dict_pt.sql - psql -d testdict -c "select lexize('pt','bobagem');" - lexize - --------- - {bobag} - (1 row) - - Here is what I have in pg_ts_dict table - - psql -d testdict -c "select * from pg_ts_dict where dict_name='pt';" - dict_name | dict_init | dict_initoption | dict_lexize | dict_comment - -----------+--------------------+-----------------+---------------------------------------+--------------------------------- - pt | dinit_pt(internal) | | snb_lexize(internal,internal,integer) | Snowball stemmer for Portuguese - - (1 row) - - - Note, that you have already installed dictionary and corresponding - entry in tsearch configuration and you may modify it using - plain SQL commands, for example, specify stop words. - -Example 2: - - a) Simple template dictionary with init method - - ./config.sh -n wow -v -i -C WOW - - b) Create simple template dict (without init method): - ./config.sh -n wow -v -C WOW - - The same as above, but dictionary will have not init method - - Dictionaries obtained in a) and b) are fully working and ready - for use: - a) lowercase input word and remove it if it is a stop word - b) recognizes any word - - c) Simple template dictionary with source files (with init method): - - ./config.sh -n wow -v -i -c a.c -h a.h -C WOW - - Source files ( a.c ) must be placed in contrib/tsearch2/gendict directory. - These files will be used in Makefile. - - Header files ( a.h ), must be placed in contrib/tsearch2/gendict directory. - These files will be used in Makefile and subinclude.h - - d) Simple template dictionary with source files (without init method): - - ./config.sh -n wow -v -c a.c -h a.h -C WOW - - The same as above, but dictionary will have not init method - - After that you have sources in PGSQL_SRC/contrib/dict_wow and - you may edit them to create actual dictionary. - - Please, check Tsearch2 home page (http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/) - for additional information about "Gendict tutorial" and dictionaries. diff --git a/contrib/tsearch2/gendict/config.sh b/contrib/tsearch2/gendict/config.sh deleted file mode 100755 index 2dc71fb73ef7..000000000000 --- a/contrib/tsearch2/gendict/config.sh +++ /dev/null @@ -1,190 +0,0 @@ -#!/bin/sh - -usage () { - echo Usage: - echo $0 -n DICTNAME \( [ -s [ -p PREFIX ] ] \| [ -c CFILES ] [ -h HFILES ] [ -i ] \) [ -v ] [ -d DIR ] [ -C COMMENT ] - echo ' -v - be verbose' - echo ' -d DIR - name of directory in PGSQL_SRL/contrib (default dict_DICTNAME)' - echo ' -C COMMENT - dictionary comment' - echo Generate Snowball stemmer: - echo $0 -n DICTNAME -s [ -p PREFIX ] [ -v ] [ -d DIR ] [ -C COMMENT ] - echo ' -s - generate Snowball wrapper' - echo " -p - prefix of Snowball's function, (default DICTNAME)" - echo Generate template dictionary: - echo $0 -n DICTNAME [ -c CFILES ] [ -h HFILES ] [ -i ] [ -v ] [ -d DIR ] [ -C COMMENT ] - echo ' -c CFILES - source files, must be placed in contrib/tsearch2/gendict directory.' - echo ' These files will be used in Makefile.' - echo ' -h HFILES - header files, must be placed in contrib/tsearch2/gendict directory.' - echo ' These files will be used in Makefile and subinclude.h' - echo ' -i - dictionary has init method' - exit 1; -} - -dictname= -stemmode=no -verbose=no -cfile= -hfile= -dir= -hasinit=no -comment= -prefix= - -while getopts n:c:C:h:d:p:vis opt -do - case "$opt" in - v) verbose=yes;; - s) stemmode=yes;; - i) hasinit=yes;; - n) dictname="$OPTARG";; - c) cfile="$OPTARG";; - h) hfile="$OPTARG";; - d) dir="$OPTARG";; - C) comment="$OPTARG";; - p) prefix="$OPTARG";; - \?) usage;; - esac -done - -[ ${#dictname} -eq 0 ] && usage - -dictname=`echo $dictname | tr '[:upper:]' '[:lower:]'` - -if [ $stemmode = "yes" ] ; then - [ ${#prefix} -eq 0 ] && prefix=$dictname - hasinit=yes - cfile="stem.c" - hfile="stem.h" -fi - -[ ${#dir} -eq 0 ] && dir="dict_$dictname" - -if [ ${#comment} -eq 0 ]; then - comment=null -else - comment="'$comment'" -fi - -ofile= -for f in $cfile -do - f=` echo $f | sed 's#c$#o#'` - ofile="$ofile $f" -done - -if [ $stemmode = "yes" ] ; then - ofile="$ofile dict_snowball.o" -else - ofile="$ofile dict_tmpl.o" -fi - -if [ $verbose = "yes" ]; then - echo Dictname: "'"$dictname"'" - echo Snowball stemmer: $stemmode - echo Has init method: $hasinit - [ $stemmode = "yes" ] && echo Function prefix: $prefix - echo Source files: $cfile - echo Header files: $hfile - echo Object files: $ofile - echo Comment: $comment - echo Directory: ../../$dir -fi - - -[ $verbose = "yes" ] && echo -n 'Build directory... ' -if [ ! -d ../../$dir ]; then - if ! mkdir ../../$dir ; then - echo "Can't create directory ../../$dir" - exit 1 - fi -fi -[ $verbose = "yes" ] && echo ok - - -[ $verbose = "yes" ] && echo -n 'Build Makefile... ' -sed s#CFG_DIR#$dir# < Makefile.IN | sed s#CFG_MODNAME#$dictname# | sed "s#CFG_OFILE#$ofile#" > ../../$dir/Makefile.tmp -if [ $stemmode = "yes" ] ; then - sed "s#^PG_CPPFLAGS.*\$#PG_CPPFLAGS = -I../tsearch2/snowball -I../tsearch2#" < ../../$dir/Makefile.tmp > ../../$dir/Makefile -else - sed "s#^PG_CPPFLAGS.*\$#PG_CPPFLAGS = -I../tsearch2#" < ../../$dir/Makefile.tmp > ../../$dir/Makefile -fi -rm ../../$dir/Makefile.tmp -[ $verbose = "yes" ] && echo ok - - -[ $verbose = "yes" ] && echo -n Build dict_$dictname'.sql.in... ' -if [ $hasinit = "yes" ]; then - sed s#CFG_MODNAME#$dictname# < sql.IN | sed "s#CFG_COMMENT#$comment#" | sed s#^HASINIT## | sed 's#^NOINIT.*$##' > ../../$dir/dict_$dictname.sql.in.tmp - if [ $stemmode = "yes" ] ; then - sed s#^ISSNOWBALL## < ../../$dir/dict_$dictname.sql.in.tmp | sed s#^NOSNOWBALL.*\$## > ../../$dir/dict_$dictname.sql.in - else - sed s#^NOSNOWBALL## < ../../$dir/dict_$dictname.sql.in.tmp | sed s#^ISSNOWBALL.*\$## > ../../$dir/dict_$dictname.sql.in - fi - rm ../../$dir/dict_$dictname.sql.in.tmp -else - sed s#CFG_MODNAME#$dictname# < sql.IN | sed "s#CFG_COMMENT#$comment#" | sed s#^NOINIT## | sed 's#^HASINIT.*$##' | sed s#^NOSNOWBALL## | sed s#^ISSNOWBALL.*\$## > ../../$dir/dict_$dictname.sql.in -fi -[ $verbose = "yes" ] && echo ok - - - -if [ ${#cfile} -ne 0 ] || [ ${#hfile} -ne 0 ] ; then - [ $verbose = "yes" ] && echo -n 'Copy source and header files... ' - if [ ${#cfile} -ne 0 ] ; then - if [ $stemmode = "yes" ] ; then - for cfn in $cfile - do - sed s#../runtime/## < $cfn > ../../$dir/$cfn - done - else - if ! cp $cfile ../../$dir ; then - echo "Can't cp all or one of files: $cfile" - exit 1 - fi - fi - fi - if [ ${#hfile} -ne 0 ] ; then - if ! cp $hfile ../../$dir ; then - echo "Cant cp all or one of files: $hfile" - exit 1 - fi - fi - [ $verbose = "yes" ] && echo ok -fi - - -[ $verbose = "yes" ] && echo -n 'Build sub-include header... ' -echo -n > ../../$dir/subinclude.h -for i in $hfile -do - echo "#include \"$i\"" >> ../../$dir/subinclude.h -done -[ $verbose = "yes" ] && echo ok - - -if [ $stemmode = "yes" ] ; then - [ $verbose = "yes" ] && echo -n 'Build Snowball stemmer... ' - sed s#CFG_MODNAME#$dictname#g < dict_snowball.c.IN | sed s#CFG_PREFIX#$prefix#g > ../../$dir/dict_snowball.c -else - [ $verbose = "yes" ] && echo -n 'Build dictinonary... ' - sed s#CFG_MODNAME#$dictname#g < dict_tmpl.c.IN > ../../$dir/dict_tmpl.c.tmp - if [ $hasinit = "yes" ]; then - sed s#^HASINIT## < ../../$dir/dict_tmpl.c.tmp | sed 's#^NOINIT.*$##' > ../../$dir/dict_tmpl.c - else - sed s#^HASINIT.*\$## < ../../$dir/dict_tmpl.c.tmp | sed 's#^NOINIT##' > ../../$dir/dict_tmpl.c - fi - rm ../../$dir/dict_tmpl.c.tmp -fi -[ $verbose = "yes" ] && echo ok - - -[ $verbose = "yes" ] && echo -n "Build README.$dictname... " -if [ $stemmode = "yes" ] ; then - echo "Autogenerated Snowball's wrapper for $prefix" > ../../$dir/README.$dictname -else - echo "Autogenerated template for $dictname" > ../../$dir/README.$dictname -fi -[ $verbose = "yes" ] && echo ok - -echo All is done - diff --git a/contrib/tsearch2/gendict/dict_snowball.c.IN b/contrib/tsearch2/gendict/dict_snowball.c.IN deleted file mode 100755 index 7136ee19ddc7..000000000000 --- a/contrib/tsearch2/gendict/dict_snowball.c.IN +++ /dev/null @@ -1,56 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/gendict/dict_snowball.c.IN,v 1.5 2006/07/14 05:28:27 tgl Exp $ */ - -/* - * example of Snowball dictionary - * http://snowball.tartarus.org/ - * Teodor Sigaev - */ -#include "postgres.h" - -#include "dict.h" -#include "common.h" -#include "snowball/header.h" -#include "subinclude.h" -#include "ts_locale.h" - -typedef struct { - struct SN_env *z; - StopList stoplist; - int (*stem)(struct SN_env * z); -} DictSnowball; - - -PG_FUNCTION_INFO_V1(dinit_CFG_MODNAME); -Datum dinit_CFG_MODNAME(PG_FUNCTION_ARGS); - -Datum -dinit_CFG_MODNAME(PG_FUNCTION_ARGS) { - DictSnowball *d = (DictSnowball*)malloc( sizeof(DictSnowball) ); - - if ( !d ) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - memset(d,0,sizeof(DictSnowball)); - d->stoplist.wordop=lowerstr; - - if ( !PG_ARGISNULL(0) && PG_GETARG_POINTER(0)!=NULL ) { - text *in = PG_GETARG_TEXT_P(0); - readstoplist(in, &(d->stoplist)); - sortstoplist(&(d->stoplist)); - PG_FREE_IF_COPY(in, 0); - } - - d->z = CFG_PREFIX_create_env(); - if (!d->z) { - freestoplist(&(d->stoplist)); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - d->stem=CFG_PREFIX_stem; - - PG_RETURN_POINTER(d); -} - - diff --git a/contrib/tsearch2/gendict/dict_tmpl.c.IN b/contrib/tsearch2/gendict/dict_tmpl.c.IN deleted file mode 100755 index a47720aa1008..000000000000 --- a/contrib/tsearch2/gendict/dict_tmpl.c.IN +++ /dev/null @@ -1,65 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/gendict/dict_tmpl.c.IN,v 1.6 2006/07/14 05:28:27 tgl Exp $ */ - -/* - * example of dictionary - * Teodor Sigaev - */ -#include "postgres.h" - -#include "dict.h" -#include "common.h" - -#include "subinclude.h" -#include "ts_locale.h" - -HASINIT typedef struct { -HASINIT StopList stoplist; -HASINIT } DictExample; - - -HASINIT PG_FUNCTION_INFO_V1(dinit_CFG_MODNAME); -HASINIT Datum dinit_CFG_MODNAME(PG_FUNCTION_ARGS); - -HASINIT Datum -HASINIT dinit_CFG_MODNAME(PG_FUNCTION_ARGS) { -HASINIT DictExample *d = (DictExample*)malloc( sizeof(DictExample) ); -HASINIT -HASINIT if ( !d ) -HASINIT ereport(ERROR, -HASINIT (errcode(ERRCODE_OUT_OF_MEMORY), -HASINIT errmsg("out of memory"))); -HASINIT memset(d,0,sizeof(DictExample)); -HASINIT -HASINIT d->stoplist.wordop=lowerstr; -HASINIT -HASINIT /* Your INIT code */ -HASINIT -HASINIT if ( !PG_ARGISNULL(0) && PG_GETARG_POINTER(0)!=NULL ) { -HASINIT text *in = PG_GETARG_TEXT_P(0); -HASINIT readstoplist(in, &(d->stoplist)); -HASINIT sortstoplist(&(d->stoplist)); -HASINIT PG_FREE_IF_COPY(in, 0); -HASINIT } -HASINIT -HASINIT PG_RETURN_POINTER(d); -HASINIT } - -PG_FUNCTION_INFO_V1(dlexize_CFG_MODNAME); -Datum dlexize_CFG_MODNAME(PG_FUNCTION_ARGS); -Datum -dlexize_CFG_MODNAME(PG_FUNCTION_ARGS) { -HASINIT DictExample *d = (DictExample*)PG_GETARG_POINTER(0); - char *in = (char*)PG_GETARG_POINTER(1); - char *txt = pnstrdup(in, PG_GETARG_INT32(2)); - TSLexeme *res=palloc(sizeof(TSLexeme*)*2); - - /* Your LEXIZE dictionary code */ -HASINIT if ( *txt=='\0' || searchstoplist(&(d->stoplist),txt) ) { -HASINIT pfree(txt); -HASINIT res[0].lexeme=NULL; -HASINIT } else - res[0].lexeme=txt; - res[1].lexeme=NULL; - - PG_RETURN_POINTER(res); -} diff --git a/contrib/tsearch2/gendict/sql.IN b/contrib/tsearch2/gendict/sql.IN deleted file mode 100755 index 399b26b0d9ec..000000000000 --- a/contrib/tsearch2/gendict/sql.IN +++ /dev/null @@ -1,26 +0,0 @@ -SET search_path = public; -BEGIN; - -HASINIT create function dinit_CFG_MODNAME(internal) -HASINIT returns internal -HASINIT as 'MODULE_PATHNAME' -HASINIT language C; - -NOSNOWBALL create function dlexize_CFG_MODNAME(internal,internal,int4) -NOSNOWBALL returns internal -NOSNOWBALL as 'MODULE_PATHNAME' -NOSNOWBALL language C -NOSNOWBALL returns null on null input; - -insert into pg_ts_dict select - 'CFG_MODNAME', -HASINIT (select oid from pg_proc where proname='dinit_CFG_MODNAME'), -NOINIT null, - null, -ISSNOWBALL (select oid from pg_proc where proname='snb_lexize'), -NOSNOWBALL (select oid from pg_proc where proname='dlexize_CFG_MODNAME'), - CFG_COMMENT -; - - -END; diff --git a/contrib/tsearch2/ginidx.c b/contrib/tsearch2/ginidx.c deleted file mode 100644 index d77ee9bb9f73..000000000000 --- a/contrib/tsearch2/ginidx.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "postgres.h" - -#include - -#include "access/gist.h" -#include "access/itup.h" -#include "access/skey.h" -#include "access/tuptoaster.h" -#include "storage/bufpage.h" -#include "utils/array.h" -#include "utils/builtins.h" - -#include "tsvector.h" -#include "query.h" -#include "query_cleanup.h" - -PG_FUNCTION_INFO_V1(gin_extract_tsvector); -Datum gin_extract_tsvector(PG_FUNCTION_ARGS); - -Datum -gin_extract_tsvector(PG_FUNCTION_ARGS) -{ - tsvector *vector = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - int32 *nentries = (int32 *) PG_GETARG_POINTER(1); - Datum *entries = NULL; - - *nentries = 0; - if (vector->size > 0) - { - int i; - WordEntry *we = ARRPTR(vector); - - *nentries = (int32) vector->size; - entries = (Datum *) palloc(sizeof(Datum) * vector->size); - - for (i = 0; i < vector->size; i++) - { - text *txt = (text *) palloc(VARHDRSZ + we->len); - - SET_VARSIZE(txt, VARHDRSZ + we->len); - memcpy(VARDATA(txt), STRPTR(vector) + we->pos, we->len); - - entries[i] = PointerGetDatum(txt); - - we++; - } - } - - PG_FREE_IF_COPY(vector, 0); - PG_RETURN_POINTER(entries); -} - - -PG_FUNCTION_INFO_V1(gin_extract_tsquery); -Datum gin_extract_tsquery(PG_FUNCTION_ARGS); - -Datum -gin_extract_tsquery(PG_FUNCTION_ARGS) -{ - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - int32 *nentries = (int32 *) PG_GETARG_POINTER(1); - StrategyNumber strategy = DatumGetUInt16(PG_GETARG_DATUM(2)); - Datum *entries = NULL; - - *nentries = 0; - if (query->size > 0) - { - int4 i, - j = 0, - len; - ITEM *item; - - item = clean_NOT_v2(GETQUERY(query), &len); - if (!item) - elog(ERROR, "Query requires full scan, GIN doesn't support it"); - - item = GETQUERY(query); - - for (i = 0; i < query->size; i++) - if (item[i].type == VAL) - (*nentries)++; - - entries = (Datum *) palloc(sizeof(Datum) * (*nentries)); - - for (i = 0; i < query->size; i++) - if (item[i].type == VAL) - { - text *txt; - - txt = (text *) palloc(VARHDRSZ + item[i].length); - - SET_VARSIZE(txt, VARHDRSZ + item[i].length); - memcpy(VARDATA(txt), GETOPERAND(query) + item[i].distance, item[i].length); - - entries[j++] = PointerGetDatum(txt); - - if (strategy == 1 && item[i].weight != 0) - elog(ERROR, "With class of lexeme restrictions use @@@ operation"); - } - - } - else - *nentries = -1; /* nothing can be found */ - - PG_FREE_IF_COPY(query, 0); - PG_RETURN_POINTER(entries); -} - -typedef struct -{ - ITEM *frst; - bool *mapped_check; -} GinChkVal; - -static bool -checkcondition_gin(void *checkval, ITEM * val) -{ - GinChkVal *gcv = (GinChkVal *) checkval; - - return gcv->mapped_check[val - gcv->frst]; -} - -PG_FUNCTION_INFO_V1(gin_ts_consistent); -Datum gin_ts_consistent(PG_FUNCTION_ARGS); - -Datum -gin_ts_consistent(PG_FUNCTION_ARGS) -{ - bool *check = (bool *) PG_GETARG_POINTER(0); - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(2)); - bool res = FALSE; - - if (query->size > 0) - { - int4 i, - j = 0; - ITEM *item; - GinChkVal gcv; - - gcv.frst = item = GETQUERY(query); - gcv.mapped_check = (bool *) palloc(sizeof(bool) * query->size); - - for (i = 0; i < query->size; i++) - if (item[i].type == VAL) - gcv.mapped_check[i] = check[j++]; - - - res = TS_execute( - GETQUERY(query), - &gcv, - true, - checkcondition_gin - ); - - } - - PG_FREE_IF_COPY(query, 2); - PG_RETURN_BOOL(res); -} diff --git a/contrib/tsearch2/gistidx.h b/contrib/tsearch2/gistidx.h deleted file mode 100644 index 5362584b8078..000000000000 --- a/contrib/tsearch2/gistidx.h +++ /dev/null @@ -1,62 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/gistidx.h,v 1.8 2007/02/28 22:44:38 tgl Exp $ */ - -#ifndef __GISTIDX_H__ -#define __GISTIDX_H__ - -/* -#define GISTIDX_DEBUG -*/ - -/* - * signature defines - */ - -#define SIGLENINT 63 /* >121 => key will toast, so it will not work - * !!! */ -#define SIGLEN ( sizeof(int4) * SIGLENINT ) -#define SIGLENBIT (SIGLEN * BITS_PER_BYTE) - -typedef char BITVEC[SIGLEN]; -typedef char *BITVECP; - -#define LOOPBYTE(a) \ - for(i=0;i> (i) & 0x01 ) -#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITS_PER_BYTE ) ) -#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) ) -#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 ) - -#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT) -#define HASH(sign, val) SETBIT((sign), HASHVAL(val)) - - -/* - * type of index key - */ -typedef struct -{ - int32 vl_len_; /* varlena header (do not touch directly!) */ - int4 flag; - char data[1]; -} GISTTYPE; - -#define ARRKEY 0x01 -#define SIGNKEY 0x02 -#define ALLISTRUE 0x04 - -#define ISARRKEY(x) ( ((GISTTYPE*)(x))->flag & ARRKEY ) -#define ISSIGNKEY(x) ( ((GISTTYPE*)(x))->flag & SIGNKEY ) -#define ISALLTRUE(x) ( ((GISTTYPE*)(x))->flag & ALLISTRUE ) - -#define GTHDRSIZE ( VARHDRSZ + sizeof(int4) ) -#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int4)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) ) - -#define GETSIGN(x) ( (BITVECP)( (char*)(x)+GTHDRSIZE ) ) -#define GETARR(x) ( (int4*)( (char*)(x)+GTHDRSIZE ) ) -#define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int4) ) - -#endif diff --git a/contrib/tsearch2/ispell/Makefile b/contrib/tsearch2/ispell/Makefile deleted file mode 100644 index 341f7f41e5d4..000000000000 --- a/contrib/tsearch2/ispell/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# $PostgreSQL: pgsql/contrib/tsearch2/ispell/Makefile,v 1.9 2005/10/18 01:30:48 tgl Exp $ - -SUBOBJS = spell.o regis.o - -EXTRA_CLEAN = SUBSYS.o $(SUBOBJS) - -PG_CPPFLAGS = -I$(srcdir)/.. - -ifdef USE_PGXS -PGXS := $(shell pg_config --pgxs) -include $(PGXS) -else -subdir = contrib/tsearch2/ispell -top_builddir = ../../.. -include $(top_builddir)/src/Makefile.global -include $(top_srcdir)/contrib/contrib-global.mk -endif - -override CFLAGS += $(CFLAGS_SL) - -all: SUBSYS.o - -SUBSYS.o: $(SUBOBJS) - $(LD) $(LDREL) $(LDOUT) $@ $^ - - diff --git a/contrib/tsearch2/ispell/regis.h b/contrib/tsearch2/ispell/regis.h deleted file mode 100644 index a0b840b0a0d6..000000000000 --- a/contrib/tsearch2/ispell/regis.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef __REGIS_H__ -#define __REGIS_H__ - -#include "postgres.h" - -typedef struct RegisNode -{ - uint32 - type:2, - len:16, - unused:14; - struct RegisNode *next; - unsigned char data[1]; -} RegisNode; - -#define RNHDRSZ (offsetof(RegisNode,data)) - -#define RSF_ONEOF 1 -#define RSF_NONEOF 2 - -typedef struct Regis -{ - RegisNode *node; - uint32 - issuffix:1, - nchar:16, - unused:15; -} Regis; - -bool RS_isRegis(const char *str); - -void RS_compile(Regis * r, bool issuffix, char *str); -void RS_free(Regis * r); - -/*returns true if matches */ -bool RS_execute(Regis * r, char *str); - -#endif diff --git a/contrib/tsearch2/ispell/spell.c b/contrib/tsearch2/ispell/spell.c deleted file mode 100644 index 4d83532f5029..000000000000 --- a/contrib/tsearch2/ispell/spell.c +++ /dev/null @@ -1,1544 +0,0 @@ -#include "postgres.h" - -#include - -#include "spell.h" -#include "common.h" -#include "ts_locale.h" - -#define MAX_NORM 1024 -#define MAXNORMLEN 256 - -#define ERRSTRSIZE 1024 - -#define STRNCMP(s,p) strncmp( (s), (p), strlen(p) ) -#define GETWCHAR(W,L,N,T) ( ((uint8*)(W))[ ((T)==FF_PREFIX) ? (N) : ( (L) - 1 - (N) ) ] ) -#define GETCHAR(A,N,T) GETWCHAR( (A)->repl, (A)->replen, N, T ) - -static char *VoidString = ""; - -#define MEMOUT(X) if ( !(X) ) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))) - -static int -cmpspell(const void *s1, const void *s2) -{ - return (strcmp((*(const SPELL **) s1)->word, (*(const SPELL **) s2)->word)); -} -static int -cmpspellaffix(const void *s1, const void *s2) -{ - return (strcmp((*(const SPELL **) s1)->p.flag, (*(const SPELL **) s2)->p.flag)); -} - -static char * -strnduplicate(char *s, int len) -{ - char *d = (char *) palloc(len + 1); - - memcpy(d, s, len); - d[len] = '\0'; - return d; -} - -static char * -findchar(char *str, int c) -{ - while (*str) - { - if (t_iseq(str, c)) - return str; - str += pg_mblen(str); - } - - return NULL; -} - - -/* backward string compare for suffix tree operations */ -static int -strbcmp(const unsigned char *s1, const unsigned char *s2) -{ - int l1 = strlen((const char *) s1) - 1, - l2 = strlen((const char *) s2) - 1; - - while (l1 >= 0 && l2 >= 0) - { - if (s1[l1] < s2[l2]) - return -1; - if (s1[l1] > s2[l2]) - return 1; - l1--; - l2--; - } - if (l1 < l2) - return -1; - if (l1 > l2) - return 1; - - return 0; -} -static int -strbncmp(const unsigned char *s1, const unsigned char *s2, size_t count) -{ - int l1 = strlen((const char *) s1) - 1, - l2 = strlen((const char *) s2) - 1, - l = count; - - while (l1 >= 0 && l2 >= 0 && l > 0) - { - if (s1[l1] < s2[l2]) - return -1; - if (s1[l1] > s2[l2]) - return 1; - l1--; - l2--; - l--; - } - if (l == 0) - return 0; - if (l1 < l2) - return -1; - if (l1 > l2) - return 1; - return 0; -} - -static int -cmpaffix(const void *s1, const void *s2) -{ - const AFFIX *a1 = (const AFFIX *) s1; - const AFFIX *a2 = (const AFFIX *) s2; - - if (a1->type < a2->type) - return -1; - if (a1->type > a2->type) - return 1; - if (a1->type == FF_PREFIX) - return strcmp(a1->repl, a2->repl); - else - return strbcmp((const unsigned char *) a1->repl, - (const unsigned char *) a2->repl); -} - -int -NIAddSpell(IspellDict * Conf, const char *word, const char *flag) -{ - if (Conf->nspell >= Conf->mspell) - { - if (Conf->mspell) - { - Conf->mspell += 1024 * 20; - Conf->Spell = (SPELL **) repalloc(Conf->Spell, Conf->mspell * sizeof(SPELL *)); - } - else - { - Conf->mspell = 1024 * 20; - Conf->Spell = (SPELL **) palloc(Conf->mspell * sizeof(SPELL *)); - } - } - Conf->Spell[Conf->nspell] = (SPELL *) palloc(SPELLHDRSZ + strlen(word) + 1); - strcpy(Conf->Spell[Conf->nspell]->word, word); - strncpy(Conf->Spell[Conf->nspell]->p.flag, flag, 16); - Conf->nspell++; - return (0); -} - - -int -NIImportDictionary(IspellDict * Conf, const char *filename) -{ - char str[BUFSIZ], *pstr; - FILE *dict; - - if (!(dict = fopen(filename, "r"))) - return (1); - while (fgets(str, sizeof(str), dict)) - { - char *s; - const char *flag; - - pg_verifymbstr(str, strlen(str), false); - - flag = NULL; - if ((s = findchar(str, '/'))) - { - *s++ = '\0'; - flag = s; - while (*s) - { - /* we allow only single encoded flags for faster works */ - if (pg_mblen(s) == 1 && t_isprint(s) && !t_isspace(s)) - s++; - else - { - *s = '\0'; - break; - } - } - } - else - flag = ""; - - - s = str; - while (*s) - { - if (t_isspace(s)) - { - *s = '\0'; - break; - } - s += pg_mblen(s); - } - pstr = lowerstr(str); - - NIAddSpell(Conf, pstr, flag); - pfree(pstr); - } - fclose(dict); - return (0); -} - - -static int -FindWord(IspellDict * Conf, const char *word, int affixflag, char compoundonly) -{ - SPNode *node = Conf->Dictionary; - SPNodeData *StopLow, - *StopHigh, - *StopMiddle; - uint8 *ptr = (uint8 *) word; - - while (node && *ptr) - { - StopLow = node->data; - StopHigh = node->data + node->length; - while (StopLow < StopHigh) - { - StopMiddle = StopLow + ((StopHigh - StopLow) >> 1); - if (StopMiddle->val == *ptr) - { - if (*(ptr + 1) == '\0' && StopMiddle->isword) - { - if (compoundonly && !StopMiddle->compoundallow) - return 0; - if ((affixflag == 0) || (strchr(Conf->AffixData[StopMiddle->affix], affixflag) != NULL)) - return 1; - } - node = StopMiddle->node; - ptr++; - break; - } - else if (StopMiddle->val < *ptr) - StopLow = StopMiddle + 1; - else - StopHigh = StopMiddle; - } - if (StopLow >= StopHigh) - break; - } - return 0; -} - -int -NIAddAffix(IspellDict * Conf, int flag, char flagflags, const char *mask, const char *find, const char *repl, int type) -{ - if (Conf->naffixes >= Conf->maffixes) - { - if (Conf->maffixes) - { - Conf->maffixes += 16; - Conf->Affix = (AFFIX *) realloc((void *) Conf->Affix, Conf->maffixes * sizeof(AFFIX)); - } - else - { - Conf->maffixes = 16; - Conf->Affix = (AFFIX *) malloc(Conf->maffixes * sizeof(AFFIX)); - } - MEMOUT(Conf->Affix); - } - - if (strcmp(mask, ".") == 0) - { - Conf->Affix[Conf->naffixes].issimple = 1; - Conf->Affix[Conf->naffixes].isregis = 0; - Conf->Affix[Conf->naffixes].mask = VoidString; - } - else if (RS_isRegis(mask)) - { - Conf->Affix[Conf->naffixes].issimple = 0; - Conf->Affix[Conf->naffixes].isregis = 1; - Conf->Affix[Conf->naffixes].mask = (mask && *mask) ? strdup(mask) : VoidString; - } - else - { - int masklen = strlen(mask); - - Conf->Affix[Conf->naffixes].issimple = 0; - Conf->Affix[Conf->naffixes].isregis = 0; - Conf->Affix[Conf->naffixes].mask = (char *) malloc(masklen + 2); - if (type == FF_SUFFIX) - sprintf(Conf->Affix[Conf->naffixes].mask, "%s$", mask); - else - sprintf(Conf->Affix[Conf->naffixes].mask, "^%s", mask); - } - MEMOUT(Conf->Affix[Conf->naffixes].mask); - - Conf->Affix[Conf->naffixes].compile = 1; - Conf->Affix[Conf->naffixes].flagflags = flagflags; - Conf->Affix[Conf->naffixes].flag = flag; - Conf->Affix[Conf->naffixes].type = type; - - Conf->Affix[Conf->naffixes].find = (find && *find) ? strdup(find) : VoidString; - MEMOUT(Conf->Affix[Conf->naffixes].find); - if ((Conf->Affix[Conf->naffixes].replen = strlen(repl)) > 0) - { - Conf->Affix[Conf->naffixes].repl = strdup(repl); - MEMOUT(Conf->Affix[Conf->naffixes].repl); - } - else - Conf->Affix[Conf->naffixes].repl = VoidString; - Conf->naffixes++; - return (0); -} - -#define PAE_WAIT_MASK 0 -#define PAE_INMASK 1 -#define PAE_WAIT_FIND 2 -#define PAE_INFIND 3 -#define PAE_WAIT_REPL 4 -#define PAE_INREPL 5 - -static bool -parse_affentry(char *str, char *mask, char *find, char *repl, int line) -{ - int state = PAE_WAIT_MASK; - char *pmask = mask, - *pfind = find, - *prepl = repl; - - *mask = *find = *repl = '\0'; - - while (*str) - { - if (state == PAE_WAIT_MASK) - { - if (t_iseq(str, '#')) - return false; - else if (!t_isspace(str)) - { - COPYCHAR(pmask, str); - pmask += pg_mblen(str); - state = PAE_INMASK; - } - } - else if (state == PAE_INMASK) - { - if (t_iseq(str, '>')) - { - *pmask = '\0'; - state = PAE_WAIT_FIND; - } - else if (!t_isspace(str)) - { - COPYCHAR(pmask, str); - pmask += pg_mblen(str); - } - } - else if (state == PAE_WAIT_FIND) - { - if (t_iseq(str, '-')) - { - state = PAE_INFIND; - } - else if (t_isalpha(str) || t_iseq(str, '\'') /* english 's */ ) - { - COPYCHAR(prepl, str); - prepl += pg_mblen(str); - state = PAE_INREPL; - } - else if (!t_isspace(str)) - ts_error(ERROR, "Affix parse error at %d line", line); - } - else if (state == PAE_INFIND) - { - if (t_iseq(str, ',')) - { - *pfind = '\0'; - state = PAE_WAIT_REPL; - } - else if (t_isalpha(str)) - { - COPYCHAR(pfind, str); - pfind += pg_mblen(str); - } - else if (!t_isspace(str)) - ts_error(ERROR, "Affix parse error at %d line", line); - } - else if (state == PAE_WAIT_REPL) - { - if (t_iseq(str, '-')) - { - break; /* void repl */ - } - else if (t_isalpha(str)) - { - COPYCHAR(prepl, str); - prepl += pg_mblen(str); - state = PAE_INREPL; - } - else if (!t_isspace(str)) - ts_error(ERROR, "Affix parse error at %d line", line); - } - else if (state == PAE_INREPL) - { - if (t_iseq(str, '#')) - { - *prepl = '\0'; - break; - } - else if (t_isalpha(str)) - { - COPYCHAR(prepl, str); - prepl += pg_mblen(str); - } - else if (!t_isspace(str)) - ts_error(ERROR, "Affix parse error at %d line", line); - } - else - ts_error(ERROR, "Unknown state in parse_affentry: %d", state); - - str += pg_mblen(str); - } - - *pmask = *pfind = *prepl = '\0'; - - return (*mask && (*find || *repl)) ? true : false; -} - -int -NIImportAffixes(IspellDict * Conf, const char *filename) -{ - char str[BUFSIZ], *pstr = NULL; - char mask[BUFSIZ]; - char find[BUFSIZ]; - char repl[BUFSIZ]; - char *s; - int suffixes = 0; - int prefixes = 0; - int flag = 0; - char flagflags = 0; - FILE *affix; - int line = 0; - int oldformat = 0; - - if (!(affix = fopen(filename, "r"))) - return (1); - Conf->compoundcontrol = '\t'; - - while (fgets(str, sizeof(str), affix)) - { - line++; - if ( *str == '#' || *str == '\n' ) - continue; - - pg_verifymbstr(str, strlen(str), false); - if ( pstr ) - pfree( pstr ); - pstr = lowerstr(str); - if (STRNCMP(pstr, "compoundwords") == 0) - { - s = findchar(str, 'l'); - if (s) - { - while (*s && !t_isspace(s)) - s++; - while (*s && t_isspace(s)) - s++; - if (*s && pg_mblen(s) == 1) - Conf->compoundcontrol = *s; - oldformat++; - continue; - } - } - if (STRNCMP(pstr, "suffixes") == 0) - { - suffixes = 1; - prefixes = 0; - oldformat++; - continue; - } - if (STRNCMP(pstr, "prefixes") == 0) - { - suffixes = 0; - prefixes = 1; - oldformat++; - continue; - } - if (STRNCMP(pstr, "flag") == 0) - { - s = str + 4; - flagflags = 0; - - while (*s && t_isspace(s)) - s++; - oldformat++; - - /* allow only single-encoded flags */ - if (pg_mblen(s) != 1) - elog(ERROR, "Multiencoded flag at line %d: %s", line, s); - - if (*s == '*') - { - flagflags |= FF_CROSSPRODUCT; - s++; - } - else if (*s == '~') - { - flagflags |= FF_COMPOUNDONLYAFX; - s++; - } - - if (*s == '\\') - s++; - - /* allow only single-encoded flags */ - if (pg_mblen(s) != 1) - { - flagflags = 0; - elog(ERROR, "Multiencoded flag at line %d: %s", line, s); - } - - flag = (unsigned char) *s; - continue; - } - if (STRNCMP(str, "COMPOUNDFLAG") == 0 || STRNCMP(str, "COMPOUNDMIN") == 0 || - STRNCMP(str, "PFX") == 0 || STRNCMP(str, "SFX") == 0) - { - - if (oldformat) - elog(ERROR, "Wrong affix file format"); - - fclose(affix); - return NIImportOOAffixes(Conf, filename); - - } - if ((!suffixes) && (!prefixes)) - continue; - - if (!parse_affentry(pstr, mask, find, repl, line)) - continue; - - NIAddAffix(Conf, flag, flagflags, mask, find, repl, suffixes ? FF_SUFFIX : FF_PREFIX); - } - fclose(affix); - - if ( pstr ) - pfree( pstr ); - - return (0); -} - -int -NIImportOOAffixes(IspellDict * Conf, const char *filename) -{ - char str[BUFSIZ]; - char type[BUFSIZ], *ptype = NULL; - char sflag[BUFSIZ]; - char mask[BUFSIZ], *pmask; - char find[BUFSIZ], *pfind; - char repl[BUFSIZ], *prepl; - bool isSuffix = false; - int flag = 0; - char flagflags = 0; - FILE *affix; - int line = 0; - int scanread = 0; - char scanbuf[BUFSIZ]; - - sprintf(scanbuf, "%%6s %%%ds %%%ds %%%ds %%%ds", BUFSIZ / 5, BUFSIZ / 5, BUFSIZ / 5, BUFSIZ / 5); - - if (!(affix = fopen(filename, "r"))) - return (1); - Conf->compoundcontrol = '\t'; - - while (fgets(str, sizeof(str), affix)) - { - line++; - if (*str == '\0' || t_isspace(str) || t_iseq(str, '#')) - continue; - pg_verifymbstr(str, strlen(str), false); - - if (STRNCMP(str, "COMPOUNDFLAG") == 0) - { - char *s = str + strlen("COMPOUNDFLAG"); - - while (*s && t_isspace(s)) - s++; - if (*s && pg_mblen(s) == 1) - Conf->compoundcontrol = *s; - continue; - } - - scanread = sscanf(str, scanbuf, type, sflag, find, repl, mask); - - if (ptype) - pfree(ptype); - ptype = lowerstr(type); - if (scanread < 4 || (STRNCMP(ptype, "sfx") && STRNCMP(ptype, "pfx"))) - continue; - - if (scanread == 4) - { - if (strlen(sflag) != 1) - continue; - flag = *sflag; - isSuffix = (STRNCMP(ptype, "sfx") == 0) ? true : false; - pfind = lowerstr(find); - if (t_iseq(find, 'y')) - flagflags |= FF_CROSSPRODUCT; - else - flagflags = 0; - pfree(pfind); - } - else - { - if (strlen(sflag) != 1 || flag != *sflag || flag == 0) - continue; - prepl = lowerstr(repl); - pfind = lowerstr(find); - pmask = lowerstr(mask); - if (t_iseq(find, '0')) - *pfind = '\0'; - if (t_iseq(repl, '0')) - *prepl = '\0'; - - NIAddAffix(Conf, flag, flagflags, pmask, pfind, prepl, isSuffix ? FF_SUFFIX : FF_PREFIX); - pfree(prepl); - pfree(pfind); - pfree(pmask); - } - } - - if (ptype) - pfree(ptype); - fclose(affix); - - return 0; -} - -static int -MergeAffix(IspellDict * Conf, int a1, int a2) -{ - int naffix = 0; - char **ptr = Conf->AffixData; - - while (*ptr) - { - naffix++; - ptr++; - } - - Conf->AffixData = (char **) realloc(Conf->AffixData, (naffix + 2) * sizeof(char *)); - MEMOUT(Conf->AffixData); - ptr = Conf->AffixData + naffix; - *ptr = malloc(strlen(Conf->AffixData[a1]) + strlen(Conf->AffixData[a2]) + 1 /* space */ + 1 /* \0 */ ); - MEMOUT(ptr); - sprintf(*ptr, "%s %s", Conf->AffixData[a1], Conf->AffixData[a2]); - ptr++; - *ptr = '\0'; - return naffix; -} - - -static SPNode * -mkSPNode(IspellDict * Conf, int low, int high, int level) -{ - int i; - int nchar = 0; - char lastchar = '\0'; - SPNode *rs; - SPNodeData *data; - int lownew = low; - - for (i = low; i < high; i++) - if (Conf->Spell[i]->p.d.len > level && lastchar != Conf->Spell[i]->word[level]) - { - nchar++; - lastchar = Conf->Spell[i]->word[level]; - } - - if (!nchar) - return NULL; - - rs = (SPNode *) malloc(SPNHDRSZ + nchar * sizeof(SPNodeData)); - MEMOUT(rs); - memset(rs, 0, SPNHDRSZ + nchar * sizeof(SPNodeData)); - rs->length = nchar; - data = rs->data; - - lastchar = '\0'; - for (i = low; i < high; i++) - if (Conf->Spell[i]->p.d.len > level) - { - if (lastchar != Conf->Spell[i]->word[level]) - { - if (lastchar) - { - data->node = mkSPNode(Conf, lownew, i, level + 1); - lownew = i; - data++; - } - lastchar = Conf->Spell[i]->word[level]; - } - data->val = ((uint8 *) (Conf->Spell[i]->word))[level]; - if (Conf->Spell[i]->p.d.len == level + 1) - { - if (data->isword && data->affix != Conf->Spell[i]->p.d.affix) - { - /* - * fprintf(stderr,"Word already exists: %s (affixes: '%s' - * and '%s')\n", Conf->Spell[i]->word, - * Conf->AffixData[data->affix], - * Conf->AffixData[Conf->Spell[i]->p.d.affix] ); - */ - /* MergeAffix called a few times */ - data->affix = MergeAffix(Conf, data->affix, Conf->Spell[i]->p.d.affix); - } - else - data->affix = Conf->Spell[i]->p.d.affix; - data->isword = 1; - if (strchr(Conf->AffixData[data->affix], Conf->compoundcontrol)) - data->compoundallow = 1; - } - } - - data->node = mkSPNode(Conf, lownew, high, level + 1); - - return rs; -} - -void -NISortDictionary(IspellDict * Conf) -{ - size_t i; - int naffix = 3; - - /* compress affixes */ - qsort((void *) Conf->Spell, Conf->nspell, sizeof(SPELL *), cmpspellaffix); - for (i = 1; i < Conf->nspell; i++) - if (strcmp(Conf->Spell[i]->p.flag, Conf->Spell[i - 1]->p.flag)) - naffix++; - - Conf->AffixData = (char **) malloc(naffix * sizeof(char *)); - MEMOUT(Conf->AffixData); - memset(Conf->AffixData, 0, naffix * sizeof(char *)); - naffix = 1; - Conf->AffixData[0] = strdup(""); - MEMOUT(Conf->AffixData[0]); - Conf->AffixData[1] = strdup(Conf->Spell[0]->p.flag); - MEMOUT(Conf->AffixData[1]); - Conf->Spell[0]->p.d.affix = 1; - Conf->Spell[0]->p.d.len = strlen(Conf->Spell[0]->word); - for (i = 1; i < Conf->nspell; i++) - { - if (strcmp(Conf->Spell[i]->p.flag, Conf->AffixData[naffix])) - { - naffix++; - Conf->AffixData[naffix] = strdup(Conf->Spell[i]->p.flag); - MEMOUT(Conf->AffixData[naffix]); - } - Conf->Spell[i]->p.d.affix = naffix; - Conf->Spell[i]->p.d.len = strlen(Conf->Spell[i]->word); - } - - qsort((void *) Conf->Spell, Conf->nspell, sizeof(SPELL *), cmpspell); - Conf->Dictionary = mkSPNode(Conf, 0, Conf->nspell, 0); - - for (i = 0; i < Conf->nspell; i++) - pfree(Conf->Spell[i]); - pfree(Conf->Spell); - Conf->Spell = NULL; -} - -static AffixNode * -mkANode(IspellDict * Conf, int low, int high, int level, int type) -{ - int i; - int nchar = 0; - uint8 lastchar = '\0'; - AffixNode *rs; - AffixNodeData *data; - int lownew = low; - - for (i = low; i < high; i++) - if (Conf->Affix[i].replen > level && lastchar != GETCHAR(Conf->Affix + i, level, type)) - { - nchar++; - lastchar = GETCHAR(Conf->Affix + i, level, type); - } - - if (!nchar) - return NULL; - - rs = (AffixNode *) malloc(ANHRDSZ + nchar * sizeof(AffixNodeData)); - MEMOUT(rs); - memset(rs, 0, ANHRDSZ + nchar * sizeof(AffixNodeData)); - rs->length = nchar; - data = rs->data; - - lastchar = '\0'; - for (i = low; i < high; i++) - if (Conf->Affix[i].replen > level) - { - if (lastchar != GETCHAR(Conf->Affix + i, level, type)) - { - if (lastchar) - { - data->node = mkANode(Conf, lownew, i, level + 1, type); - lownew = i; - data++; - } - lastchar = GETCHAR(Conf->Affix + i, level, type); - } - data->val = GETCHAR(Conf->Affix + i, level, type); - if (Conf->Affix[i].replen == level + 1) - { /* affix stopped */ - if (!data->naff) - { - data->aff = (AFFIX **) malloc(sizeof(AFFIX *) * (high - i + 1)); - MEMOUT(data->aff); - } - data->aff[data->naff] = Conf->Affix + i; - data->naff++; - } - } - - data->node = mkANode(Conf, lownew, high, level + 1, type); - - return rs; -} - -static void -mkVoidAffix(IspellDict * Conf, int issuffix, int startsuffix) -{ - int i, - cnt = 0; - int start = (issuffix) ? startsuffix : 0; - int end = (issuffix) ? Conf->naffixes : startsuffix; - AffixNode *Affix = (AffixNode *) malloc(ANHRDSZ + sizeof(AffixNodeData)); - - MEMOUT(Affix); - memset(Affix, 0, ANHRDSZ + sizeof(AffixNodeData)); - Affix->length = 1; - Affix->isvoid = 1; - - if (issuffix) - { - Affix->data->node = Conf->Suffix; - Conf->Suffix = Affix; - } - else - { - Affix->data->node = Conf->Prefix; - Conf->Prefix = Affix; - } - - - for (i = start; i < end; i++) - if (Conf->Affix[i].replen == 0) - cnt++; - - if (cnt == 0) - return; - - Affix->data->aff = (AFFIX **) malloc(sizeof(AFFIX *) * cnt); - MEMOUT(Affix->data->aff); - Affix->data->naff = (uint32) cnt; - - cnt = 0; - for (i = start; i < end; i++) - if (Conf->Affix[i].replen == 0) - { - Affix->data->aff[cnt] = Conf->Affix + i; - cnt++; - } -} - -void -NISortAffixes(IspellDict * Conf) -{ - AFFIX *Affix; - size_t i; - CMPDAffix *ptr; - int firstsuffix = -1; - - if (Conf->naffixes == 0) - return; - - if (Conf->naffixes > 1) - qsort((void *) Conf->Affix, Conf->naffixes, sizeof(AFFIX), cmpaffix); - Conf->CompoundAffix = ptr = (CMPDAffix *) malloc(sizeof(CMPDAffix) * Conf->naffixes); - MEMOUT(Conf->CompoundAffix); - ptr->affix = NULL; - - for (i = 0; i < Conf->naffixes; i++) - { - Affix = &(((AFFIX *) Conf->Affix)[i]); - if (Affix->type == FF_SUFFIX) - { - if (firstsuffix < 0) - firstsuffix = i; - if ((Affix->flagflags & FF_COMPOUNDONLYAFX) && Affix->replen > 0) - { - if (ptr == Conf->CompoundAffix || - strbncmp((const unsigned char *) (ptr - 1)->affix, - (const unsigned char *) Affix->repl, - (ptr - 1)->len)) - { - /* leave only unique and minimals suffixes */ - ptr->affix = Affix->repl; - ptr->len = Affix->replen; - ptr++; - } - } - } - } - ptr->affix = NULL; - Conf->CompoundAffix = (CMPDAffix *) realloc(Conf->CompoundAffix, sizeof(CMPDAffix) * (ptr - Conf->CompoundAffix + 1)); - - Conf->Prefix = mkANode(Conf, 0, firstsuffix, 0, FF_PREFIX); - Conf->Suffix = mkANode(Conf, firstsuffix, Conf->naffixes, 0, FF_SUFFIX); - mkVoidAffix(Conf, 1, firstsuffix); - mkVoidAffix(Conf, 0, firstsuffix); -} - -static AffixNodeData * -FinfAffixes(AffixNode * node, const char *word, int wrdlen, int *level, int type) -{ - AffixNodeData *StopLow, - *StopHigh, - *StopMiddle; - uint8 symbol; - - if (node->isvoid) - { /* search void affixes */ - if (node->data->naff) - return node->data; - node = node->data->node; - } - - while (node && *level < wrdlen) - { - StopLow = node->data; - StopHigh = node->data + node->length; - while (StopLow < StopHigh) - { - StopMiddle = StopLow + ((StopHigh - StopLow) >> 1); - symbol = GETWCHAR(word, wrdlen, *level, type); - if (StopMiddle->val == symbol) - { - (*level)++; - if (StopMiddle->naff) - return StopMiddle; - node = StopMiddle->node; - break; - } - else if (StopMiddle->val < symbol) - StopLow = StopMiddle + 1; - else - StopHigh = StopMiddle; - } - if (StopLow >= StopHigh) - break; - } - return NULL; -} - -static char * -CheckAffix(const char *word, size_t len, AFFIX * Affix, char flagflags, char *newword, int *baselen) -{ - - if (flagflags & FF_COMPOUNDONLYAFX) - { - if ((Affix->flagflags & FF_COMPOUNDONLYAFX) == 0) - return NULL; - } - else - { - if (Affix->flagflags & FF_COMPOUNDONLYAFX) - return NULL; - } - - if (Affix->type == FF_SUFFIX) - { - strcpy(newword, word); - strcpy(newword + len - Affix->replen, Affix->find); - if (baselen) /* store length of non-changed part of word */ - *baselen = len - Affix->replen; - } - else - { - /* - * if prefix is a all non-chaged part's length then all word contains - * only prefix and suffix, so out - */ - if (baselen && *baselen + strlen(Affix->find) <= Affix->replen) - return NULL; - strcpy(newword, Affix->find); - strcat(newword, word + Affix->replen); - } - - if (Affix->issimple) - return newword; - else if (Affix->isregis) - { - if (Affix->compile) - { - RS_compile(&(Affix->reg.regis), (Affix->type == FF_SUFFIX) ? true : false, Affix->mask); - Affix->compile = 0; - } - if (RS_execute(&(Affix->reg.regis), newword)) - return newword; - } - else - { - int err; - pg_wchar *data; - size_t data_len; - int newword_len; - - if (Affix->compile) - { - int wmasklen, - masklen = strlen(Affix->mask); - pg_wchar *mask; - - mask = (pg_wchar *) palloc((masklen + 1) * sizeof(pg_wchar)); - wmasklen = pg_mb2wchar_with_len(Affix->mask, mask, masklen); - - err = pg_regcomp(&(Affix->reg.regex), mask, wmasklen, REG_ADVANCED | REG_NOSUB); - pfree(mask); - if (err) - { - char regerrstr[ERRSTRSIZE]; - - pg_regerror(err, &(Affix->reg.regex), regerrstr, ERRSTRSIZE); - elog(ERROR, "regex error in '%s': %s", Affix->mask, regerrstr); - } - Affix->compile = 0; - } - - /* Convert data string to wide characters */ - newword_len = strlen(newword); - data = (pg_wchar *) palloc((newword_len + 1) * sizeof(pg_wchar)); - data_len = pg_mb2wchar_with_len(newword, data, newword_len); - - if (!(err = pg_regexec(&(Affix->reg.regex), data, data_len, 0, NULL, 0, NULL, 0))) - { - pfree(data); - return newword; - } - pfree(data); - } - - return NULL; -} - - -static char ** -NormalizeSubWord(IspellDict * Conf, char *word, char flag) -{ - AffixNodeData *suffix = NULL, - *prefix = NULL; - int slevel = 0, - plevel = 0; - int wrdlen = strlen(word), - swrdlen; - char **forms; - char **cur; - char newword[2 * MAXNORMLEN] = ""; - char pnewword[2 * MAXNORMLEN] = ""; - AffixNode *snode = Conf->Suffix, - *pnode; - int i, - j; - - if (wrdlen > MAXNORMLEN) - return NULL; - cur = forms = (char **) palloc(MAX_NORM * sizeof(char *)); - *cur = NULL; - - - /* Check that the word itself is normal form */ - if (FindWord(Conf, word, 0, flag & FF_COMPOUNDWORD)) - { - *cur = pstrdup(word); - cur++; - *cur = NULL; - } - - /* Find all other NORMAL forms of the 'word' (check only prefix) */ - pnode = Conf->Prefix; - plevel = 0; - while (pnode) - { - prefix = FinfAffixes(pnode, word, wrdlen, &plevel, FF_PREFIX); - if (!prefix) - break; - for (j = 0; j < prefix->naff; j++) - { - if (CheckAffix(word, wrdlen, prefix->aff[j], flag, newword, NULL)) - { - /* prefix success */ - if (FindWord(Conf, newword, prefix->aff[j]->flag, flag & FF_COMPOUNDWORD) && (cur - forms) < (MAX_NORM - 1)) - { - /* word search success */ - *cur = pstrdup(newword); - cur++; - *cur = NULL; - } - } - } - pnode = prefix->node; - } - - /* - * Find all other NORMAL forms of the 'word' (check suffix and then - * prefix) - */ - while (snode) - { - int baselen = 0; - - /* find possible suffix */ - suffix = FinfAffixes(snode, word, wrdlen, &slevel, FF_SUFFIX); - if (!suffix) - break; - /* foreach suffix check affix */ - for (i = 0; i < suffix->naff; i++) - { - if (CheckAffix(word, wrdlen, suffix->aff[i], flag, newword, &baselen)) - { - /* suffix success */ - if (FindWord(Conf, newword, suffix->aff[i]->flag, flag & FF_COMPOUNDWORD) && (cur - forms) < (MAX_NORM - 1)) - { - /* word search success */ - *cur = pstrdup(newword); - cur++; - *cur = NULL; - } - /* now we will look changed word with prefixes */ - pnode = Conf->Prefix; - plevel = 0; - swrdlen = strlen(newword); - while (pnode) - { - prefix = FinfAffixes(pnode, newword, swrdlen, &plevel, FF_PREFIX); - if (!prefix) - break; - for (j = 0; j < prefix->naff; j++) - { - if (CheckAffix(newword, swrdlen, prefix->aff[j], flag, pnewword, &baselen)) - { - /* prefix success */ - int ff = (prefix->aff[j]->flagflags & suffix->aff[i]->flagflags & FF_CROSSPRODUCT) ? - 0 : prefix->aff[j]->flag; - - if (FindWord(Conf, pnewword, ff, flag & FF_COMPOUNDWORD) && (cur - forms) < (MAX_NORM - 1)) - { - /* word search success */ - *cur = pstrdup(pnewword); - cur++; - *cur = NULL; - } - } - } - pnode = prefix->node; - } - } - } - - snode = suffix->node; - } - - if (cur == forms) - { - pfree(forms); - return (NULL); - } - return (forms); -} - -typedef struct SplitVar -{ - int nstem; - char **stem; - struct SplitVar *next; -} SplitVar; - -static int -CheckCompoundAffixes(CMPDAffix ** ptr, char *word, int len, bool CheckInPlace) -{ - if (CheckInPlace) - { - while ((*ptr)->affix) - { - if (len > (*ptr)->len && strncmp((*ptr)->affix, word, (*ptr)->len) == 0) - { - len = (*ptr)->len; - (*ptr)++; - return len; - } - (*ptr)++; - } - } - else - { - char *affbegin; - - while ((*ptr)->affix) - { - if (len > (*ptr)->len && (affbegin = strstr(word, (*ptr)->affix)) != NULL) - { - len = (*ptr)->len + (affbegin - word); - (*ptr)++; - return len; - } - (*ptr)++; - } - } - return 0; -} - -static SplitVar * -CopyVar(SplitVar * s, int makedup) -{ - SplitVar *v = (SplitVar *) palloc(sizeof(SplitVar)); - - v->stem = (char **) palloc(sizeof(char *) * (MAX_NORM)); - v->next = NULL; - if (s) - { - int i; - - v->nstem = s->nstem; - for (i = 0; i < s->nstem; i++) - v->stem[i] = (makedup) ? pstrdup(s->stem[i]) : s->stem[i]; - } - else - v->nstem = 0; - return v; -} - - -static SplitVar * -SplitToVariants(IspellDict * Conf, SPNode * snode, SplitVar * orig, char *word, int wordlen, int startpos, int minpos) -{ - SplitVar *var = NULL; - SPNodeData *StopLow, - *StopHigh, - *StopMiddle = NULL; - SPNode *node = (snode) ? snode : Conf->Dictionary; - int level = (snode) ? minpos : startpos; /* recursive - * minpos==level */ - int lenaff; - CMPDAffix *caff; - char *notprobed; - - notprobed = (char *) palloc(wordlen); - memset(notprobed, 1, wordlen); - var = CopyVar(orig, 1); - - while (level < wordlen) - { - /* find word with epenthetic or/and compound suffix */ - caff = Conf->CompoundAffix; - while (level > startpos && (lenaff = CheckCompoundAffixes(&caff, word + level, wordlen - level, (node) ? true : false)) > 0) - { - /* - * there is one of compound suffixes, so check word for existings - */ - char buf[MAXNORMLEN]; - char **subres; - - lenaff = level - startpos + lenaff; - - if (!notprobed[startpos + lenaff - 1]) - continue; - - if (level + lenaff - 1 <= minpos) - continue; - - memcpy(buf, word + startpos, lenaff); - buf[lenaff] = '\0'; - - subres = NormalizeSubWord(Conf, buf, FF_COMPOUNDWORD | FF_COMPOUNDONLYAFX); - if (subres) - { - /* Yes, it was a word from dictionary */ - SplitVar *new = CopyVar(var, 0); - SplitVar *ptr = var; - char **sptr = subres; - - notprobed[startpos + lenaff - 1] = 0; - - while (*sptr) - { - new->stem[new->nstem] = *sptr; - new->nstem++; - sptr++; - } - pfree(subres); - - while (ptr->next) - ptr = ptr->next; - ptr->next = SplitToVariants(Conf, NULL, new, word, wordlen, startpos + lenaff, startpos + lenaff); - - pfree(new->stem); - pfree(new); - } - } - - if (!node) - break; - - StopLow = node->data; - StopHigh = node->data + node->length; - while (StopLow < StopHigh) - { - StopMiddle = StopLow + ((StopHigh - StopLow) >> 1); - if (StopMiddle->val == ((uint8 *) (word))[level]) - break; - else if (StopMiddle->val < ((uint8 *) (word))[level]) - StopLow = StopMiddle + 1; - else - StopHigh = StopMiddle; - } - - if (StopLow < StopHigh) - { - - /* find infinitive */ - if (StopMiddle->isword && StopMiddle->compoundallow && notprobed[level]) - { - /* ok, we found full compoundallowed word */ - if (level > minpos) - { - /* and its length more than minimal */ - if (wordlen == level + 1) - { - /* well, it was last word */ - var->stem[var->nstem] = strnduplicate(word + startpos, wordlen - startpos); - var->nstem++; - pfree(notprobed); - return var; - } - else - { - /* then we will search more big word at the same point */ - SplitVar *ptr = var; - - while (ptr->next) - ptr = ptr->next; - ptr->next = SplitToVariants(Conf, node, var, word, wordlen, startpos, level); - /* we can find next word */ - level++; - var->stem[var->nstem] = strnduplicate(word + startpos, level - startpos); - var->nstem++; - node = Conf->Dictionary; - startpos = level; - continue; - } - } - } - node = StopMiddle->node; - } - else - node = NULL; - level++; - } - - var->stem[var->nstem] = strnduplicate(word + startpos, wordlen - startpos); - var->nstem++; - pfree(notprobed); - return var; -} - -TSLexeme * -NINormalizeWord(IspellDict * Conf, char *uword) -{ - char **res; - char *word; - TSLexeme *lcur = NULL, - *lres = NULL; - uint16 NVariant = 1; - - word = lowerstr(uword); - res = NormalizeSubWord(Conf, word, 0); - - if (res) - { - char **ptr = res; - - lcur = lres = (TSLexeme *) palloc(MAX_NORM * sizeof(TSLexeme)); - while (*ptr) - { - lcur->lexeme = *ptr; - lcur->flags = 0; - lcur->nvariant = NVariant++; - lcur++; - ptr++; - } - lcur->lexeme = NULL; - pfree(res); - } - - if (Conf->compoundcontrol != '\t') - { - int wordlen = strlen(word); - SplitVar *ptr, - *var = SplitToVariants(Conf, NULL, NULL, word, wordlen, 0, -1); - int i; - - while (var) - { - if (var->nstem > 1) - { - char **subres = NormalizeSubWord(Conf, var->stem[var->nstem - 1], FF_COMPOUNDWORD); - - if (subres) - { - char **subptr = subres; - - if (!lcur) - lcur = lres = (TSLexeme *) palloc(MAX_NORM * sizeof(TSLexeme)); - - while (*subptr) - { - for (i = 0; i < var->nstem - 1; i++) - { - lcur->lexeme = (subptr == subres) ? var->stem[i] : pstrdup(var->stem[i]); - lcur->flags = 0; - lcur->nvariant = NVariant; - lcur++; - } - - lcur->lexeme = *subptr; - lcur->flags = 0; - lcur->nvariant = NVariant; - lcur++; - subptr++; - NVariant++; - } - - lcur->lexeme = NULL; - pfree(subres); - var->stem[0] = NULL; - pfree(var->stem[var->nstem - 1]); - } - } - - for (i = 0; i < var->nstem && var->stem[i]; i++) - pfree(var->stem[i]); - ptr = var->next; - pfree(var->stem); - pfree(var); - var = ptr; - } - } - - pfree(word); - - return lres; -} - - -static void -freeSPNode(SPNode * node) -{ - SPNodeData *data; - - if (!node) - return; - data = node->data; - while (node->length) - { - freeSPNode(data->node); - data++; - node->length--; - } - free(node); -} - -static void -freeANode(AffixNode * node) -{ - AffixNodeData *data; - - if (!node) - return; - data = node->data; - while (node->length) - { - freeANode(data->node); - if (data->naff) - free(data->aff); - data++; - node->length--; - } - free(node); -} - - -void -NIFree(IspellDict * Conf) -{ - int i; - AFFIX *Affix = (AFFIX *) Conf->Affix; - char **aff = Conf->AffixData; - - if (aff) - { - while (*aff) - { - free(*aff); - aff++; - } - free(Conf->AffixData); - } - - - for (i = 0; i < Conf->naffixes; i++) - { - if (Affix[i].compile == 0) - { - if (Affix[i].isregis) - RS_free(&(Affix[i].reg.regis)); - else - pg_regfree(&(Affix[i].reg.regex)); - } - if (Affix[i].mask != VoidString) - free(Affix[i].mask); - if (Affix[i].find != VoidString) - free(Affix[i].find); - if (Affix[i].repl != VoidString) - free(Affix[i].repl); - } - if (Conf->Spell) - { - for (i = 0; i < Conf->nspell; i++) - pfree(Conf->Spell[i]); - pfree(Conf->Spell); - } - - if (Conf->Affix) - free(Conf->Affix); - if (Conf->CompoundAffix) - free(Conf->CompoundAffix); - freeSPNode(Conf->Dictionary); - freeANode(Conf->Suffix); - freeANode(Conf->Prefix); - memset((void *) Conf, 0, sizeof(IspellDict)); - return; -} diff --git a/contrib/tsearch2/ispell/spell.h b/contrib/tsearch2/ispell/spell.h deleted file mode 100644 index 2b79f455a308..000000000000 --- a/contrib/tsearch2/ispell/spell.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef __SPELL_H__ -#define __SPELL_H__ - -#include "c.h" - -#include - -#include "regex/regex.h" - -#include "regis.h" -#include "dict.h" - -struct SPNode; - - -typedef struct -{ - uint32 - val:8, - isword:1, - compoundallow:1, - affix:22; - struct SPNode *node; -} SPNodeData; - -typedef struct SPNode -{ - uint32 length; - SPNodeData data[1]; -} SPNode; - -#define SPNHDRSZ (offsetof(SPNode,data)) - - -typedef struct spell_struct -{ - union - { - char flag[16]; - struct - { - int affix; - int len; - } d; - } p; - char word[1]; -} SPELL; - -#define SPELLHDRSZ (offsetof(SPELL, word)) - -typedef struct aff_struct -{ - uint32 - flag:8, - type:2, - compile:1, - flagflags:3, - issimple:1, - isregis:1, - unused:1, - replen:16; - char *mask; - char *find; - char *repl; - union - { - regex_t regex; - Regis regis; - } reg; -} AFFIX; - -#define FF_CROSSPRODUCT 0x01 -#define FF_COMPOUNDWORD 0x02 -#define FF_COMPOUNDONLYAFX 0x04 -#define FF_SUFFIX 2 -#define FF_PREFIX 1 - -struct AffixNode; - -typedef struct -{ - uint32 - val:8, - naff:24; - AFFIX **aff; - struct AffixNode *node; -} AffixNodeData; - -typedef struct AffixNode -{ - uint32 isvoid:1, - length:31; - AffixNodeData data[1]; -} AffixNode; - -#define ANHRDSZ (offsetof(AffixNode, data)) - -typedef struct -{ - char *affix; - int len; -} CMPDAffix; - -typedef struct -{ - int maffixes; - int naffixes; - AFFIX *Affix; - char compoundcontrol; - - int nspell; - int mspell; - SPELL **Spell; - - AffixNode *Suffix; - AffixNode *Prefix; - - SPNode *Dictionary; - char **AffixData; - CMPDAffix *CompoundAffix; - -} IspellDict; - -TSLexeme *NINormalizeWord(IspellDict * Conf, char *word); -int NIImportAffixes(IspellDict * Conf, const char *filename); -int NIImportOOAffixes(IspellDict * Conf, const char *filename); -int NIImportDictionary(IspellDict * Conf, const char *filename); - -int NIAddSpell(IspellDict * Conf, const char *word, const char *flag); -int NIAddAffix(IspellDict * Conf, int flag, char flagflags, const char *mask, const char *find, const char *repl, int type); -void NISortDictionary(IspellDict * Conf); -void NISortAffixes(IspellDict * Conf); -void NIFree(IspellDict * Conf); - -#endif diff --git a/contrib/tsearch2/my2ispell/Makefile b/contrib/tsearch2/my2ispell/Makefile deleted file mode 100644 index 426129a8df9d..000000000000 --- a/contrib/tsearch2/my2ispell/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -ZIPFILE=nb_NO -LANGUAGE=norsk - - -UNZIP=unzip -o - - -all: $(LANGUAGE).dict $(LANGUAGE).aff - -$(ZIPFILE).aff: $(ZIPFILE).zip - $(UNZIP) $? $@ - touch $@ - - -# 1 Cleanup dictionary -# 2 remove " symbol -# 3 add compoundwords controlled flag to word which hasn't it, but -# has compound only suffixes - -$(LANGUAGE).dict: $(ZIPFILE).zip - $(UNZIP) $? $(ZIPFILE).dic - grep -v -E '^[[:digit:]]+$$' < $(ZIPFILE).dic \ - | grep -v '\.' \ - | sed -e 's/"//g' \ - | perl -pi -e 's|/(\S+)| $$q=$$1; ( $$q=~/[\\_`]/ && $$q!~/z/ ) ? "/$${q}z" : "/$${q}"|e' \ - | sort \ - > $@ - -#just convert affix file - -$(LANGUAGE).aff: $(ZIPFILE).aff - grep -v -i zyzyzy $(ZIPFILE).aff \ - | grep -v -i zyzyzy \ - | perl -pi \ - -e 's/^COMPOUNDFLAG\s+(\S+)/compoundwords controlled $$1/;' \ - -e 's/^COMPOUNDMIN\s+(\d+)/compoundmin $$1/;' \ - -e 's/^PFX\s+(\S+)\s+([YN])\s+\d+.*$$/ if ( !$$wasprf ) { $$wasprf=1; "prefixes\n\nflag $$1:" } else { "flag $$1:" } /e;' \ - -e 's/^PFX\s+\S+\s+(\S+)\s+(\S+)\s+(\S+)/ uc(" $$3 > $$2")/e;' \ - -e 's/^(.*)SFX\s+(\S+)\s+([YN])\s+\d+.*$$/ $$flg=($$3 eq "Y") ? "*" : ""; $$flg="~$$flg" if length $$1; $$q=$$2; $$q="\\$$q" if $$q!~m#[a-zA-Z]#; if ( !$$wassfx ) { $$wassfx=1; "suffixes\n\nflag $$flg$$q:" } else { "flag $$flg$$q:" } /e;' \ - -e 's/^.*SFX\s+\S+\s+(\S+)\s+(\S+)\s+(\S+)/ uc(" $$3 > ".( ($$1 eq "0") ? "" : "-$$1,").( ($$2 eq "0") ? "" : "$$2") )/e;' \ - -e 's/^(SET|TRY)/#$$1/' \ - > $@ - -clean: - rm -rf $(ZIPFILE).aff $(ZIPFILE).dic $(LANGUAGE).dict $(LANGUAGE).aff - - diff --git a/contrib/tsearch2/my2ispell/README b/contrib/tsearch2/my2ispell/README deleted file mode 100644 index 583936ab42ea..000000000000 --- a/contrib/tsearch2/my2ispell/README +++ /dev/null @@ -1,12 +0,0 @@ -Utility for convert MySpell dictionary and affix from -myspell to ispell format. -Utility tested on nb_NO.zip and nn_NO.zip from -OpenOffice (http://lingucomponent.openoffice.org/download_dictionary.html) - -usage: -For example, make norwegian dictionary and affix: -% cp nb_NO.zip my2ispell -% cd my2ispell -% gmake ZIPFILE=nb_NO LANGUAGE=norsk - -Author: Teodor Sigaev diff --git a/contrib/tsearch2/prs_dcfg.c b/contrib/tsearch2/prs_dcfg.c deleted file mode 100644 index c519f5f3d4d4..000000000000 --- a/contrib/tsearch2/prs_dcfg.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Simple config parser - * Teodor Sigaev - */ -#include "postgres.h" - -#include - -#include "dict.h" -#include "common.h" -#include "ts_locale.h" - -#define CS_WAITKEY 0 -#define CS_INKEY 1 -#define CS_WAITEQ 2 -#define CS_WAITVALUE 3 -#define CS_INVALUE 4 -#define CS_IN2VALUE 5 -#define CS_WAITDELIM 6 -#define CS_INESC 7 -#define CS_IN2ESC 8 - -static char * -nstrdup(char *ptr, int len) -{ - char *res = palloc(len + 1), - *cptr; - - memcpy(res, ptr, len); - res[len] = '\0'; - cptr = ptr = res; - while (*ptr) - { - if (t_iseq(ptr, '\\')) - ptr++; - COPYCHAR(cptr, ptr); - cptr += pg_mblen(ptr); - ptr += pg_mblen(ptr); - } - *cptr = '\0'; - - return res; -} - -void -parse_cfgdict(text *in, Map ** m) -{ - Map *mptr; - char *ptr = VARDATA(in), - *begin = NULL; - char num = 0; - int state = CS_WAITKEY; - - while (ptr - VARDATA(in) < VARSIZE(in) - VARHDRSZ) - { - if (t_iseq(ptr, ',')) - num++; - ptr += pg_mblen(ptr); - } - - *m = mptr = (Map *) palloc(sizeof(Map) * (num + 2)); - memset(mptr, 0, sizeof(Map) * (num + 2)); - ptr = VARDATA(in); - while (ptr - VARDATA(in) < VARSIZE(in) - VARHDRSZ) - { - if (state == CS_WAITKEY) - { - if (t_isalpha(ptr)) - { - begin = ptr; - state = CS_INKEY; - } - else if (!t_isspace(ptr)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"), - errdetail("Syntax error in position %d.", - (int) (ptr - VARDATA(in))))); - } - else if (state == CS_INKEY) - { - if (t_isspace(ptr)) - { - mptr->key = nstrdup(begin, ptr - begin); - state = CS_WAITEQ; - } - else if (t_iseq(ptr, '=')) - { - mptr->key = nstrdup(begin, ptr - begin); - state = CS_WAITVALUE; - } - else if (!t_isalpha(ptr)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"), - errdetail("Syntax error in position %d.", - (int) (ptr - VARDATA(in))))); - } - else if (state == CS_WAITEQ) - { - if (t_iseq(ptr, '=')) - state = CS_WAITVALUE; - else if (!t_isspace(ptr)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"), - errdetail("Syntax error in position %d.", - (int) (ptr - VARDATA(in))))); - } - else if (state == CS_WAITVALUE) - { - if (t_iseq(ptr, '"')) - { - begin = ptr + 1; - state = CS_INVALUE; - } - else if (!t_isspace(ptr)) - { - begin = ptr; - state = CS_IN2VALUE; - } - } - else if (state == CS_INVALUE) - { - if (t_iseq(ptr, '"')) - { - mptr->value = nstrdup(begin, ptr - begin); - mptr++; - state = CS_WAITDELIM; - } - else if (t_iseq(ptr, '\\')) - state = CS_INESC; - } - else if (state == CS_IN2VALUE) - { - if (t_isspace(ptr) || t_iseq(ptr, ',')) - { - mptr->value = nstrdup(begin, ptr - begin); - mptr++; - state = (t_iseq(ptr, ',')) ? CS_WAITKEY : CS_WAITDELIM; - } - else if (t_iseq(ptr, '\\')) - state = CS_INESC; - } - else if (state == CS_WAITDELIM) - { - if (t_iseq(ptr, ',')) - state = CS_WAITKEY; - else if (!t_isspace(ptr)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"), - errdetail("Syntax error in position %d.", - (int) (ptr - VARDATA(in))))); - } - else if (state == CS_INESC) - state = CS_INVALUE; - else if (state == CS_IN2ESC) - state = CS_IN2VALUE; - else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad parser state"), - errdetail("%d at position %d.", - state, (int) (ptr - VARDATA(in))))); - ptr += pg_mblen(ptr); - } - - if (state == CS_IN2VALUE) - { - mptr->value = nstrdup(begin, ptr - begin); - mptr++; - } - else if (!(state == CS_WAITDELIM || state == CS_WAITKEY)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unexpected end of line"))); -} diff --git a/contrib/tsearch2/query.c b/contrib/tsearch2/query.c deleted file mode 100644 index 89dbf6f00575..000000000000 --- a/contrib/tsearch2/query.c +++ /dev/null @@ -1,1055 +0,0 @@ -/* - * IO definitions for tsquery and mtsquery. This type - * are identical, but for parsing mtsquery used parser for text - * and also morphology is used. - * Internal structure: - * query tree, then string with original value. - * Query tree with plain view. It's means that in array of nodes - * right child is always next and left position = item+item->left - * Teodor Sigaev - */ -#include "postgres.h" - -#include -#include - -#include "access/gist.h" -#include "access/itup.h" -#include "storage/bufpage.h" -#include "utils/array.h" -#include "utils/builtins.h" - -#include "ts_cfg.h" -#include "tsvector.h" -#include "crc32.h" -#include "query.h" -#include "query_cleanup.h" -#include "common.h" -#include "ts_locale.h" - -PG_FUNCTION_INFO_V1(tsquery_in); -Datum tsquery_in(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(tsquery_out); -Datum tsquery_out(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(exectsq); -Datum exectsq(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(rexectsq); -Datum rexectsq(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(tsquerytree); -Datum tsquerytree(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(to_tsquery); -Datum to_tsquery(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(to_tsquery_name); -Datum to_tsquery_name(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(to_tsquery_current); -Datum to_tsquery_current(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(plainto_tsquery); -Datum plainto_tsquery(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(plainto_tsquery_name); -Datum plainto_tsquery_name(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(plainto_tsquery_current); -Datum plainto_tsquery_current(PG_FUNCTION_ARGS); - -/* parser's states */ -#define WAITOPERAND 1 -#define WAITOPERATOR 2 -#define WAITFIRSTOPERAND 3 -#define WAITSINGLEOPERAND 4 - -/* - * node of query tree, also used - * for storing polish notation in parser - */ -typedef struct NODE -{ - int2 weight; - int2 type; - int4 val; - int2 distance; - int2 length; - struct NODE *next; -} NODE; - -typedef struct -{ - char *buffer; /* entire string we are scanning */ - char *buf; /* current scan point */ - int4 state; - int4 count; - /* reverse polish notation in list (for temprorary usage) */ - NODE *str; - /* number in str */ - int4 num; - - /* user-friendly operand */ - int4 lenop; - int4 sumlen; - char *op; - char *curop; - - /* state for value's parser */ - TI_IN_STATE valstate; - - /* tscfg */ - int cfg_id; -} QPRS_STATE; - -static char * -get_weight(char *buf, int2 *weight) -{ - *weight = 0; - - if (!t_iseq(buf, ':')) - return buf; - - buf++; - while (*buf && pg_mblen(buf) == 1) - { - switch (*buf) - { - case 'a': - case 'A': - *weight |= 1 << 3; - break; - case 'b': - case 'B': - *weight |= 1 << 2; - break; - case 'c': - case 'C': - *weight |= 1 << 1; - break; - case 'd': - case 'D': - *weight |= 1; - break; - default: - return buf; - } - buf++; - } - - return buf; -} - -/* - * get token from query string - */ -static int4 -gettoken_query(QPRS_STATE * state, int4 *val, int4 *lenval, char **strval, int2 *weight) -{ - while (1) - { - switch (state->state) - { - case WAITFIRSTOPERAND: - case WAITOPERAND: - if (t_iseq(state->buf, '!')) - { - (state->buf)++; /* can safely ++, t_iseq guarantee - * that pg_mblen()==1 */ - *val = (int4) '!'; - state->state = WAITOPERAND; - return OPR; - } - else if (t_iseq(state->buf, '(')) - { - state->count++; - (state->buf)++; - state->state = WAITOPERAND; - return OPEN; - } - else if (t_iseq(state->buf, ':')) - { - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("error at start of operand in tsearch query: \"%s\"", - state->buffer))); - } - else if (!t_isspace(state->buf)) - { - state->valstate.prsbuf = state->buf; - if (gettoken_tsvector(&(state->valstate))) - { - *strval = state->valstate.word; - *lenval = state->valstate.curpos - state->valstate.word; - state->buf = get_weight(state->valstate.prsbuf, weight); - state->state = WAITOPERATOR; - return VAL; - } - else if (state->state == WAITFIRSTOPERAND) - return END; - else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("no operand in tsearch query: \"%s\"", - state->buffer))); - } - break; - case WAITOPERATOR: - if (t_iseq(state->buf, '&') || t_iseq(state->buf, '|')) - { - state->state = WAITOPERAND; - *val = (int4) *(state->buf); - (state->buf)++; - return OPR; - } - else if (t_iseq(state->buf, ')')) - { - (state->buf)++; - state->count--; - return (state->count < 0) ? ERR : CLOSE; - } - else if (*(state->buf) == '\0') - return (state->count) ? ERR : END; - else if (!t_isspace(state->buf)) - return ERR; - break; - case WAITSINGLEOPERAND: - if (*(state->buf) == '\0') - return END; - *strval = state->buf; - *lenval = strlen(state->buf); - state->buf += strlen(state->buf); - state->count++; - return VAL; - default: - return ERR; - break; - } - state->buf += pg_mblen(state->buf); - } - return END; -} - -/* - * push new one in polish notation reverse view - */ -static void -pushquery(QPRS_STATE * state, int4 type, int4 val, int4 distance, int4 lenval, int2 weight) -{ - NODE *tmp = (NODE *) palloc(sizeof(NODE)); - - tmp->weight = weight; - tmp->type = type; - tmp->val = val; - if (distance >= MAXSTRPOS) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("value is too big in tsearch query: \"%s\"", - state->buffer))); - if (lenval >= MAXSTRLEN) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("operand is too long in tsearch query: \"%s\"", - state->buffer))); - tmp->distance = distance; - tmp->length = lenval; - tmp->next = state->str; - state->str = tmp; - state->num++; -} - -/* - * This function is used for tsquery parsing - */ -static void -pushval_asis(QPRS_STATE * state, int type, char *strval, int lenval, int2 weight) -{ - if (lenval >= MAXSTRLEN) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("word is too long in tsearch query: \"%s\"", - state->buffer))); - - pushquery(state, type, crc32_sz(strval, lenval), - state->curop - state->op, lenval, weight); - - while (state->curop - state->op + lenval + 1 >= state->lenop) - { - int4 tmp = state->curop - state->op; - - state->lenop *= 2; - state->op = (char *) repalloc((void *) state->op, state->lenop); - state->curop = state->op + tmp; - } - memcpy((void *) state->curop, (void *) strval, lenval); - state->curop += lenval; - *(state->curop) = '\0'; - state->curop++; - state->sumlen += lenval + 1; - return; -} - -/* - * This function is used for morph parsing - */ -static void -pushval_morph(QPRS_STATE * state, int typeval, char *strval, int lenval, int2 weight) -{ - int4 count = 0; - PRSTEXT prs; - uint32 variant, - pos, - cntvar = 0, - cntpos = 0, - cnt = 0; - - prs.lenwords = 32; - prs.curwords = 0; - prs.pos = 0; - prs.words = (TSWORD *) palloc(sizeof(TSWORD) * prs.lenwords); - - parsetext_v2(findcfg(state->cfg_id), &prs, strval, lenval); - - if (prs.curwords > 0) - { - - while (count < prs.curwords) - { - pos = prs.words[count].pos.pos; - cntvar = 0; - while (count < prs.curwords && pos == prs.words[count].pos.pos) - { - variant = prs.words[count].nvariant; - - cnt = 0; - while (count < prs.curwords && pos == prs.words[count].pos.pos && variant == prs.words[count].nvariant) - { - - pushval_asis(state, VAL, prs.words[count].word, prs.words[count].len, weight); - pfree(prs.words[count].word); - if (cnt) - pushquery(state, OPR, (int4) '&', 0, 0, 0); - cnt++; - count++; - } - - if (cntvar) - pushquery(state, OPR, (int4) '|', 0, 0, 0); - cntvar++; - } - - if (cntpos) - pushquery(state, OPR, (int4) '&', 0, 0, 0); - - cntpos++; - } - - pfree(prs.words); - - } - else - pushval_asis(state, VALSTOP, NULL, 0, 0); -} - -#define STACKDEPTH 32 -/* - * make polish notaion of query - */ -static int4 -makepol(QPRS_STATE * state, void (*pushval) (QPRS_STATE *, int, char *, int, int2)) -{ - int4 val = 0, - type; - int4 lenval = 0; - char *strval = NULL; - int4 stack[STACKDEPTH]; - int4 lenstack = 0; - int2 weight = 0; - - while ((type = gettoken_query(state, &val, &lenval, &strval, &weight)) != END) - { - switch (type) - { - case VAL: - (*pushval) (state, VAL, strval, lenval, weight); - while (lenstack && (stack[lenstack - 1] == (int4) '&' || - stack[lenstack - 1] == (int4) '!')) - { - lenstack--; - pushquery(state, OPR, stack[lenstack], 0, 0, 0); - } - break; - case OPR: - if (lenstack && val == (int4) '|') - pushquery(state, OPR, val, 0, 0, 0); - else - { - if (lenstack == STACKDEPTH) - /* internal error */ - elog(ERROR, "stack too short"); - stack[lenstack] = val; - lenstack++; - } - break; - case OPEN: - if (makepol(state, pushval) == ERR) - return ERR; - if (lenstack && (stack[lenstack - 1] == (int4) '&' || - stack[lenstack - 1] == (int4) '!')) - { - lenstack--; - pushquery(state, OPR, stack[lenstack], 0, 0, 0); - } - break; - case CLOSE: - while (lenstack) - { - lenstack--; - pushquery(state, OPR, stack[lenstack], 0, 0, 0); - }; - return END; - break; - case ERR: - default: - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error in tsearch query: \"%s\"", - state->buffer))); - return ERR; - - } - } - while (lenstack) - { - lenstack--; - pushquery(state, OPR, stack[lenstack], 0, 0, 0); - }; - return END; -} - -typedef struct -{ - WordEntry *arrb; - WordEntry *arre; - char *values; - char *operand; -} CHKVAL; - -/* - * compare 2 string values - */ -static int4 -ValCompare(CHKVAL * chkval, WordEntry * ptr, ITEM * item) -{ - if (ptr->len == item->length) - return strncmp( - &(chkval->values[ptr->pos]), - &(chkval->operand[item->distance]), - item->length); - - return (ptr->len > item->length) ? 1 : -1; -} - -/* - * check weight info - */ -static bool -checkclass_str(CHKVAL * chkval, WordEntry * val, ITEM * item) -{ - WordEntryPos *ptr = (WordEntryPos *) (chkval->values + val->pos + SHORTALIGN(val->len) + sizeof(uint16)); - uint16 len = *((uint16 *) (chkval->values + val->pos + SHORTALIGN(val->len))); - - while (len--) - { - if (item->weight & (1 << WEP_GETWEIGHT(*ptr))) - return true; - ptr++; - } - return false; -} - -/* - * is there value 'val' in array or not ? - */ -static bool -checkcondition_str(void *checkval, ITEM * val) -{ - WordEntry *StopLow = ((CHKVAL *) checkval)->arrb; - WordEntry *StopHigh = ((CHKVAL *) checkval)->arre; - WordEntry *StopMiddle; - int difference; - - /* Loop invariant: StopLow <= val < StopHigh */ - - while (StopLow < StopHigh) - { - StopMiddle = StopLow + (StopHigh - StopLow) / 2; - difference = ValCompare((CHKVAL *) checkval, StopMiddle, val); - if (difference == 0) - return (val->weight && StopMiddle->haspos) ? - checkclass_str((CHKVAL *) checkval, StopMiddle, val) : true; - else if (difference < 0) - StopLow = StopMiddle + 1; - else - StopHigh = StopMiddle; - } - - return (false); -} - -/* - * check for boolean condition - */ -bool -TS_execute(ITEM * curitem, void *checkval, bool calcnot, bool (*chkcond) (void *checkval, ITEM * val)) -{ - if (curitem->type == VAL) - return (*chkcond) (checkval, curitem); - else if (curitem->val == (int4) '!') - { - return (calcnot) ? - ((TS_execute(curitem + 1, checkval, calcnot, chkcond)) ? false : true) - : true; - } - else if (curitem->val == (int4) '&') - { - if (TS_execute(curitem + curitem->left, checkval, calcnot, chkcond)) - return TS_execute(curitem + 1, checkval, calcnot, chkcond); - else - return false; - } - else - { /* |-operator */ - if (TS_execute(curitem + curitem->left, checkval, calcnot, chkcond)) - return true; - else - return TS_execute(curitem + 1, checkval, calcnot, chkcond); - } - return false; -} - -/* - * boolean operations - */ -Datum -rexectsq(PG_FUNCTION_ARGS) -{ - SET_FUNCOID(); - return DirectFunctionCall2( - exectsq, - PG_GETARG_DATUM(1), - PG_GETARG_DATUM(0) - ); -} - -Datum -exectsq(PG_FUNCTION_ARGS) -{ - tsvector *val = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - CHKVAL chkval; - bool result; - - SET_FUNCOID(); - if (!val->size || !query->size) - { - PG_FREE_IF_COPY(val, 0); - PG_FREE_IF_COPY(query, 1); - PG_RETURN_BOOL(false); - } - - chkval.arrb = ARRPTR(val); - chkval.arre = chkval.arrb + val->size; - chkval.values = STRPTR(val); - chkval.operand = GETOPERAND(query); - result = TS_execute( - GETQUERY(query), - &chkval, - true, - checkcondition_str - ); - - PG_FREE_IF_COPY(val, 0); - PG_FREE_IF_COPY(query, 1); - PG_RETURN_BOOL(result); -} - -/* - * find left operand in polish notation view - */ -static void -findoprnd(ITEM * ptr, int4 *pos) -{ -#ifdef BS_DEBUG - elog(DEBUG3, (ptr[*pos].type == OPR) ? - "%d %c" : "%d %d", *pos, ptr[*pos].val); -#endif - if (ptr[*pos].type == VAL || ptr[*pos].type == VALSTOP) - { - ptr[*pos].left = 0; - (*pos)++; - } - else if (ptr[*pos].val == (int4) '!') - { - ptr[*pos].left = 1; - (*pos)++; - findoprnd(ptr, pos); - } - else - { - ITEM *curitem = &ptr[*pos]; - int4 tmp = *pos; - - (*pos)++; - findoprnd(ptr, pos); - curitem->left = *pos - tmp; - findoprnd(ptr, pos); - } -} - - -/* - * input - */ -static QUERYTYPE * - queryin(char *buf, void (*pushval) (QPRS_STATE *, int, char *, int, int2), int cfg_id, bool isplain) -{ - QPRS_STATE state; - int4 i; - QUERYTYPE *query; - int4 commonlen; - ITEM *ptr; - NODE *tmp; - int4 pos = 0; - -#ifdef BS_DEBUG - char pbuf[16384], - *cur; -#endif - - /* init state */ - state.buffer = buf; - state.buf = buf; - state.state = (isplain) ? WAITSINGLEOPERAND : WAITFIRSTOPERAND; - state.count = 0; - state.num = 0; - state.str = NULL; - state.cfg_id = cfg_id; - - /* init value parser's state */ - state.valstate.oprisdelim = true; - state.valstate.len = 32; - state.valstate.word = (char *) palloc(state.valstate.len); - - /* init list of operand */ - state.sumlen = 0; - state.lenop = 64; - state.curop = state.op = (char *) palloc(state.lenop); - *(state.curop) = '\0'; - - /* parse query & make polish notation (postfix, but in reverse order) */ - makepol(&state, pushval); - pfree(state.valstate.word); - if (!state.num) - { - ereport(NOTICE, - (errmsg("tsearch query doesn't contain lexeme(s): \"%s\"", - state.buffer))); - query = (QUERYTYPE *) palloc(HDRSIZEQT); - SET_VARSIZE(query, HDRSIZEQT); - query->size = 0; - return query; - } - - /* make finish struct */ - commonlen = COMPUTESIZE(state.num, state.sumlen); - query = (QUERYTYPE *) palloc(commonlen); - SET_VARSIZE(query, commonlen); - query->size = state.num; - ptr = GETQUERY(query); - - /* set item in polish notation */ - for (i = 0; i < state.num; i++) - { - ptr[i].weight = state.str->weight; - ptr[i].type = state.str->type; - ptr[i].val = state.str->val; - ptr[i].distance = state.str->distance; - ptr[i].length = state.str->length; - tmp = state.str->next; - pfree(state.str); - state.str = tmp; - } - - /* set user friendly-operand view */ - memcpy((void *) GETOPERAND(query), (void *) state.op, state.sumlen); - pfree(state.op); - - /* set left operand's position for every operator */ - pos = 0; - findoprnd(ptr, &pos); - -#ifdef BS_DEBUG - cur = pbuf; - *cur = '\0'; - for (i = 0; i < query->size; i++) - { - if (ptr[i].type == OPR) - sprintf(cur, "%c(%d) ", ptr[i].val, ptr[i].left); - else - sprintf(cur, "%d(%s) ", ptr[i].val, GETOPERAND(query) + ptr[i].distance); - cur = strchr(cur, '\0'); - } - elog(DEBUG3, "POR: %s", pbuf); -#endif - - return query; -} - -/* - * in without morphology - */ -Datum -tsquery_in(PG_FUNCTION_ARGS) -{ - char *in = (char *) PG_GETARG_POINTER(0); - - pg_verifymbstr(in, strlen(in), false); - - SET_FUNCOID(); - PG_RETURN_POINTER(queryin((char *) in, pushval_asis, 0, false)); -} - -/* - * out function - */ -typedef struct -{ - ITEM *curpol; - char *buf; - char *cur; - char *op; - int4 buflen; -} INFIX; - -#define RESIZEBUF(inf,addsize) \ -while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \ -{ \ - int4 len = (inf)->cur - (inf)->buf; \ - (inf)->buflen *= 2; \ - (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \ - (inf)->cur = (inf)->buf + len; \ -} - -/* - * recursive walk on tree and print it in - * infix (human-readable) view - */ -static void -infix(INFIX * in, bool first) -{ - if (in->curpol->type == VAL) - { - char *op = in->op + in->curpol->distance; - int clen; - - RESIZEBUF(in, in->curpol->length * (pg_database_encoding_max_length() + 1) + 2 + 5); - *(in->cur) = '\''; - in->cur++; - while (*op) - { - if (t_iseq(op, '\'')) - { - *(in->cur) = '\''; - in->cur++; - } - else if (t_iseq(op, '\\')) - { - *(in->cur) = '\\'; - in->cur++; - } - COPYCHAR(in->cur, op); - - clen = pg_mblen(op); - op += clen; - in->cur += clen; - } - *(in->cur) = '\''; - in->cur++; - if (in->curpol->weight) - { - *(in->cur) = ':'; - in->cur++; - if (in->curpol->weight & (1 << 3)) - { - *(in->cur) = 'A'; - in->cur++; - } - if (in->curpol->weight & (1 << 2)) - { - *(in->cur) = 'B'; - in->cur++; - } - if (in->curpol->weight & (1 << 1)) - { - *(in->cur) = 'C'; - in->cur++; - } - if (in->curpol->weight & 1) - { - *(in->cur) = 'D'; - in->cur++; - } - } - *(in->cur) = '\0'; - in->curpol++; - } - else if (in->curpol->val == (int4) '!') - { - bool isopr = false; - - RESIZEBUF(in, 1); - *(in->cur) = '!'; - in->cur++; - *(in->cur) = '\0'; - in->curpol++; - if (in->curpol->type == OPR) - { - isopr = true; - RESIZEBUF(in, 2); - sprintf(in->cur, "( "); - in->cur = strchr(in->cur, '\0'); - } - infix(in, isopr); - if (isopr) - { - RESIZEBUF(in, 2); - sprintf(in->cur, " )"); - in->cur = strchr(in->cur, '\0'); - } - } - else - { - int4 op = in->curpol->val; - INFIX nrm; - - in->curpol++; - if (op == (int4) '|' && !first) - { - RESIZEBUF(in, 2); - sprintf(in->cur, "( "); - in->cur = strchr(in->cur, '\0'); - } - - nrm.curpol = in->curpol; - nrm.op = in->op; - nrm.buflen = 16; - nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen); - - /* get right operand */ - infix(&nrm, false); - - /* get & print left operand */ - in->curpol = nrm.curpol; - infix(in, false); - - /* print operator & right operand */ - RESIZEBUF(in, 3 + (nrm.cur - nrm.buf)); - sprintf(in->cur, " %c %s", op, nrm.buf); - in->cur = strchr(in->cur, '\0'); - pfree(nrm.buf); - - if (op == (int4) '|' && !first) - { - RESIZEBUF(in, 2); - sprintf(in->cur, " )"); - in->cur = strchr(in->cur, '\0'); - } - } -} - - -Datum -tsquery_out(PG_FUNCTION_ARGS) -{ - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - INFIX nrm; - - if (query->size == 0) - { - char *b = palloc(1); - - *b = '\0'; - PG_RETURN_POINTER(b); - } - nrm.curpol = GETQUERY(query); - nrm.buflen = 32; - nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen); - *(nrm.cur) = '\0'; - nrm.op = GETOPERAND(query); - infix(&nrm, true); - - PG_FREE_IF_COPY(query, 0); - PG_RETURN_POINTER(nrm.buf); -} - -/* - * debug function, used only for view query - * which will be executed in non-leaf pages in index - */ -Datum -tsquerytree(PG_FUNCTION_ARGS) -{ - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - INFIX nrm; - text *res; - ITEM *q; - int4 len; - - - if (query->size == 0) - { - res = (text *) palloc(VARHDRSZ); - SET_VARSIZE(res, VARHDRSZ); - PG_RETURN_POINTER(res); - } - - q = clean_NOT_v2(GETQUERY(query), &len); - - if (!q) - { - res = (text *) palloc(1 + VARHDRSZ); - SET_VARSIZE(res, 1 + VARHDRSZ); - *((char *) VARDATA(res)) = 'T'; - } - else - { - nrm.curpol = q; - nrm.buflen = 32; - nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen); - *(nrm.cur) = '\0'; - nrm.op = GETOPERAND(query); - infix(&nrm, true); - - res = (text *) palloc(nrm.cur - nrm.buf + VARHDRSZ); - SET_VARSIZE(res, nrm.cur - nrm.buf + VARHDRSZ); - memcpy(VARDATA(res), nrm.buf, nrm.cur - nrm.buf); - pfree(q); - } - - PG_FREE_IF_COPY(query, 0); - - PG_RETURN_POINTER(res); -} - -Datum -to_tsquery(PG_FUNCTION_ARGS) -{ - text *in = PG_GETARG_TEXT_P(1); - char *str; - QUERYTYPE *query; - ITEM *res; - int4 len; - - SET_FUNCOID(); - - str = text2char(in); - PG_FREE_IF_COPY(in, 1); - - query = queryin(str, pushval_morph, PG_GETARG_INT32(0), false); - - if (query->size == 0) - PG_RETURN_POINTER(query); - - res = clean_fakeval_v2(GETQUERY(query), &len); - if (!res) - { - SET_VARSIZE(query, HDRSIZEQT); - query->size = 0; - PG_RETURN_POINTER(query); - } - memcpy((void *) GETQUERY(query), (void *) res, len * sizeof(ITEM)); - pfree(res); - PG_RETURN_POINTER(query); -} - -Datum -to_tsquery_name(PG_FUNCTION_ARGS) -{ - text *name = PG_GETARG_TEXT_P(0); - Datum res; - - SET_FUNCOID(); - res = DirectFunctionCall2(to_tsquery, - Int32GetDatum(name2id_cfg(name)), - PG_GETARG_DATUM(1)); - - PG_FREE_IF_COPY(name, 0); - PG_RETURN_DATUM(res); -} - -Datum -to_tsquery_current(PG_FUNCTION_ARGS) -{ - SET_FUNCOID(); - PG_RETURN_DATUM(DirectFunctionCall2(to_tsquery, - Int32GetDatum(get_currcfg()), - PG_GETARG_DATUM(0))); -} - -Datum -plainto_tsquery(PG_FUNCTION_ARGS) -{ - text *in = PG_GETARG_TEXT_P(1); - char *str; - QUERYTYPE *query; - ITEM *res; - int4 len; - - SET_FUNCOID(); - - str = text2char(in); - PG_FREE_IF_COPY(in, 1); - - query = queryin(str, pushval_morph, PG_GETARG_INT32(0), true); - - if (query->size == 0) - PG_RETURN_POINTER(query); - - res = clean_fakeval_v2(GETQUERY(query), &len); - if (!res) - { - SET_VARSIZE(query, HDRSIZEQT); - query->size = 0; - PG_RETURN_POINTER(query); - } - memcpy((void *) GETQUERY(query), (void *) res, len * sizeof(ITEM)); - pfree(res); - PG_RETURN_POINTER(query); -} - -Datum -plainto_tsquery_name(PG_FUNCTION_ARGS) -{ - text *name = PG_GETARG_TEXT_P(0); - Datum res; - - SET_FUNCOID(); - res = DirectFunctionCall2(plainto_tsquery, - Int32GetDatum(name2id_cfg(name)), - PG_GETARG_DATUM(1)); - - PG_FREE_IF_COPY(name, 0); - PG_RETURN_DATUM(res); -} - -Datum -plainto_tsquery_current(PG_FUNCTION_ARGS) -{ - SET_FUNCOID(); - PG_RETURN_DATUM(DirectFunctionCall2(plainto_tsquery, - Int32GetDatum(get_currcfg()), - PG_GETARG_DATUM(0))); -} diff --git a/contrib/tsearch2/query.h b/contrib/tsearch2/query.h deleted file mode 100644 index 44f87012130d..000000000000 --- a/contrib/tsearch2/query.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef __QUERY_H__ -#define __QUERY_H__ -/* -#define BS_DEBUG -*/ - -#include "ts_locale.h" -/* - * item in polish notation with back link - * to left operand - */ -typedef struct ITEM -{ - int8 type; - int8 weight; - int2 left; - int4 val; - /* user-friendly value, must correlate with WordEntry */ - uint32 - istrue:1, /* use for ranking in Cover */ - length:11, - distance:20; -} ITEM; - -/* - *Storage: - * (len)(size)(array of ITEM)(array of operand in user-friendly form) - */ -typedef struct -{ - int32 vl_len_; /* varlena header (do not touch directly!) */ - int4 size; - char data[1]; -} QUERYTYPE; - -#define HDRSIZEQT ( VARHDRSZ + sizeof(int4) ) -#define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + (size) * sizeof(ITEM) + (lenofoperand) ) -#define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT ) -#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((QUERYTYPE*)(x))->size * sizeof(ITEM) ) - -#define ISOPERATOR(x) ( pg_mblen(x)==1 && ( *(x)=='!' || *(x)=='&' || *(x)=='|' || *(x)=='(' || *(x)==')' ) ) - -#define END 0 -#define ERR 1 -#define VAL 2 -#define OPR 3 -#define OPEN 4 -#define CLOSE 5 -#define VALSTOP 6 /* for stop words */ - -bool TS_execute(ITEM * curitem, void *checkval, - bool calcnot, bool (*chkcond) (void *checkval, ITEM * val)); - -#endif diff --git a/contrib/tsearch2/query_cleanup.h b/contrib/tsearch2/query_cleanup.h deleted file mode 100644 index 5879f644e649..000000000000 --- a/contrib/tsearch2/query_cleanup.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __REWRITE_H__ -#define __REWRITE_H__ - -#include "query.h" - -ITEM *clean_NOT_v2(ITEM * ptr, int4 *len); -ITEM *clean_fakeval_v2(ITEM * ptr, int4 *len); - -#endif diff --git a/contrib/tsearch2/query_gist.c b/contrib/tsearch2/query_gist.c deleted file mode 100644 index 5a547e112965..000000000000 --- a/contrib/tsearch2/query_gist.c +++ /dev/null @@ -1,392 +0,0 @@ -#include "postgres.h" - -#include "access/skey.h" -#include "storage/bufpage.h" -#include "access/gist.h" - -#include "query.h" - -typedef uint64 TPQTGist; - -#define SIGLEN (sizeof(TPQTGist)*BITS_PER_BYTE) - - -#define GETENTRY(vec,pos) ((TPQTGist *) DatumGetPointer((vec)->vector[(pos)].key)) - -PG_FUNCTION_INFO_V1(tsq_mcontains); -Datum tsq_mcontains(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(tsq_mcontained); -Datum tsq_mcontained(PG_FUNCTION_ARGS); - -static TPQTGist -makesign(QUERYTYPE * a) -{ - int i; - ITEM *ptr = GETQUERY(a); - TPQTGist sign = 0; - - for (i = 0; i < a->size; i++) - { - if (ptr->type == VAL) - sign |= ((TPQTGist) 1) << (ptr->val % SIGLEN); - ptr++; - } - - return sign; -} - -Datum -tsq_mcontains(PG_FUNCTION_ARGS) -{ - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - QUERYTYPE *ex = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - TPQTGist sq, - se; - int i, - j; - ITEM *iq, - *ie; - - if (query->size < ex->size) - { - PG_FREE_IF_COPY(query, 0); - PG_FREE_IF_COPY(ex, 1); - - PG_RETURN_BOOL(false); - } - - sq = makesign(query); - se = makesign(ex); - - if ((sq & se) != se) - { - PG_FREE_IF_COPY(query, 0); - PG_FREE_IF_COPY(ex, 1); - - PG_RETURN_BOOL(false); - } - - ie = GETQUERY(ex); - - for (i = 0; i < ex->size; i++) - { - iq = GETQUERY(query); - if (ie[i].type != VAL) - continue; - for (j = 0; j < query->size; j++) - if (iq[j].type == VAL && ie[i].val == iq[j].val) - { - j = query->size + 1; - break; - } - if (j == query->size) - { - PG_FREE_IF_COPY(query, 0); - PG_FREE_IF_COPY(ex, 1); - - PG_RETURN_BOOL(false); - } - } - - PG_FREE_IF_COPY(query, 0); - PG_FREE_IF_COPY(ex, 1); - - PG_RETURN_BOOL(true); -} - -Datum -tsq_mcontained(PG_FUNCTION_ARGS) -{ - PG_RETURN_DATUM( - DirectFunctionCall2( - tsq_mcontains, - PG_GETARG_DATUM(1), - PG_GETARG_DATUM(0) - ) - ); -} - -PG_FUNCTION_INFO_V1(gtsq_in); -Datum gtsq_in(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(gtsq_out); -Datum gtsq_out(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(gtsq_compress); -Datum gtsq_compress(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(gtsq_decompress); -Datum gtsq_decompress(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(gtsq_consistent); -Datum gtsq_consistent(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(gtsq_union); -Datum gtsq_union(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(gtsq_same); -Datum gtsq_same(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(gtsq_penalty); -Datum gtsq_penalty(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(gtsq_picksplit); -Datum gtsq_picksplit(PG_FUNCTION_ARGS); - - -Datum -gtsq_in(PG_FUNCTION_ARGS) -{ - elog(ERROR, "not implemented"); - PG_RETURN_DATUM(0); -} - -Datum -gtsq_out(PG_FUNCTION_ARGS) -{ - elog(ERROR, "not implemented"); - PG_RETURN_DATUM(0); -} - -Datum -gtsq_compress(PG_FUNCTION_ARGS) -{ - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); - GISTENTRY *retval = entry; - - if (entry->leafkey) - { - TPQTGist *sign = (TPQTGist *) palloc(sizeof(TPQTGist)); - - retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); - *sign = makesign((QUERYTYPE *) PG_DETOAST_DATUM(entry->key)); - - gistentryinit(*retval, PointerGetDatum(sign), - entry->rel, entry->page, - entry->offset, FALSE); - } - - PG_RETURN_POINTER(retval); -} - -Datum -gtsq_decompress(PG_FUNCTION_ARGS) -{ - PG_RETURN_DATUM(PG_GETARG_DATUM(0)); -} - -Datum -gtsq_consistent(PG_FUNCTION_ARGS) -{ - GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); - TPQTGist *key = (TPQTGist *) DatumGetPointer(entry->key); - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); - TPQTGist sq = makesign(query); - bool retval; - - switch (strategy) - { - case RTContainsStrategyNumber: - case RTOldContainsStrategyNumber: - if (GIST_LEAF(entry)) - retval = (*key & sq) == sq; - else - retval = (*key & sq) != 0; - break; - case RTContainedByStrategyNumber: - case RTOldContainedByStrategyNumber: - if (GIST_LEAF(entry)) - retval = (*key & sq) == *key; - else - retval = (*key & sq) != 0; - break; - default: - retval = FALSE; - } - PG_RETURN_BOOL(retval); -} - -Datum -gtsq_union(PG_FUNCTION_ARGS) -{ - GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); - TPQTGist *sign = (TPQTGist *) palloc(sizeof(TPQTGist)); - int i; - int *size = (int *) PG_GETARG_POINTER(1); - - memset(sign, 0, sizeof(TPQTGist)); - - for (i = 0; i < entryvec->n; i++) - *sign |= *GETENTRY(entryvec, i); - - *size = sizeof(TPQTGist); - - PG_RETURN_POINTER(sign); -} - -Datum -gtsq_same(PG_FUNCTION_ARGS) -{ - TPQTGist *a = (TPQTGist *) PG_GETARG_POINTER(0); - TPQTGist *b = (TPQTGist *) PG_GETARG_POINTER(1); - bool *result = (bool *) PG_GETARG_POINTER(2); - - *result = (*a == *b) ? true : false; - - PG_RETURN_POINTER(result); -} - -static int -sizebitvec(TPQTGist sign) -{ - int size = 0, - i; - - for (i = 0; i < SIGLEN; i++) - size += 0x01 & (sign >> i); - - return size; -} - -static int -hemdist(TPQTGist a, TPQTGist b) -{ - TPQTGist res = a ^ b; - - return sizebitvec(res); -} - -Datum -gtsq_penalty(PG_FUNCTION_ARGS) -{ - TPQTGist *origval = (TPQTGist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key); - TPQTGist *newval = (TPQTGist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key); - float *penalty = (float *) PG_GETARG_POINTER(2); - - *penalty = hemdist(*origval, *newval); - - PG_RETURN_POINTER(penalty); -} - - -typedef struct -{ - OffsetNumber pos; - int4 cost; -} SPLITCOST; - -static int -comparecost(const void *a, const void *b) -{ - if (((SPLITCOST *) a)->cost == ((SPLITCOST *) b)->cost) - return 0; - else - return (((SPLITCOST *) a)->cost > ((SPLITCOST *) b)->cost) ? 1 : -1; -} - -#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) ) - -Datum -gtsq_picksplit(PG_FUNCTION_ARGS) -{ - GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); - GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); - OffsetNumber maxoff = entryvec->n - 2; - OffsetNumber k, - j; - - TPQTGist *datum_l, - *datum_r; - int4 size_alpha, - size_beta; - int4 size_waste, - waste = -1; - int4 nbytes; - OffsetNumber seed_1 = 0, - seed_2 = 0; - OffsetNumber *left, - *right; - - SPLITCOST *costvector; - - nbytes = (maxoff + 2) * sizeof(OffsetNumber); - left = v->spl_left = (OffsetNumber *) palloc(nbytes); - right = v->spl_right = (OffsetNumber *) palloc(nbytes); - v->spl_nleft = v->spl_nright = 0; - - for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k)) - for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j)) - { - size_waste = hemdist(*GETENTRY(entryvec, j), *GETENTRY(entryvec, k)); - if (size_waste > waste) - { - waste = size_waste; - seed_1 = k; - seed_2 = j; - } - } - - - if (seed_1 == 0 || seed_2 == 0) - { - seed_1 = 1; - seed_2 = 2; - } - - datum_l = (TPQTGist *) palloc(sizeof(TPQTGist)); - *datum_l = *GETENTRY(entryvec, seed_1); - datum_r = (TPQTGist *) palloc(sizeof(TPQTGist)); - *datum_r = *GETENTRY(entryvec, seed_2); - - - maxoff = OffsetNumberNext(maxoff); - costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff); - for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) - { - costvector[j - 1].pos = j; - size_alpha = hemdist(*GETENTRY(entryvec, seed_1), *GETENTRY(entryvec, j)); - size_beta = hemdist(*GETENTRY(entryvec, seed_2), *GETENTRY(entryvec, j)); - costvector[j - 1].cost = abs(size_alpha - size_beta); - } - qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost); - - for (k = 0; k < maxoff; k++) - { - j = costvector[k].pos; - if (j == seed_1) - { - *left++ = j; - v->spl_nleft++; - continue; - } - else if (j == seed_2) - { - *right++ = j; - v->spl_nright++; - continue; - } - size_alpha = hemdist(*datum_l, *GETENTRY(entryvec, j)); - size_beta = hemdist(*datum_r, *GETENTRY(entryvec, j)); - - if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.05)) - { - *datum_l |= *GETENTRY(entryvec, j); - *left++ = j; - v->spl_nleft++; - } - else - { - *datum_r |= *GETENTRY(entryvec, j); - *right++ = j; - v->spl_nright++; - } - } - - *right = *left = FirstOffsetNumber; - v->spl_ldatum = PointerGetDatum(datum_l); - v->spl_rdatum = PointerGetDatum(datum_r); - - PG_RETURN_POINTER(v); -} diff --git a/contrib/tsearch2/query_rewrite.c b/contrib/tsearch2/query_rewrite.c deleted file mode 100644 index 27f1be55bfc4..000000000000 --- a/contrib/tsearch2/query_rewrite.c +++ /dev/null @@ -1,549 +0,0 @@ -#include "postgres.h" -#include "executor/spi.h" - -#include "query_util.h" - -MemoryContext AggregateContext = NULL; - -static int -addone(int *counters, int last, int total) -{ - counters[last]++; - if (counters[last] >= total) - { - if (last == 0) - return 0; - if (addone(counters, last - 1, total - 1) == 0) - return 0; - counters[last] = counters[last - 1] + 1; - } - return 1; -} - -static QTNode * -findeq(QTNode * node, QTNode * ex, MemoryType memtype, QTNode * subs, bool *isfind) -{ - - if ((node->sign & ex->sign) != ex->sign || node->valnode->type != ex->valnode->type || node->valnode->val != ex->valnode->val) - return node; - - if (node->flags & QTN_NOCHANGE) - return node; - - if (node->valnode->type == OPR) - { - if (node->nchild == ex->nchild) - { - if (QTNEq(node, ex)) - { - QTNFree(node); - if (subs) - { - node = QTNCopy(subs, memtype); - node->flags |= QTN_NOCHANGE; - } - else - node = NULL; - *isfind = true; - } - } - else if (node->nchild > ex->nchild) - { - int *counters = (int *) palloc(sizeof(int) * node->nchild); - int i; - QTNode *tnode = (QTNode *) MEMALLOC(memtype, sizeof(QTNode)); - - memset(tnode, 0, sizeof(QTNode)); - tnode->child = (QTNode **) MEMALLOC(memtype, sizeof(QTNode *) * ex->nchild); - tnode->nchild = ex->nchild; - tnode->valnode = (ITEM *) MEMALLOC(memtype, sizeof(ITEM)); - *(tnode->valnode) = *(ex->valnode); - - for (i = 0; i < ex->nchild; i++) - counters[i] = i; - - do - { - tnode->sign = 0; - for (i = 0; i < ex->nchild; i++) - { - tnode->child[i] = node->child[counters[i]]; - tnode->sign |= tnode->child[i]->sign; - } - - if (QTNEq(tnode, ex)) - { - int j = 0; - - MEMFREE(memtype, tnode->valnode); - MEMFREE(memtype, tnode->child); - MEMFREE(memtype, tnode); - if (subs) - { - tnode = QTNCopy(subs, memtype); - tnode->flags = QTN_NOCHANGE | QTN_NEEDFREE; - } - else - tnode = NULL; - - node->child[counters[0]] = tnode; - - for (i = 1; i < ex->nchild; i++) - node->child[counters[i]] = NULL; - for (i = 0; i < node->nchild; i++) - { - if (node->child[i]) - { - node->child[j] = node->child[i]; - j++; - } - } - - node->nchild = j; - - *isfind = true; - - break; - } - } while (addone(counters, ex->nchild - 1, node->nchild)); - if (tnode && (tnode->flags & QTN_NOCHANGE) == 0) - { - MEMFREE(memtype, tnode->valnode); - MEMFREE(memtype, tnode->child); - MEMFREE(memtype, tnode); - } - else - QTNSort(node); - pfree(counters); - } - } - else if (QTNEq(node, ex)) - { - QTNFree(node); - if (subs) - { - node = QTNCopy(subs, memtype); - node->flags |= QTN_NOCHANGE; - } - else - { - node = NULL; - } - *isfind = true; - } - - return node; -} - -static QTNode * -dofindsubquery(QTNode * root, QTNode * ex, MemoryType memtype, QTNode * subs, bool *isfind) -{ - root = findeq(root, ex, memtype, subs, isfind); - - if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == OPR) - { - int i; - - for (i = 0; i < root->nchild; i++) - root->child[i] = dofindsubquery(root->child[i], ex, memtype, subs, isfind); - } - - return root; -} - -static QTNode * -dropvoidsubtree(QTNode * root) -{ - - if (!root) - return NULL; - - if (root->valnode->type == OPR) - { - int i, - j = 0; - - for (i = 0; i < root->nchild; i++) - { - if (root->child[i]) - { - root->child[j] = root->child[i]; - j++; - } - } - - root->nchild = j; - - if (root->valnode->val == (int4) '!' && root->nchild == 0) - { - QTNFree(root); - root = NULL; - } - else if (root->nchild == 1) - { - QTNode *nroot = root->child[0]; - - pfree(root); - root = nroot; - } - } - - return root; -} - -static QTNode * -findsubquery(QTNode * root, QTNode * ex, MemoryType memtype, QTNode * subs, bool *isfind) -{ - bool DidFind = false; - - root = dofindsubquery(root, ex, memtype, subs, &DidFind); - - if (!subs && DidFind) - root = dropvoidsubtree(root); - - if (isfind) - *isfind = DidFind; - - return root; -} - -static Oid tsqOid = InvalidOid; -static void -get_tsq_Oid(void) -{ - int ret; - bool isnull; - - if ((ret = SPI_exec("select oid from pg_type where typname='tsquery'", 1)) < 0) - /* internal error */ - elog(ERROR, "SPI_exec to get tsquery oid returns %d", ret); - - if (SPI_processed < 1) - /* internal error */ - elog(ERROR, "there is no tsvector type"); - tsqOid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); - if (tsqOid == InvalidOid) - /* internal error */ - elog(ERROR, "tsquery type has InvalidOid"); -} - - -PG_FUNCTION_INFO_V1(tsquery_rewrite); -PG_FUNCTION_INFO_V1(rewrite_accum); -Datum rewrite_accum(PG_FUNCTION_ARGS); - -Datum -rewrite_accum(PG_FUNCTION_ARGS) -{ - QUERYTYPE *acc = (QUERYTYPE *) PG_GETARG_POINTER(0); - ArrayType *qa = (ArrayType *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)); - QUERYTYPE *q; - QTNode *qex, - *subs = NULL, - *acctree; - bool isfind = false; - Datum *elemsp; - int nelemsp; - - AggregateContext = ((AggState *) fcinfo->context)->aggcontext; - - if (acc == NULL || PG_ARGISNULL(0)) - { - acc = (QUERYTYPE *) MEMALLOC(AggMemory, sizeof(QUERYTYPE)); - SET_VARSIZE(acc, HDRSIZEQT); - acc->size = 0; - } - - if (qa == NULL || PG_ARGISNULL(1)) - { - PG_FREE_IF_COPY(qa, 1); - PG_RETURN_POINTER(acc); - } - - if (ARR_NDIM(qa) != 1) - elog(ERROR, "array must be one-dimensional, not %d dimension", ARR_NDIM(qa)); - - if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) - elog(ERROR, "array should have only three elements"); - - if (tsqOid == InvalidOid) - { - SPI_connect(); - get_tsq_Oid(); - SPI_finish(); - } - - if (ARR_ELEMTYPE(qa) != tsqOid) - elog(ERROR, "array should contain tsquery type"); - - deconstruct_array(qa, tsqOid, -1, false, 'i', &elemsp, NULL, &nelemsp); - - q = (QUERYTYPE *) DatumGetPointer(elemsp[0]); - if (q->size == 0) - { - pfree(elemsp); - PG_RETURN_POINTER(acc); - } - - if (!acc->size) - { - if (VARSIZE(acc) > HDRSIZEQT) - { - pfree(elemsp); - PG_RETURN_POINTER(acc); - } - else - acctree = QT2QTN(GETQUERY(q), GETOPERAND(q)); - } - else - acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc)); - - QTNTernary(acctree); - QTNSort(acctree); - - q = (QUERYTYPE *) DatumGetPointer(elemsp[1]); - if (q->size == 0) - { - pfree(elemsp); - PG_RETURN_POINTER(acc); - } - qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); - QTNTernary(qex); - QTNSort(qex); - - q = (QUERYTYPE *) DatumGetPointer(elemsp[2]); - if (q->size) - subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); - - acctree = findsubquery(acctree, qex, PlainMemory, subs, &isfind); - - if (isfind || !acc->size) - { - /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ - if (acctree) - { - QTNBinary(acctree); - acc = QTN2QT(acctree, AggMemory); - } - else - { - acc = (QUERYTYPE *) MEMALLOC(AggMemory, HDRSIZEQT * 2); - SET_VARSIZE(acc, HDRSIZEQT * 2); - acc->size = 0; - } - } - - pfree(elemsp); - QTNFree(qex); - QTNFree(subs); - QTNFree(acctree); - - PG_RETURN_POINTER(acc); -} - -PG_FUNCTION_INFO_V1(rewrite_finish); -Datum rewrite_finish(PG_FUNCTION_ARGS); - -Datum -rewrite_finish(PG_FUNCTION_ARGS) -{ - QUERYTYPE *acc = (QUERYTYPE *) PG_GETARG_POINTER(0); - QUERYTYPE *rewrited; - - if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0) - { - acc = (QUERYTYPE *) palloc(sizeof(QUERYTYPE)); - SET_VARSIZE(acc, HDRSIZEQT); - acc->size = 0; - } - - rewrited = (QUERYTYPE *) palloc(VARSIZE(acc)); - memcpy(rewrited, acc, VARSIZE(acc)); - pfree(acc); - - PG_RETURN_POINTER(rewrited); -} - -Datum tsquery_rewrite(PG_FUNCTION_ARGS); - -Datum -tsquery_rewrite(PG_FUNCTION_ARGS) -{ - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); - text *in = PG_GETARG_TEXT_P(1); - QUERYTYPE *rewrited = query; - QTNode *tree; - char *buf; - void *plan; - Portal portal; - bool isnull; - int i; - - if (query->size == 0) - { - PG_FREE_IF_COPY(in, 1); - PG_RETURN_POINTER(rewrited); - } - - tree = QT2QTN(GETQUERY(query), GETOPERAND(query)); - QTNTernary(tree); - QTNSort(tree); - - buf = (char *) palloc(VARSIZE(in)); - memcpy(buf, VARDATA(in), VARSIZE(in) - VARHDRSZ); - buf[VARSIZE(in) - VARHDRSZ] = '\0'; - - SPI_connect(); - - if (tsqOid == InvalidOid) - get_tsq_Oid(); - - if ((plan = SPI_prepare(buf, 0, NULL)) == NULL) - elog(ERROR, "SPI_prepare('%s') returns NULL", buf); - - if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, false)) == NULL) - elog(ERROR, "SPI_cursor_open('%s') returns NULL", buf); - - SPI_cursor_fetch(portal, true, 100); - - if (SPI_tuptable->tupdesc->natts != 2) - elog(ERROR, "number of fields doesn't equal to 2"); - - if (SPI_gettypeid(SPI_tuptable->tupdesc, 1) != tsqOid) - elog(ERROR, "column #1 isn't of tsquery type"); - - if (SPI_gettypeid(SPI_tuptable->tupdesc, 2) != tsqOid) - elog(ERROR, "column #2 isn't of tsquery type"); - - while (SPI_processed > 0 && tree) - { - for (i = 0; i < SPI_processed && tree; i++) - { - Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull); - Datum sdata; - - if (isnull) - continue; - - sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull); - - if (!isnull) - { - QUERYTYPE *qtex = (QUERYTYPE *) PG_DETOAST_DATUM(qdata); - QUERYTYPE *qtsubs = (QUERYTYPE *) PG_DETOAST_DATUM(sdata); - QTNode *qex, - *qsubs = NULL; - - if (qtex->size == 0) - { - if (qtex != (QUERYTYPE *) DatumGetPointer(qdata)) - pfree(qtex); - if (qtsubs != (QUERYTYPE *) DatumGetPointer(sdata)) - pfree(qtsubs); - continue; - } - - qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex)); - - QTNTernary(qex); - QTNSort(qex); - - if (qtsubs->size) - qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs)); - - tree = findsubquery(tree, qex, SPIMemory, qsubs, NULL); - - QTNFree(qex); - if (qtex != (QUERYTYPE *) DatumGetPointer(qdata)) - pfree(qtex); - QTNFree(qsubs); - if (qtsubs != (QUERYTYPE *) DatumGetPointer(sdata)) - pfree(qtsubs); - } - } - - SPI_freetuptable(SPI_tuptable); - SPI_cursor_fetch(portal, true, 100); - } - - SPI_freetuptable(SPI_tuptable); - SPI_cursor_close(portal); - SPI_freeplan(plan); - SPI_finish(); - - - if (tree) - { - QTNBinary(tree); - rewrited = QTN2QT(tree, PlainMemory); - QTNFree(tree); - PG_FREE_IF_COPY(query, 0); - } - else - { - SET_VARSIZE(rewrited, HDRSIZEQT); - rewrited->size = 0; - } - - pfree(buf); - PG_FREE_IF_COPY(in, 1); - PG_RETURN_POINTER(rewrited); -} - - -PG_FUNCTION_INFO_V1(tsquery_rewrite_query); -Datum tsquery_rewrite_query(PG_FUNCTION_ARGS); - -Datum -tsquery_rewrite_query(PG_FUNCTION_ARGS) -{ - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); - QUERYTYPE *ex = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - QUERYTYPE *subst = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(2)); - QUERYTYPE *rewrited = query; - QTNode *tree, - *qex, - *subs = NULL; - - if (query->size == 0 || ex->size == 0) - { - PG_FREE_IF_COPY(ex, 1); - PG_FREE_IF_COPY(subst, 2); - PG_RETURN_POINTER(rewrited); - } - - tree = QT2QTN(GETQUERY(query), GETOPERAND(query)); - QTNTernary(tree); - QTNSort(tree); - - qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex)); - QTNTernary(qex); - QTNSort(qex); - - if (subst->size) - subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst)); - - tree = findsubquery(tree, qex, PlainMemory, subs, NULL); - QTNFree(qex); - QTNFree(subs); - - if (!tree) - { - SET_VARSIZE(rewrited, HDRSIZEQT); - rewrited->size = 0; - PG_FREE_IF_COPY(ex, 1); - PG_FREE_IF_COPY(subst, 2); - PG_RETURN_POINTER(rewrited); - } - else - { - QTNBinary(tree); - rewrited = QTN2QT(tree, PlainMemory); - QTNFree(tree); - } - - PG_FREE_IF_COPY(query, 0); - PG_FREE_IF_COPY(ex, 1); - PG_FREE_IF_COPY(subst, 2); - PG_RETURN_POINTER(rewrited); -} diff --git a/contrib/tsearch2/query_support.c b/contrib/tsearch2/query_support.c deleted file mode 100644 index a4ca8728ac8e..000000000000 --- a/contrib/tsearch2/query_support.c +++ /dev/null @@ -1,205 +0,0 @@ -#include "postgres.h" -#include "fmgr.h" - -#include "query_util.h" - -PG_FUNCTION_INFO_V1(tsquery_numnode); -Datum tsquery_numnode(PG_FUNCTION_ARGS); - -Datum -tsquery_numnode(PG_FUNCTION_ARGS) -{ - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); - int nnode = query->size; - - PG_FREE_IF_COPY(query, 0); - PG_RETURN_INT32(nnode); -} - -static QTNode * -join_tsqueries(QUERYTYPE * a, QUERYTYPE * b) -{ - QTNode *res = (QTNode *) palloc0(sizeof(QTNode)); - - res->flags |= QTN_NEEDFREE; - - res->valnode = (ITEM *) palloc0(sizeof(ITEM)); - res->valnode->type = OPR; - - res->child = (QTNode **) palloc0(sizeof(QTNode *) * 2); - res->child[0] = QT2QTN(GETQUERY(b), GETOPERAND(b)); - res->child[1] = QT2QTN(GETQUERY(a), GETOPERAND(a)); - res->nchild = 2; - - return res; -} - -PG_FUNCTION_INFO_V1(tsquery_and); -Datum tsquery_and(PG_FUNCTION_ARGS); - -Datum -tsquery_and(PG_FUNCTION_ARGS) -{ - QUERYTYPE *a = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); - QUERYTYPE *b = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)); - QTNode *res; - QUERYTYPE *query; - - if (a->size == 0) - { - PG_FREE_IF_COPY(a, 1); - PG_RETURN_POINTER(b); - } - else if (b->size == 0) - { - PG_FREE_IF_COPY(b, 1); - PG_RETURN_POINTER(a); - } - - res = join_tsqueries(a, b); - - res->valnode->val = '&'; - - query = QTN2QT(res, PlainMemory); - - QTNFree(res); - PG_FREE_IF_COPY(a, 0); - PG_FREE_IF_COPY(b, 1); - - PG_RETURN_POINTER(query); -} - -PG_FUNCTION_INFO_V1(tsquery_or); -Datum tsquery_or(PG_FUNCTION_ARGS); - -Datum -tsquery_or(PG_FUNCTION_ARGS) -{ - QUERYTYPE *a = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); - QUERYTYPE *b = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)); - QTNode *res; - QUERYTYPE *query; - - if (a->size == 0) - { - PG_FREE_IF_COPY(a, 1); - PG_RETURN_POINTER(b); - } - else if (b->size == 0) - { - PG_FREE_IF_COPY(b, 1); - PG_RETURN_POINTER(a); - } - - res = join_tsqueries(a, b); - - res->valnode->val = '|'; - - query = QTN2QT(res, PlainMemory); - - QTNFree(res); - PG_FREE_IF_COPY(a, 0); - PG_FREE_IF_COPY(b, 1); - - PG_RETURN_POINTER(query); -} - -PG_FUNCTION_INFO_V1(tsquery_not); -Datum tsquery_not(PG_FUNCTION_ARGS); - -Datum -tsquery_not(PG_FUNCTION_ARGS) -{ - QUERYTYPE *a = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); - QTNode *res; - QUERYTYPE *query; - - if (a->size == 0) - PG_RETURN_POINTER(a); - - res = (QTNode *) palloc0(sizeof(QTNode)); - - res->flags |= QTN_NEEDFREE; - - res->valnode = (ITEM *) palloc0(sizeof(ITEM)); - res->valnode->type = OPR; - res->valnode->val = '!'; - - res->child = (QTNode **) palloc0(sizeof(QTNode *)); - res->child[0] = QT2QTN(GETQUERY(a), GETOPERAND(a)); - res->nchild = 1; - - query = QTN2QT(res, PlainMemory); - - QTNFree(res); - PG_FREE_IF_COPY(a, 0); - - PG_RETURN_POINTER(query); -} - -static int -CompareTSQ(QUERYTYPE * a, QUERYTYPE * b) -{ - if (a->size != b->size) - { - return (a->size < b->size) ? -1 : 1; - } - else if (VARSIZE(a) != VARSIZE(b)) - { - return (VARSIZE(a) < VARSIZE(b)) ? -1 : 1; - } - else - { - QTNode *an = QT2QTN(GETQUERY(a), GETOPERAND(a)); - QTNode *bn = QT2QTN(GETQUERY(b), GETOPERAND(b)); - int res = QTNodeCompare(an, bn); - - QTNFree(an); - QTNFree(bn); - - return res; - } - - return 0; -} - -PG_FUNCTION_INFO_V1(tsquery_cmp); -\ -Datum tsquery_cmp(PG_FUNCTION_ARGS); - -Datum -tsquery_cmp(PG_FUNCTION_ARGS) -{ - QUERYTYPE *a = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); - QUERYTYPE *b = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)); - int res = CompareTSQ(a, b); - - PG_FREE_IF_COPY(a, 0); - PG_FREE_IF_COPY(b, 1); - - PG_RETURN_INT32(res); -} - -#define CMPFUNC( NAME, ACTION ) \ -Datum NAME(PG_FUNCTION_ARGS); \ - \ -Datum \ -NAME(PG_FUNCTION_ARGS) { \ - QUERYTYPE *a = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); \ - QUERYTYPE *b = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)); \ - int res = CompareTSQ(a,b); \ - \ - PG_FREE_IF_COPY(a,0); \ - PG_FREE_IF_COPY(b,1); \ - \ - PG_RETURN_BOOL( ACTION ); \ -} \ - \ -PG_FUNCTION_INFO_V1(NAME) - -CMPFUNC(tsquery_lt, res < 0); -CMPFUNC(tsquery_le, res <= 0); -CMPFUNC(tsquery_eq, res == 0); -CMPFUNC(tsquery_ge, res >= 0); -CMPFUNC(tsquery_gt, res > 0); -CMPFUNC(tsquery_ne, res != 0); diff --git a/contrib/tsearch2/query_util.c b/contrib/tsearch2/query_util.c deleted file mode 100644 index 083d173d1320..000000000000 --- a/contrib/tsearch2/query_util.c +++ /dev/null @@ -1,301 +0,0 @@ -#include "postgres.h" -#include "executor/spi.h" -#include "query_util.h" - -QTNode * -QT2QTN(ITEM * in, char *operand) -{ - QTNode *node = (QTNode *) palloc0(sizeof(QTNode)); - - node->valnode = in; - - if (in->type == OPR) - { - node->child = (QTNode **) palloc0(sizeof(QTNode *) * 2); - node->child[0] = QT2QTN(in + 1, operand); - node->sign = node->child[0]->sign; - if (in->val == (int4) '!') - node->nchild = 1; - else - { - node->nchild = 2; - node->child[1] = QT2QTN(in + in->left, operand); - node->sign |= node->child[1]->sign; - } - } - else if (operand) - { - node->word = operand + in->distance; - node->sign = 1 << (in->val % 32); - } - - return node; -} - -void -QTNFree(QTNode * in) -{ - if (!in) - return; - - if (in->valnode->type == VAL && in->word && (in->flags & QTN_WORDFREE) != 0) - pfree(in->word); - - if (in->child) - { - if (in->valnode) - { - if (in->valnode->type == OPR && in->nchild > 0) - { - int i; - - for (i = 0; i < in->nchild; i++) - QTNFree(in->child[i]); - } - if (in->flags & QTN_NEEDFREE) - pfree(in->valnode); - } - pfree(in->child); - } - - pfree(in); -} - -int -QTNodeCompare(QTNode * an, QTNode * bn) -{ - if (an->valnode->type != bn->valnode->type) - return (an->valnode->type > bn->valnode->type) ? -1 : 1; - else if (an->valnode->val != bn->valnode->val) - return (an->valnode->val > bn->valnode->val) ? -1 : 1; - else if (an->valnode->type == VAL) - { - if (an->valnode->length == bn->valnode->length) - return strncmp(an->word, bn->word, an->valnode->length); - else - return (an->valnode->length > bn->valnode->length) ? -1 : 1; - } - else if (an->nchild != bn->nchild) - { - return (an->nchild > bn->nchild) ? -1 : 1; - } - else - { - int i, - res; - - for (i = 0; i < an->nchild; i++) - if ((res = QTNodeCompare(an->child[i], bn->child[i])) != 0) - return res; - } - - return 0; -} - -static int -cmpQTN(const void *a, const void *b) -{ - return QTNodeCompare(*(QTNode **) a, *(QTNode **) b); -} - -void -QTNSort(QTNode * in) -{ - int i; - - if (in->valnode->type != OPR) - return; - - for (i = 0; i < in->nchild; i++) - QTNSort(in->child[i]); - if (in->nchild > 1) - qsort((void *) in->child, in->nchild, sizeof(QTNode *), cmpQTN); -} - -bool -QTNEq(QTNode * a, QTNode * b) -{ - uint32 sign = a->sign & b->sign; - - if (!(sign == a->sign && sign == b->sign)) - return 0; - - return (QTNodeCompare(a, b) == 0) ? true : false; -} - -void -QTNTernary(QTNode * in) -{ - int i; - - if (in->valnode->type != OPR) - return; - - for (i = 0; i < in->nchild; i++) - QTNTernary(in->child[i]); - - for (i = 0; i < in->nchild; i++) - { - if (in->valnode->type == in->child[i]->valnode->type && in->valnode->val == in->child[i]->valnode->val) - { - QTNode *cc = in->child[i]; - int oldnchild = in->nchild; - - in->nchild += cc->nchild - 1; - in->child = (QTNode **) repalloc(in->child, in->nchild * sizeof(QTNode *)); - - if (i + 1 != oldnchild) - memmove(in->child + i + cc->nchild, in->child + i + 1, - (oldnchild - i - 1) * sizeof(QTNode *)); - - memcpy(in->child + i, cc->child, cc->nchild * sizeof(QTNode *)); - i += cc->nchild - 1; - - pfree(cc); - } - } -} - -void -QTNBinary(QTNode * in) -{ - int i; - - if (in->valnode->type != OPR) - return; - - for (i = 0; i < in->nchild; i++) - QTNBinary(in->child[i]); - - if (in->nchild <= 2) - return; - - while (in->nchild > 2) - { - QTNode *nn = (QTNode *) palloc0(sizeof(QTNode)); - - nn->valnode = (ITEM *) palloc0(sizeof(ITEM)); - nn->child = (QTNode **) palloc0(sizeof(QTNode *) * 2); - - nn->nchild = 2; - nn->flags = QTN_NEEDFREE; - - nn->child[0] = in->child[0]; - nn->child[1] = in->child[1]; - nn->sign = nn->child[0]->sign | nn->child[1]->sign; - - nn->valnode->type = in->valnode->type; - nn->valnode->val = in->valnode->val; - - in->child[0] = nn; - in->child[1] = in->child[in->nchild - 1]; - in->nchild--; - } -} - -static void -cntsize(QTNode * in, int4 *sumlen, int4 *nnode) -{ - *nnode += 1; - if (in->valnode->type == OPR) - { - int i; - - for (i = 0; i < in->nchild; i++) - cntsize(in->child[i], sumlen, nnode); - } - else - { - *sumlen += in->valnode->length + 1; - } -} - -typedef struct -{ - ITEM *curitem; - char *operand; - char *curoperand; -} QTN2QTState; - -static void -fillQT(QTN2QTState * state, QTNode * in) -{ - *(state->curitem) = *(in->valnode); - - if (in->valnode->type == VAL) - { - memcpy(state->curoperand, in->word, in->valnode->length); - state->curitem->distance = state->curoperand - state->operand; - state->curoperand[in->valnode->length] = '\0'; - state->curoperand += in->valnode->length + 1; - state->curitem++; - } - else - { - ITEM *curitem = state->curitem; - - Assert(in->nchild <= 2); - state->curitem++; - - fillQT(state, in->child[0]); - - if (in->nchild == 2) - { - curitem->left = state->curitem - curitem; - fillQT(state, in->child[1]); - } - } -} - -QUERYTYPE * -QTN2QT(QTNode * in, MemoryType memtype) -{ - QUERYTYPE *out; - int len; - int sumlen = 0, - nnode = 0; - QTN2QTState state; - - cntsize(in, &sumlen, &nnode); - len = COMPUTESIZE(nnode, sumlen); - - out = (QUERYTYPE *) MEMALLOC(memtype, len); - SET_VARSIZE(out, len); - out->size = nnode; - - state.curitem = GETQUERY(out); - state.operand = state.curoperand = GETOPERAND(out); - - fillQT(&state, in); - return out; -} - -QTNode * -QTNCopy(QTNode * in, MemoryType memtype) -{ - QTNode *out = (QTNode *) MEMALLOC(memtype, sizeof(QTNode)); - - *out = *in; - out->valnode = (ITEM *) MEMALLOC(memtype, sizeof(ITEM)); - *(out->valnode) = *(in->valnode); - out->flags |= QTN_NEEDFREE; - - if (in->valnode->type == VAL) - { - out->word = MEMALLOC(memtype, in->valnode->length + 1); - memcpy(out->word, in->word, in->valnode->length); - out->word[in->valnode->length] = '\0'; - out->flags |= QTN_WORDFREE; - } - else - { - int i; - - out->child = (QTNode **) MEMALLOC(memtype, sizeof(QTNode *) * in->nchild); - - for (i = 0; i < in->nchild; i++) - out->child[i] = QTNCopy(in->child[i], memtype); - } - - return out; -} diff --git a/contrib/tsearch2/query_util.h b/contrib/tsearch2/query_util.h deleted file mode 100644 index 5ed98e628ddf..000000000000 --- a/contrib/tsearch2/query_util.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef __QUERY_UTIL_H__ -#define __QUERY_UTIL_H__ - -#include "postgres.h" -#include "utils/memutils.h" - -#include "query.h" -#include "executor/spi.h" - -typedef struct QTNode -{ - ITEM *valnode; - uint32 flags; - int4 nchild; - char *word; - uint32 sign; - struct QTNode **child; -} QTNode; - -#define QTN_NEEDFREE 0x01 -#define QTN_NOCHANGE 0x02 -#define QTN_WORDFREE 0x04 - -typedef enum -{ - PlainMemory, - SPIMemory, - AggMemory -} MemoryType; - -QTNode *QT2QTN(ITEM * in, char *operand); -QUERYTYPE *QTN2QT(QTNode * in, MemoryType memtype); -void QTNFree(QTNode * in); -void QTNSort(QTNode * in); -void QTNTernary(QTNode * in); -void QTNBinary(QTNode * in); -int QTNodeCompare(QTNode * an, QTNode * bn); -QTNode *QTNCopy(QTNode * in, MemoryType memtype); -bool QTNEq(QTNode * a, QTNode * b); - - -extern MemoryContext AggregateContext; - -#define MEMALLOC(us, s) ( ((us)==SPIMemory) ? SPI_palloc(s) : ( ( (us)==PlainMemory ) ? palloc(s) : MemoryContextAlloc(AggregateContext, (s)) ) ) -#define MEMFREE(us, p) ( ((us)==SPIMemory) ? SPI_pfree(p) : pfree(p) ) - -#endif diff --git a/contrib/tsearch2/rank.c b/contrib/tsearch2/rank.c deleted file mode 100644 index 44edb22333be..000000000000 --- a/contrib/tsearch2/rank.c +++ /dev/null @@ -1,924 +0,0 @@ -/* - * Relevation - * Teodor Sigaev - */ -#include "postgres.h" - -#include - -#include "access/gist.h" -#include "access/itup.h" -#include "catalog/namespace.h" -#include "commands/trigger.h" -#include "executor/spi.h" -#include "fmgr.h" -#include "funcapi.h" -#include "nodes/pg_list.h" -#include "storage/bufpage.h" -#include "utils/array.h" -#include "utils/builtins.h" - -#include "tsvector.h" -#include "query.h" -#include "common.h" - -PG_FUNCTION_INFO_V1(rank); -Datum rank(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(rank_def); -Datum rank_def(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(rank_cd); -Datum rank_cd(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(rank_cd_def); -Datum rank_cd_def(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(get_covers); -Datum get_covers(PG_FUNCTION_ARGS); - -static float weights[] = {0.1f, 0.2f, 0.4f, 1.0f}; - -#define wpos(wep) ( w[ WEP_GETWEIGHT(wep) ] ) - -#define RANK_NO_NORM 0x00 -#define RANK_NORM_LOGLENGTH 0x01 -#define RANK_NORM_LENGTH 0x02 -#define RANK_NORM_EXTDIST 0x04 -#define RANK_NORM_UNIQ 0x08 -#define RANK_NORM_LOGUNIQ 0x10 -#define DEF_NORM_METHOD RANK_NO_NORM - -static float calc_rank_or(float *w, tsvector * t, QUERYTYPE * q); -static float calc_rank_and(float *w, tsvector * t, QUERYTYPE * q); - -/* - * Returns a weight of a word collocation - */ -static float4 -word_distance(int4 w) -{ - if (w > 100) - return (float4)1e-30; - - return 1.0 / (1.005 + 0.05 * exp(((float4) w) / 1.5 - 2)); -} - -static int -cnt_length(tsvector * t) -{ - WordEntry *ptr = ARRPTR(t), - *end = (WordEntry *) STRPTR(t); - int len = 0, - clen; - - while (ptr < end) - { - if ((clen = POSDATALEN(t, ptr)) == 0) - len += 1; - else - len += clen; - ptr++; - } - - return len; -} - -static int4 -WordECompareITEM(char *eval, char *qval, WordEntry * ptr, ITEM * item) -{ - if (ptr->len == item->length) - return strncmp( - eval + ptr->pos, - qval + item->distance, - item->length); - - return (ptr->len > item->length) ? 1 : -1; -} - -static WordEntry * -find_wordentry(tsvector * t, QUERYTYPE * q, ITEM * item) -{ - WordEntry *StopLow = ARRPTR(t); - WordEntry *StopHigh = (WordEntry *) STRPTR(t); - WordEntry *StopMiddle; - int difference; - - /* Loop invariant: StopLow <= item < StopHigh */ - - while (StopLow < StopHigh) - { - StopMiddle = StopLow + (StopHigh - StopLow) / 2; - difference = WordECompareITEM(STRPTR(t), GETOPERAND(q), StopMiddle, item); - if (difference == 0) - return StopMiddle; - else if (difference < 0) - StopLow = StopMiddle + 1; - else - StopHigh = StopMiddle; - } - - return NULL; -} - - -static int -compareITEM(const void *a, const void *b, void *arg) -{ - char *operand = (char *) arg; - - if ((*(ITEM **) a)->length == (*(ITEM **) b)->length) - return strncmp(operand + (*(ITEM **) a)->distance, - operand + (*(ITEM **) b)->distance, - (*(ITEM **) b)->length); - - return ((*(ITEM **) a)->length > (*(ITEM **) b)->length) ? 1 : -1; -} - -static ITEM ** -SortAndUniqItems(char *operand, ITEM * item, int *size) -{ - ITEM **res, - **ptr, - **prevptr; - - ptr = res = (ITEM **) palloc(sizeof(ITEM *) * *size); - - while ((*size)--) - { - if (item->type == VAL) - { - *ptr = item; - ptr++; - } - item++; - } - - *size = ptr - res; - if (*size < 2) - return res; - - qsort_arg(res, *size, sizeof(ITEM **), compareITEM, (void *) operand); - - ptr = res + 1; - prevptr = res; - - while (ptr - res < *size) - { - if (compareITEM((void *) ptr, (void *) prevptr, (void *) operand) != 0) - { - prevptr++; - *prevptr = *ptr; - } - ptr++; - } - - *size = prevptr + 1 - res; - return res; -} - -static WordEntryPos POSNULL[] = { - 0, - 0 -}; - -static float -calc_rank_and(float *w, tsvector * t, QUERYTYPE * q) -{ - uint16 **pos; - int i, - k, - l, - p; - WordEntry *entry; - WordEntryPos *post, - *ct; - int4 dimt, - lenct, - dist; - float res = -1.0; - ITEM **item; - int size = q->size; - - item = SortAndUniqItems(GETOPERAND(q), GETQUERY(q), &size); - if (size < 2) - { - pfree(item); - return calc_rank_or(w, t, q); - } - pos = (uint16 **) palloc(sizeof(uint16 *) * q->size); - memset(pos, 0, sizeof(uint16 *) * q->size); - *(uint16 *) POSNULL = lengthof(POSNULL) - 1; - WEP_SETPOS(POSNULL[1], MAXENTRYPOS - 1); - - for (i = 0; i < size; i++) - { - entry = find_wordentry(t, q, item[i]); - if (!entry) - continue; - - if (entry->haspos) - pos[i] = (uint16 *) _POSDATAPTR(t, entry); - else - pos[i] = (uint16 *) POSNULL; - - - dimt = *(uint16 *) (pos[i]); - post = (WordEntryPos *) (pos[i] + 1); - for (k = 0; k < i; k++) - { - if (!pos[k]) - continue; - lenct = *(uint16 *) (pos[k]); - ct = (WordEntryPos *) (pos[k] + 1); - for (l = 0; l < dimt; l++) - { - for (p = 0; p < lenct; p++) - { - dist = Abs((int) WEP_GETPOS(post[l]) - (int) WEP_GETPOS(ct[p])); - if (dist || (dist == 0 && (pos[i] == (uint16 *) POSNULL || pos[k] == (uint16 *) POSNULL))) - { - float curw; - - if (!dist) - dist = MAXENTRYPOS; - curw = sqrt(wpos(post[l]) * wpos(ct[p]) * word_distance(dist)); - res = (res < 0) ? curw : 1.0 - (1.0 - res) * (1.0 - curw); - } - } - } - } - } - pfree(pos); - pfree(item); - return res; -} - -static float -calc_rank_or(float *w, tsvector * t, QUERYTYPE * q) -{ - WordEntry *entry; - WordEntryPos *post; - int4 dimt, - j, - i; - float res = 0.0; - ITEM **item; - int size = q->size; - - *(uint16 *) POSNULL = lengthof(POSNULL) - 1; - item = SortAndUniqItems(GETOPERAND(q), GETQUERY(q), &size); - - for (i = 0; i < size; i++) - { - float resj, - wjm; - int4 jm; - - entry = find_wordentry(t, q, item[i]); - if (!entry) - continue; - - if (entry->haspos) - { - dimt = POSDATALEN(t, entry); - post = POSDATAPTR(t, entry); - } - else - { - dimt = *(uint16 *) POSNULL; - post = POSNULL + 1; - } - - resj = 0.0; - wjm = -1.0; - jm = 0; - for (j = 0; j < dimt; j++) - { - resj = resj + wpos(post[j]) / ((j + 1) * (j + 1)); - if (wpos(post[j]) > wjm) - { - wjm = wpos(post[j]); - jm = j; - } - } -/* - limit (sum(i/i^2),i->inf) = pi^2/6 - resj = sum(wi/i^2),i=1,noccurence, - wi - should be sorted desc, - don't sort for now, just choose maximum weight. This should be corrected - Oleg Bartunov -*/ - res = res + (wjm + resj - wjm / ((jm + 1) * (jm + 1))) / 1.64493406685; - } - if (size > 0) - res = res / size; - pfree(item); - return res; -} - -static float -calc_rank(float *w, tsvector * t, QUERYTYPE * q, int4 method) -{ - ITEM *item = GETQUERY(q); - float res = 0.0; - int len; - - if (!t->size || !q->size) - return 0.0; - - res = (item->type != VAL && item->val == (int4) '&') ? - calc_rank_and(w, t, q) : calc_rank_or(w, t, q); - - if (res < 0) - res = (float)1e-20; - - if ((method & RANK_NORM_LOGLENGTH) && t->size > 0) - res /= log((double) (cnt_length(t) + 1)) / log(2.0); - - if (method & RANK_NORM_LENGTH) - { - len = cnt_length(t); - if (len > 0) - res /= (float) len; - } - - if ((method & RANK_NORM_UNIQ) && t->size > 0) - res /= (float) (t->size); - - if ((method & RANK_NORM_LOGUNIQ) && t->size > 0) - res /= log((double) (t->size + 1)) / log(2.0); - - return res; -} - -Datum -rank(PG_FUNCTION_ARGS) -{ - ArrayType *win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(2)); - int method = DEF_NORM_METHOD; - float res = 0.0; - float ws[lengthof(weights)]; - float4 *arrdata; - int i; - - if (ARR_NDIM(win) != 1) - ereport(ERROR, - (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), - errmsg("array of weight must be one-dimensional"))); - - if (ARRNELEMS(win) < lengthof(weights)) - ereport(ERROR, - (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), - errmsg("array of weight is too short"))); - - if (ARR_HASNULL(win)) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("array of weight must not contain nulls"))); - - arrdata = (float4 *) ARR_DATA_PTR(win); - for (i = 0; i < lengthof(weights); i++) - { - ws[i] = (arrdata[i] >= 0) ? arrdata[i] : weights[i]; - if (ws[i] > 1.0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("weight out of range"))); - } - - if (PG_NARGS() == 4) - method = PG_GETARG_INT32(3); - - res = calc_rank(ws, txt, query, method); - - PG_FREE_IF_COPY(win, 0); - PG_FREE_IF_COPY(txt, 1); - PG_FREE_IF_COPY(query, 2); - PG_RETURN_FLOAT4(res); -} - -Datum -rank_def(PG_FUNCTION_ARGS) -{ - tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - float res = 0.0; - int method = DEF_NORM_METHOD; - - if (PG_NARGS() == 3) - method = PG_GETARG_INT32(2); - - res = calc_rank(weights, txt, query, method); - - PG_FREE_IF_COPY(txt, 0); - PG_FREE_IF_COPY(query, 1); - PG_RETURN_FLOAT4(res); -} - - -typedef struct -{ - ITEM **item; - int16 nitem; - bool needfree; - uint8 wclass; - int32 pos; -} DocRepresentation; - -static int -compareDocR(const void *a, const void *b) -{ - if (((DocRepresentation *) a)->pos == ((DocRepresentation *) b)->pos) - return 0; - return (((DocRepresentation *) a)->pos > ((DocRepresentation *) b)->pos) ? 1 : -1; -} - -static bool -checkcondition_ITEM(void *checkval, ITEM * val) -{ - return (bool) (val->istrue); -} - -static void -reset_istrue_flag(QUERYTYPE * query) -{ - ITEM *item = GETQUERY(query); - int i; - - /* reset istrue flag */ - for (i = 0; i < query->size; i++) - { - if (item->type == VAL) - item->istrue = 0; - item++; - } -} - -typedef struct -{ - int pos; - int p; - int q; - DocRepresentation *begin; - DocRepresentation *end; -} Extention; - - -static bool -Cover(DocRepresentation * doc, int len, QUERYTYPE * query, Extention * ext) -{ - DocRepresentation *ptr; - int lastpos = ext->pos; - int i; - bool found = false; - - reset_istrue_flag(query); - - ext->p = 0x7fffffff; - ext->q = 0; - ptr = doc + ext->pos; - - /* find upper bound of cover from current position, move up */ - while (ptr - doc < len) - { - for (i = 0; i < ptr->nitem; i++) - ptr->item[i]->istrue = 1; - if (TS_execute(GETQUERY(query), NULL, false, checkcondition_ITEM)) - { - if (ptr->pos > ext->q) - { - ext->q = ptr->pos; - ext->end = ptr; - lastpos = ptr - doc; - found = true; - } - break; - } - ptr++; - } - - if (!found) - return false; - - reset_istrue_flag(query); - - ptr = doc + lastpos; - - /* find lower bound of cover from founded upper bound, move down */ - while (ptr >= doc + ext->pos) - { - for (i = 0; i < ptr->nitem; i++) - ptr->item[i]->istrue = 1; - if (TS_execute(GETQUERY(query), NULL, true, checkcondition_ITEM)) - { - if (ptr->pos < ext->p) - { - ext->begin = ptr; - ext->p = ptr->pos; - } - break; - } - ptr--; - } - - if (ext->p <= ext->q) - { - /* - * set position for next try to next lexeme after begining of founded - * cover - */ - ext->pos = (ptr - doc) + 1; - return true; - } - - ext->pos++; - return Cover(doc, len, query, ext); -} - -static DocRepresentation * -get_docrep(tsvector * txt, QUERYTYPE * query, int *doclen) -{ - ITEM *item = GETQUERY(query); - WordEntry *entry; - WordEntryPos *post; - int4 dimt, - j, - i; - int len = query->size * 4, - cur = 0; - DocRepresentation *doc; - char *operand; - - *(uint16 *) POSNULL = lengthof(POSNULL) - 1; - doc = (DocRepresentation *) palloc(sizeof(DocRepresentation) * len); - operand = GETOPERAND(query); - reset_istrue_flag(query); - - for (i = 0; i < query->size; i++) - { - if (item[i].type != VAL || item[i].istrue) - continue; - - entry = find_wordentry(txt, query, &(item[i])); - if (!entry) - continue; - - if (entry->haspos) - { - dimt = POSDATALEN(txt, entry); - post = POSDATAPTR(txt, entry); - } - else - { - dimt = *(uint16 *) POSNULL; - post = POSNULL + 1; - } - - while (cur + dimt >= len) - { - len *= 2; - doc = (DocRepresentation *) repalloc(doc, sizeof(DocRepresentation) * len); - } - - for (j = 0; j < dimt; j++) - { - if (j == 0) - { - ITEM *kptr, - *iptr = item + i; - int k; - - doc[cur].needfree = false; - doc[cur].nitem = 0; - doc[cur].item = (ITEM **) palloc(sizeof(ITEM *) * query->size); - - for (k = 0; k < query->size; k++) - { - kptr = item + k; - if (k == i || - (item[k].type == VAL && - compareITEM(&kptr, &iptr, operand) == 0)) - { - doc[cur].item[doc[cur].nitem] = item + k; - doc[cur].nitem++; - kptr->istrue = 1; - } - } - } - else - { - doc[cur].needfree = false; - doc[cur].nitem = doc[cur - 1].nitem; - doc[cur].item = doc[cur - 1].item; - } - doc[cur].pos = WEP_GETPOS(post[j]); - doc[cur].wclass = WEP_GETWEIGHT(post[j]); - cur++; - } - } - - *doclen = cur; - - if (cur > 0) - { - if (cur > 1) - qsort((void *) doc, cur, sizeof(DocRepresentation), compareDocR); - return doc; - } - - pfree(doc); - return NULL; -} - -static float4 -calc_rank_cd(float4 *arrdata, tsvector * txt, QUERYTYPE * query, int method) -{ - DocRepresentation *doc; - int len, - i, - doclen = 0; - Extention ext; - double Wdoc = 0.0; - double invws[lengthof(weights)]; - double SumDist = 0.0, - PrevExtPos = 0.0, - CurExtPos = 0.0; - int NExtent = 0; - - for (i = 0; i < lengthof(weights); i++) - { - invws[i] = ((double) ((arrdata[i] >= 0) ? arrdata[i] : weights[i])); - if (invws[i] > 1.0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("weight out of range"))); - invws[i] = 1.0 / invws[i]; - } - - doc = get_docrep(txt, query, &doclen); - if (!doc) - return 0.0; - - MemSet(&ext, 0, sizeof(Extention)); - while (Cover(doc, doclen, query, &ext)) - { - double Cpos = 0.0; - double InvSum = 0.0; - int nNoise; - DocRepresentation *ptr = ext.begin; - - while (ptr <= ext.end) - { - InvSum += invws[ptr->wclass]; - ptr++; - } - - Cpos = ((double) (ext.end - ext.begin + 1)) / InvSum; - /* - * if doc are big enough then ext.q may be equal to ext.p - * due to limit of posional information. In this case we - * approximate number of noise word as half cover's - * length - */ - nNoise = (ext.q - ext.p) - (ext.end - ext.begin); - if ( nNoise < 0 ) - nNoise = (ext.end - ext.begin) / 2; - Wdoc += Cpos / ((double) (1 + nNoise)); - - CurExtPos = ((double) (ext.q + ext.p)) / 2.0; - if (NExtent > 0 && CurExtPos > PrevExtPos /* prevent devision by - * zero in a case of - multiple lexize */ ) - SumDist += 1.0 / (CurExtPos - PrevExtPos); - - PrevExtPos = CurExtPos; - NExtent++; - } - - if ((method & RANK_NORM_LOGLENGTH) && txt->size > 0) - Wdoc /= log((double) (cnt_length(txt) + 1)); - - if (method & RANK_NORM_LENGTH) - { - len = cnt_length(txt); - if (len > 0) - Wdoc /= (double) len; - } - - if ((method & RANK_NORM_EXTDIST) && SumDist > 0) - Wdoc /= ((double) NExtent) / SumDist; - - if ((method & RANK_NORM_UNIQ) && txt->size > 0) - Wdoc /= (double) (txt->size); - - if ((method & RANK_NORM_LOGUNIQ) && txt->size > 0) - Wdoc /= log((double) (txt->size + 1)) / log(2.0); - - for (i = 0; i < doclen; i++) - if (doc[i].needfree) - pfree(doc[i].item); - pfree(doc); - - return (float4) Wdoc; -} - -Datum -rank_cd(PG_FUNCTION_ARGS) -{ - ArrayType *win; - tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(2)); - int method = DEF_NORM_METHOD; - float4 res; - - /* - * Pre-8.2, rank_cd took just a plain int as its first argument. - * It was a mistake to keep the same C function name while changing the - * signature, but it's too late to fix that. Instead, do a runtime test - * to make sure the expected datatype has been passed. This is needed - * to prevent core dumps if tsearch2 function definitions from an old - * database are loaded into an 8.2 server. - */ - if (get_fn_expr_argtype(fcinfo->flinfo, 0) != FLOAT4ARRAYOID) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("rank_cd() now takes real[] as its first argument, not integer"))); - - /* now safe to dereference the first arg */ - win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - - if (ARR_NDIM(win) != 1) - ereport(ERROR, - (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), - errmsg("array of weight must be one-dimensional"))); - - if (ARRNELEMS(win) < lengthof(weights)) - ereport(ERROR, - (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), - errmsg("array of weight is too short"))); - - if (ARR_HASNULL(win)) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("array of weight must not contain nulls"))); - - if (PG_NARGS() == 4) - method = PG_GETARG_INT32(3); - - res = calc_rank_cd((float4 *) ARR_DATA_PTR(win), txt, query, method); - - PG_FREE_IF_COPY(win, 0); - PG_FREE_IF_COPY(txt, 1); - PG_FREE_IF_COPY(query, 2); - - PG_RETURN_FLOAT4(res); -} - - -Datum -rank_cd_def(PG_FUNCTION_ARGS) -{ - tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)); - float4 res; - - res = calc_rank_cd(weights, txt, query, (PG_NARGS() == 3) ? PG_GETARG_DATUM(2) : DEF_NORM_METHOD); - - PG_FREE_IF_COPY(txt, 0); - PG_FREE_IF_COPY(query, 1); - - PG_RETURN_FLOAT4(res); -} - -/**************debug*************/ - -typedef struct -{ - char *w; - int2 len; - int2 pos; - int2 start; - int2 finish; -} DocWord; - -static int -compareDocWord(const void *a, const void *b) -{ - if (((DocWord *) a)->pos == ((DocWord *) b)->pos) - return 0; - return (((DocWord *) a)->pos > ((DocWord *) b)->pos) ? 1 : -1; -} - - -Datum -get_covers(PG_FUNCTION_ARGS) -{ - tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)); - WordEntry *pptr = ARRPTR(txt); - int i, - dlen = 0, - j, - cur = 0, - len = 0, - rlen; - DocWord *dw, - *dwptr; - text *out; - char *cptr; - DocRepresentation *doc; - int olddwpos = 0; - int ncover = 1; - Extention ext; - - doc = get_docrep(txt, query, &rlen); - - if (!doc) - { - out = palloc(VARHDRSZ); - SET_VARSIZE(out, VARHDRSZ); - PG_FREE_IF_COPY(txt, 0); - PG_FREE_IF_COPY(query, 1); - PG_RETURN_POINTER(out); - } - - for (i = 0; i < txt->size; i++) - { - if (!pptr[i].haspos) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("no pos info"))); - dlen += POSDATALEN(txt, &(pptr[i])); - } - - dwptr = dw = palloc(sizeof(DocWord) * dlen); - memset(dw, 0, sizeof(DocWord) * dlen); - - for (i = 0; i < txt->size; i++) - { - WordEntryPos *posdata = POSDATAPTR(txt, &(pptr[i])); - - for (j = 0; j < POSDATALEN(txt, &(pptr[i])); j++) - { - dw[cur].w = STRPTR(txt) + pptr[i].pos; - dw[cur].len = pptr[i].len; - dw[cur].pos = WEP_GETPOS(posdata[j]); - cur++; - } - len += (pptr[i].len + 1) * (int) POSDATALEN(txt, &(pptr[i])); - } - qsort((void *) dw, dlen, sizeof(DocWord), compareDocWord); - - MemSet(&ext, 0, sizeof(Extention)); - while (Cover(doc, rlen, query, &ext)) - { - dwptr = dw + olddwpos; - while (dwptr->pos < ext.p && dwptr - dw < dlen) - dwptr++; - olddwpos = dwptr - dw; - dwptr->start = ncover; - while (dwptr->pos < ext.q + 1 && dwptr - dw < dlen) - dwptr++; - (dwptr - 1)->finish = ncover; - len += 4 /* {}+two spaces */ + 2 * 16 /* numbers */ ; - ncover++; - } - - out = palloc(VARHDRSZ + len); - cptr = ((char *) out) + VARHDRSZ; - dwptr = dw; - - while (dwptr - dw < dlen) - { - if (dwptr->start) - { - sprintf(cptr, "{%d ", dwptr->start); - cptr = strchr(cptr, '\0'); - } - memcpy(cptr, dwptr->w, dwptr->len); - cptr += dwptr->len; - *cptr = ' '; - cptr++; - if (dwptr->finish) - { - sprintf(cptr, "}%d ", dwptr->finish); - cptr = strchr(cptr, '\0'); - } - dwptr++; - } - - SET_VARSIZE(out, cptr - ((char *) out)); - - pfree(dw); - for (i = 0; i < rlen; i++) - if (doc[i].needfree) - pfree(doc[i].item); - pfree(doc); - - PG_FREE_IF_COPY(txt, 0); - PG_FREE_IF_COPY(query, 1); - PG_RETURN_POINTER(out); -} diff --git a/contrib/tsearch2/snmap.c b/contrib/tsearch2/snmap.c deleted file mode 100644 index 9aa0e2214f6b..000000000000 --- a/contrib/tsearch2/snmap.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * simple but fast map from str to Oid - * Teodor Sigaev - */ -#include "postgres.h" - -#include "snmap.h" -#include "common.h" - -static int -compareSNMapEntry(const void *a, const void *b) -{ - if (((SNMapEntry *) a)->nsp < ((SNMapEntry *) b)->nsp) - return -1; - else if (((SNMapEntry *) a)->nsp > ((SNMapEntry *) b)->nsp) - return 1; - else - return strcmp(((SNMapEntry *) a)->key, ((SNMapEntry *) b)->key); -} - -void -addSNMap(SNMap * map, char *key, Oid value) -{ - if (map->len >= map->reallen) - { - SNMapEntry *tmp; - int len = (map->reallen) ? 2 * map->reallen : 16; - - tmp = (SNMapEntry *) realloc(map->list, sizeof(SNMapEntry) * len); - if (!tmp) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - map->reallen = len; - map->list = tmp; - } - map->list[map->len].key = strdup(key); - if (!map->list[map->len].key) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - map->list[map->len].nsp = get_oidnamespace(TSNSP_FunctionOid); - map->list[map->len].value = value; - map->len++; - if (map->len > 1) - qsort(map->list, map->len, sizeof(SNMapEntry), compareSNMapEntry); -} - -void -addSNMap_t(SNMap * map, text *key, Oid value) -{ - char *k = text2char(key); - - addSNMap(map, k, value); - pfree(k); -} - -Oid -findSNMap(SNMap * map, char *key) -{ - SNMapEntry *ptr; - SNMapEntry ks; - - ks.key = key; - ks.nsp = get_oidnamespace(TSNSP_FunctionOid); - ks.value = 0; - - if (map->len == 0 || !map->list) - return 0; - ptr = (SNMapEntry *) bsearch(&ks, map->list, map->len, sizeof(SNMapEntry), compareSNMapEntry); - return (ptr) ? ptr->value : 0; -} - -Oid -findSNMap_t(SNMap * map, text *key) -{ - char *k = text2char(key); - int res; - - res = findSNMap(map, k); - pfree(k); - return res; -} - -void -freeSNMap(SNMap * map) -{ - SNMapEntry *entry = map->list; - - if (map->list) - { - while (map->len) - { - if (entry->key) - free(entry->key); - entry++; - map->len--; - } - free(map->list); - } - memset(map, 0, sizeof(SNMap)); -} diff --git a/contrib/tsearch2/snmap.h b/contrib/tsearch2/snmap.h deleted file mode 100644 index c3e80fca57c4..000000000000 --- a/contrib/tsearch2/snmap.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __SNMAP_H__ -#define __SNMAP_H__ - -#include "postgres.h" - -typedef struct -{ - char *key; - Oid value; - Oid nsp; -} SNMapEntry; - -typedef struct -{ - int len; - int reallen; - SNMapEntry *list; -} SNMap; - -void addSNMap(SNMap * map, char *key, Oid value); -void addSNMap_t(SNMap * map, text *key, Oid value); -Oid findSNMap(SNMap * map, char *key); -Oid findSNMap_t(SNMap * map, text *key); -void freeSNMap(SNMap * map); - -#endif diff --git a/contrib/tsearch2/snowball/Makefile b/contrib/tsearch2/snowball/Makefile deleted file mode 100644 index aa94658f6578..000000000000 --- a/contrib/tsearch2/snowball/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# $PostgreSQL: pgsql/contrib/tsearch2/snowball/Makefile,v 1.9 2006/01/27 16:32:31 teodor Exp $ - -SUBOBJS = english_stem.o api.o russian_stem.o russian_stem_UTF8.o utilities.o - -EXTRA_CLEAN = SUBSYS.o $(SUBOBJS) - -PG_CPPFLAGS = -I$(srcdir)/.. - -ifdef USE_PGXS -PGXS := $(shell pg_config --pgxs) -include $(PGXS) -else -subdir = contrib/tsearch2/snowball -top_builddir = ../../.. -include $(top_builddir)/src/Makefile.global -include $(top_srcdir)/contrib/contrib-global.mk -endif - -override CFLAGS += $(CFLAGS_SL) - -all: SUBSYS.o - -SUBSYS.o: $(SUBOBJS) - $(LD) $(LDREL) $(LDOUT) $@ $^ - - diff --git a/contrib/tsearch2/snowball/api.c b/contrib/tsearch2/snowball/api.c deleted file mode 100644 index 78e4fe0eefec..000000000000 --- a/contrib/tsearch2/snowball/api.c +++ /dev/null @@ -1,85 +0,0 @@ - -#include /* for calloc, free */ -#include "header.h" - -extern struct SN_env * -SN_create_env(int S_size, int I_size, int B_size) -{ - struct SN_env *z = (struct SN_env *) calloc(1, sizeof(struct SN_env)); - - if (z == NULL) - return NULL; - z->p = create_s(); - if (z->p == NULL) - goto error; - if (S_size) - { - int i; - - z->S = (symbol * *) calloc(S_size, sizeof(symbol *)); - if (z->S == NULL) - goto error; - - for (i = 0; i < S_size; i++) - { - z->S[i] = create_s(); - if (z->S[i] == NULL) - goto error; - } - z->S_size = S_size; - } - - if (I_size) - { - z->I = (int *) calloc(I_size, sizeof(int)); - if (z->I == NULL) - goto error; - z->I_size = I_size; - } - - if (B_size) - { - z->B = (symbol *) calloc(B_size, sizeof(symbol)); - if (z->B == NULL) - goto error; - z->B_size = B_size; - } - - return z; -error: - SN_close_env(z); - return NULL; -} - -extern void -SN_close_env(struct SN_env * z) -{ - if (z == NULL) - return; - if (z->S_size) - { - int i; - - for (i = 0; i < z->S_size; i++) - { - lose_s(z->S[i]); - } - free(z->S); - } - if (z->I_size) - free(z->I); - if (z->B_size) - free(z->B); - if (z->p) - lose_s(z->p); - free(z); -} - -extern int -SN_set_current(struct SN_env * z, int size, const symbol * s) -{ - int err = replace_s(z, 0, z->l, size, s, NULL); - - z->c = 0; - return err; -} diff --git a/contrib/tsearch2/snowball/english_stem.c b/contrib/tsearch2/snowball/english_stem.c deleted file mode 100644 index 9f6f65491c77..000000000000 --- a/contrib/tsearch2/snowball/english_stem.c +++ /dev/null @@ -1,1623 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/snowball/english_stem.c,v 1.8 2006/03/11 04:38:30 momjian Exp $ */ - -/* This file was generated automatically by the Snowball to ANSI C compiler */ - -#include "header.h" - -extern int english_ISO_8859_1_stem(struct SN_env * z); -static int r_exception2(struct SN_env * z); -static int r_exception1(struct SN_env * z); -static int r_Step_5(struct SN_env * z); -static int r_Step_4(struct SN_env * z); -static int r_Step_3(struct SN_env * z); -static int r_Step_2(struct SN_env * z); -static int r_Step_1c(struct SN_env * z); -static int r_Step_1b(struct SN_env * z); -static int r_Step_1a(struct SN_env * z); -static int r_R2(struct SN_env * z); -static int r_R1(struct SN_env * z); -static int r_shortv(struct SN_env * z); -static int r_mark_regions(struct SN_env * z); -static int r_postlude(struct SN_env * z); -static int r_prelude(struct SN_env * z); - -extern struct SN_env *english_ISO_8859_1_create_env(void); -extern void english_ISO_8859_1_close_env(struct SN_env * z); - -static symbol s_0_0[6] = {'c', 'o', 'm', 'm', 'u', 'n'}; -static symbol s_0_1[5] = {'g', 'e', 'n', 'e', 'r'}; - -static struct among a_0[2] = -{ - /* 0 */ {6, s_0_0, -1, -1, 0}, - /* 1 */ {5, s_0_1, -1, -1, 0} -}; - -static symbol s_1_0[1] = {'\''}; -static symbol s_1_1[3] = {'\'', 's', '\''}; -static symbol s_1_2[2] = {'\'', 's'}; - -static struct among a_1[3] = -{ - /* 0 */ {1, s_1_0, -1, 1, 0}, - /* 1 */ {3, s_1_1, 0, 1, 0}, - /* 2 */ {2, s_1_2, -1, 1, 0} -}; - -static symbol s_2_0[3] = {'i', 'e', 'd'}; -static symbol s_2_1[1] = {'s'}; -static symbol s_2_2[3] = {'i', 'e', 's'}; -static symbol s_2_3[4] = {'s', 's', 'e', 's'}; -static symbol s_2_4[2] = {'s', 's'}; -static symbol s_2_5[2] = {'u', 's'}; - -static struct among a_2[6] = -{ - /* 0 */ {3, s_2_0, -1, 2, 0}, - /* 1 */ {1, s_2_1, -1, 3, 0}, - /* 2 */ {3, s_2_2, 1, 2, 0}, - /* 3 */ {4, s_2_3, 1, 1, 0}, - /* 4 */ {2, s_2_4, 1, -1, 0}, - /* 5 */ {2, s_2_5, 1, -1, 0} -}; - -static symbol s_3_1[2] = {'b', 'b'}; -static symbol s_3_2[2] = {'d', 'd'}; -static symbol s_3_3[2] = {'f', 'f'}; -static symbol s_3_4[2] = {'g', 'g'}; -static symbol s_3_5[2] = {'b', 'l'}; -static symbol s_3_6[2] = {'m', 'm'}; -static symbol s_3_7[2] = {'n', 'n'}; -static symbol s_3_8[2] = {'p', 'p'}; -static symbol s_3_9[2] = {'r', 'r'}; -static symbol s_3_10[2] = {'a', 't'}; -static symbol s_3_11[2] = {'t', 't'}; -static symbol s_3_12[2] = {'i', 'z'}; - -static struct among a_3[13] = -{ - /* 0 */ {0, 0, -1, 3, 0}, - /* 1 */ {2, s_3_1, 0, 2, 0}, - /* 2 */ {2, s_3_2, 0, 2, 0}, - /* 3 */ {2, s_3_3, 0, 2, 0}, - /* 4 */ {2, s_3_4, 0, 2, 0}, - /* 5 */ {2, s_3_5, 0, 1, 0}, - /* 6 */ {2, s_3_6, 0, 2, 0}, - /* 7 */ {2, s_3_7, 0, 2, 0}, - /* 8 */ {2, s_3_8, 0, 2, 0}, - /* 9 */ {2, s_3_9, 0, 2, 0}, - /* 10 */ {2, s_3_10, 0, 1, 0}, - /* 11 */ {2, s_3_11, 0, 2, 0}, - /* 12 */ {2, s_3_12, 0, 1, 0} -}; - -static symbol s_4_0[2] = {'e', 'd'}; -static symbol s_4_1[3] = {'e', 'e', 'd'}; -static symbol s_4_2[3] = {'i', 'n', 'g'}; -static symbol s_4_3[4] = {'e', 'd', 'l', 'y'}; -static symbol s_4_4[5] = {'e', 'e', 'd', 'l', 'y'}; -static symbol s_4_5[5] = {'i', 'n', 'g', 'l', 'y'}; - -static struct among a_4[6] = -{ - /* 0 */ {2, s_4_0, -1, 2, 0}, - /* 1 */ {3, s_4_1, 0, 1, 0}, - /* 2 */ {3, s_4_2, -1, 2, 0}, - /* 3 */ {4, s_4_3, -1, 2, 0}, - /* 4 */ {5, s_4_4, 3, 1, 0}, - /* 5 */ {5, s_4_5, -1, 2, 0} -}; - -static symbol s_5_0[4] = {'a', 'n', 'c', 'i'}; -static symbol s_5_1[4] = {'e', 'n', 'c', 'i'}; -static symbol s_5_2[3] = {'o', 'g', 'i'}; -static symbol s_5_3[2] = {'l', 'i'}; -static symbol s_5_4[3] = {'b', 'l', 'i'}; -static symbol s_5_5[4] = {'a', 'b', 'l', 'i'}; -static symbol s_5_6[4] = {'a', 'l', 'l', 'i'}; -static symbol s_5_7[5] = {'f', 'u', 'l', 'l', 'i'}; -static symbol s_5_8[6] = {'l', 'e', 's', 's', 'l', 'i'}; -static symbol s_5_9[5] = {'o', 'u', 's', 'l', 'i'}; -static symbol s_5_10[5] = {'e', 'n', 't', 'l', 'i'}; -static symbol s_5_11[5] = {'a', 'l', 'i', 't', 'i'}; -static symbol s_5_12[6] = {'b', 'i', 'l', 'i', 't', 'i'}; -static symbol s_5_13[5] = {'i', 'v', 'i', 't', 'i'}; -static symbol s_5_14[6] = {'t', 'i', 'o', 'n', 'a', 'l'}; -static symbol s_5_15[7] = {'a', 't', 'i', 'o', 'n', 'a', 'l'}; -static symbol s_5_16[5] = {'a', 'l', 'i', 's', 'm'}; -static symbol s_5_17[5] = {'a', 't', 'i', 'o', 'n'}; -static symbol s_5_18[7] = {'i', 'z', 'a', 't', 'i', 'o', 'n'}; -static symbol s_5_19[4] = {'i', 'z', 'e', 'r'}; -static symbol s_5_20[4] = {'a', 't', 'o', 'r'}; -static symbol s_5_21[7] = {'i', 'v', 'e', 'n', 'e', 's', 's'}; -static symbol s_5_22[7] = {'f', 'u', 'l', 'n', 'e', 's', 's'}; -static symbol s_5_23[7] = {'o', 'u', 's', 'n', 'e', 's', 's'}; - -static struct among a_5[24] = -{ - /* 0 */ {4, s_5_0, -1, 3, 0}, - /* 1 */ {4, s_5_1, -1, 2, 0}, - /* 2 */ {3, s_5_2, -1, 13, 0}, - /* 3 */ {2, s_5_3, -1, 16, 0}, - /* 4 */ {3, s_5_4, 3, 12, 0}, - /* 5 */ {4, s_5_5, 4, 4, 0}, - /* 6 */ {4, s_5_6, 3, 8, 0}, - /* 7 */ {5, s_5_7, 3, 14, 0}, - /* 8 */ {6, s_5_8, 3, 15, 0}, - /* 9 */ {5, s_5_9, 3, 10, 0}, - /* 10 */ {5, s_5_10, 3, 5, 0}, - /* 11 */ {5, s_5_11, -1, 8, 0}, - /* 12 */ {6, s_5_12, -1, 12, 0}, - /* 13 */ {5, s_5_13, -1, 11, 0}, - /* 14 */ {6, s_5_14, -1, 1, 0}, - /* 15 */ {7, s_5_15, 14, 7, 0}, - /* 16 */ {5, s_5_16, -1, 8, 0}, - /* 17 */ {5, s_5_17, -1, 7, 0}, - /* 18 */ {7, s_5_18, 17, 6, 0}, - /* 19 */ {4, s_5_19, -1, 6, 0}, - /* 20 */ {4, s_5_20, -1, 7, 0}, - /* 21 */ {7, s_5_21, -1, 11, 0}, - /* 22 */ {7, s_5_22, -1, 9, 0}, - /* 23 */ {7, s_5_23, -1, 10, 0} -}; - -static symbol s_6_0[5] = {'i', 'c', 'a', 't', 'e'}; -static symbol s_6_1[5] = {'a', 't', 'i', 'v', 'e'}; -static symbol s_6_2[5] = {'a', 'l', 'i', 'z', 'e'}; -static symbol s_6_3[5] = {'i', 'c', 'i', 't', 'i'}; -static symbol s_6_4[4] = {'i', 'c', 'a', 'l'}; -static symbol s_6_5[6] = {'t', 'i', 'o', 'n', 'a', 'l'}; -static symbol s_6_6[7] = {'a', 't', 'i', 'o', 'n', 'a', 'l'}; -static symbol s_6_7[3] = {'f', 'u', 'l'}; -static symbol s_6_8[4] = {'n', 'e', 's', 's'}; - -static struct among a_6[9] = -{ - /* 0 */ {5, s_6_0, -1, 4, 0}, - /* 1 */ {5, s_6_1, -1, 6, 0}, - /* 2 */ {5, s_6_2, -1, 3, 0}, - /* 3 */ {5, s_6_3, -1, 4, 0}, - /* 4 */ {4, s_6_4, -1, 4, 0}, - /* 5 */ {6, s_6_5, -1, 1, 0}, - /* 6 */ {7, s_6_6, 5, 2, 0}, - /* 7 */ {3, s_6_7, -1, 5, 0}, - /* 8 */ {4, s_6_8, -1, 5, 0} -}; - -static symbol s_7_0[2] = {'i', 'c'}; -static symbol s_7_1[4] = {'a', 'n', 'c', 'e'}; -static symbol s_7_2[4] = {'e', 'n', 'c', 'e'}; -static symbol s_7_3[4] = {'a', 'b', 'l', 'e'}; -static symbol s_7_4[4] = {'i', 'b', 'l', 'e'}; -static symbol s_7_5[3] = {'a', 't', 'e'}; -static symbol s_7_6[3] = {'i', 'v', 'e'}; -static symbol s_7_7[3] = {'i', 'z', 'e'}; -static symbol s_7_8[3] = {'i', 't', 'i'}; -static symbol s_7_9[2] = {'a', 'l'}; -static symbol s_7_10[3] = {'i', 's', 'm'}; -static symbol s_7_11[3] = {'i', 'o', 'n'}; -static symbol s_7_12[2] = {'e', 'r'}; -static symbol s_7_13[3] = {'o', 'u', 's'}; -static symbol s_7_14[3] = {'a', 'n', 't'}; -static symbol s_7_15[3] = {'e', 'n', 't'}; -static symbol s_7_16[4] = {'m', 'e', 'n', 't'}; -static symbol s_7_17[5] = {'e', 'm', 'e', 'n', 't'}; - -static struct among a_7[18] = -{ - /* 0 */ {2, s_7_0, -1, 1, 0}, - /* 1 */ {4, s_7_1, -1, 1, 0}, - /* 2 */ {4, s_7_2, -1, 1, 0}, - /* 3 */ {4, s_7_3, -1, 1, 0}, - /* 4 */ {4, s_7_4, -1, 1, 0}, - /* 5 */ {3, s_7_5, -1, 1, 0}, - /* 6 */ {3, s_7_6, -1, 1, 0}, - /* 7 */ {3, s_7_7, -1, 1, 0}, - /* 8 */ {3, s_7_8, -1, 1, 0}, - /* 9 */ {2, s_7_9, -1, 1, 0}, - /* 10 */ {3, s_7_10, -1, 1, 0}, - /* 11 */ {3, s_7_11, -1, 2, 0}, - /* 12 */ {2, s_7_12, -1, 1, 0}, - /* 13 */ {3, s_7_13, -1, 1, 0}, - /* 14 */ {3, s_7_14, -1, 1, 0}, - /* 15 */ {3, s_7_15, -1, 1, 0}, - /* 16 */ {4, s_7_16, 15, 1, 0}, - /* 17 */ {5, s_7_17, 16, 1, 0} -}; - -static symbol s_8_0[1] = {'e'}; -static symbol s_8_1[1] = {'l'}; - -static struct among a_8[2] = -{ - /* 0 */ {1, s_8_0, -1, 1, 0}, - /* 1 */ {1, s_8_1, -1, 2, 0} -}; - -static symbol s_9_0[7] = {'s', 'u', 'c', 'c', 'e', 'e', 'd'}; -static symbol s_9_1[7] = {'p', 'r', 'o', 'c', 'e', 'e', 'd'}; -static symbol s_9_2[6] = {'e', 'x', 'c', 'e', 'e', 'd'}; -static symbol s_9_3[7] = {'c', 'a', 'n', 'n', 'i', 'n', 'g'}; -static symbol s_9_4[6] = {'i', 'n', 'n', 'i', 'n', 'g'}; -static symbol s_9_5[7] = {'e', 'a', 'r', 'r', 'i', 'n', 'g'}; -static symbol s_9_6[7] = {'h', 'e', 'r', 'r', 'i', 'n', 'g'}; -static symbol s_9_7[6] = {'o', 'u', 't', 'i', 'n', 'g'}; - -static struct among a_9[8] = -{ - /* 0 */ {7, s_9_0, -1, -1, 0}, - /* 1 */ {7, s_9_1, -1, -1, 0}, - /* 2 */ {6, s_9_2, -1, -1, 0}, - /* 3 */ {7, s_9_3, -1, -1, 0}, - /* 4 */ {6, s_9_4, -1, -1, 0}, - /* 5 */ {7, s_9_5, -1, -1, 0}, - /* 6 */ {7, s_9_6, -1, -1, 0}, - /* 7 */ {6, s_9_7, -1, -1, 0} -}; - -static symbol s_10_0[5] = {'a', 'n', 'd', 'e', 's'}; -static symbol s_10_1[5] = {'a', 't', 'l', 'a', 's'}; -static symbol s_10_2[4] = {'b', 'i', 'a', 's'}; -static symbol s_10_3[6] = {'c', 'o', 's', 'm', 'o', 's'}; -static symbol s_10_4[5] = {'d', 'y', 'i', 'n', 'g'}; -static symbol s_10_5[5] = {'e', 'a', 'r', 'l', 'y'}; -static symbol s_10_6[6] = {'g', 'e', 'n', 't', 'l', 'y'}; -static symbol s_10_7[4] = {'h', 'o', 'w', 'e'}; -static symbol s_10_8[4] = {'i', 'd', 'l', 'y'}; -static symbol s_10_9[5] = {'l', 'y', 'i', 'n', 'g'}; -static symbol s_10_10[4] = {'n', 'e', 'w', 's'}; -static symbol s_10_11[4] = {'o', 'n', 'l', 'y'}; -static symbol s_10_12[6] = {'s', 'i', 'n', 'g', 'l', 'y'}; -static symbol s_10_13[5] = {'s', 'k', 'i', 'e', 's'}; -static symbol s_10_14[4] = {'s', 'k', 'i', 's'}; -static symbol s_10_15[3] = {'s', 'k', 'y'}; -static symbol s_10_16[5] = {'t', 'y', 'i', 'n', 'g'}; -static symbol s_10_17[4] = {'u', 'g', 'l', 'y'}; - -static struct among a_10[18] = -{ - /* 0 */ {5, s_10_0, -1, -1, 0}, - /* 1 */ {5, s_10_1, -1, -1, 0}, - /* 2 */ {4, s_10_2, -1, -1, 0}, - /* 3 */ {6, s_10_3, -1, -1, 0}, - /* 4 */ {5, s_10_4, -1, 3, 0}, - /* 5 */ {5, s_10_5, -1, 9, 0}, - /* 6 */ {6, s_10_6, -1, 7, 0}, - /* 7 */ {4, s_10_7, -1, -1, 0}, - /* 8 */ {4, s_10_8, -1, 6, 0}, - /* 9 */ {5, s_10_9, -1, 4, 0}, - /* 10 */ {4, s_10_10, -1, -1, 0}, - /* 11 */ {4, s_10_11, -1, 10, 0}, - /* 12 */ {6, s_10_12, -1, 11, 0}, - /* 13 */ {5, s_10_13, -1, 2, 0}, - /* 14 */ {4, s_10_14, -1, 1, 0}, - /* 15 */ {3, s_10_15, -1, -1, 0}, - /* 16 */ {5, s_10_16, -1, 5, 0}, - /* 17 */ {4, s_10_17, -1, 8, 0} -}; - -static unsigned char g_v[] = {17, 65, 16, 1}; - -static unsigned char g_v_WXY[] = {1, 17, 65, 208, 1}; - -static unsigned char g_valid_LI[] = {55, 141, 2}; - -static symbol s_0[] = {'\''}; -static symbol s_1[] = {'y'}; -static symbol s_2[] = {'Y'}; -static symbol s_3[] = {'y'}; -static symbol s_4[] = {'Y'}; -static symbol s_5[] = {'s', 's'}; -static symbol s_6[] = {'i', 'e'}; -static symbol s_7[] = {'i'}; -static symbol s_8[] = {'e', 'e'}; -static symbol s_9[] = {'e'}; -static symbol s_10[] = {'e'}; -static symbol s_11[] = {'y'}; -static symbol s_12[] = {'Y'}; -static symbol s_13[] = {'i'}; -static symbol s_14[] = {'t', 'i', 'o', 'n'}; -static symbol s_15[] = {'e', 'n', 'c', 'e'}; -static symbol s_16[] = {'a', 'n', 'c', 'e'}; -static symbol s_17[] = {'a', 'b', 'l', 'e'}; -static symbol s_18[] = {'e', 'n', 't'}; -static symbol s_19[] = {'i', 'z', 'e'}; -static symbol s_20[] = {'a', 't', 'e'}; -static symbol s_21[] = {'a', 'l'}; -static symbol s_22[] = {'f', 'u', 'l'}; -static symbol s_23[] = {'o', 'u', 's'}; -static symbol s_24[] = {'i', 'v', 'e'}; -static symbol s_25[] = {'b', 'l', 'e'}; -static symbol s_26[] = {'l'}; -static symbol s_27[] = {'o', 'g'}; -static symbol s_28[] = {'f', 'u', 'l'}; -static symbol s_29[] = {'l', 'e', 's', 's'}; -static symbol s_30[] = {'t', 'i', 'o', 'n'}; -static symbol s_31[] = {'a', 't', 'e'}; -static symbol s_32[] = {'a', 'l'}; -static symbol s_33[] = {'i', 'c'}; -static symbol s_34[] = {'s'}; -static symbol s_35[] = {'t'}; -static symbol s_36[] = {'l'}; -static symbol s_37[] = {'s', 'k', 'i'}; -static symbol s_38[] = {'s', 'k', 'y'}; -static symbol s_39[] = {'d', 'i', 'e'}; -static symbol s_40[] = {'l', 'i', 'e'}; -static symbol s_41[] = {'t', 'i', 'e'}; -static symbol s_42[] = {'i', 'd', 'l'}; -static symbol s_43[] = {'g', 'e', 'n', 't', 'l'}; -static symbol s_44[] = {'u', 'g', 'l', 'i'}; -static symbol s_45[] = {'e', 'a', 'r', 'l', 'i'}; -static symbol s_46[] = {'o', 'n', 'l', 'i'}; -static symbol s_47[] = {'s', 'i', 'n', 'g', 'l'}; -static symbol s_48[] = {'Y'}; -static symbol s_49[] = {'y'}; - -static int -r_prelude(struct SN_env * z) -{ - z->B[0] = 0; /* unset Y_found, line 26 */ - { - int c = z->c; /* do, line 27 */ - - z->bra = z->c; /* [, line 27 */ - if (!(eq_s(z, 1, s_0))) - goto lab0; - z->ket = z->c; /* ], line 27 */ - { - int ret; - - ret = slice_del(z); /* delete, line 27 */ - if (ret < 0) - return ret; - } -lab0: - z->c = c; - } - { - int c = z->c; /* do, line 28 */ - - z->bra = z->c; /* [, line 28 */ - if (!(eq_s(z, 1, s_1))) - goto lab1; - z->ket = z->c; /* ], line 28 */ - if (!(in_grouping(z, g_v, 97, 121))) - goto lab1; - { - int ret; - - ret = slice_from_s(z, 1, s_2); /* <-, line 28 */ - if (ret < 0) - return ret; - } - z->B[0] = 1; /* set Y_found, line 28 */ -lab1: - z->c = c; - } - { - int c = z->c; /* do, line 29 */ - - while (1) - { /* repeat, line 29 */ - int c = z->c; - - while (1) - { /* goto, line 29 */ - int c = z->c; - - if (!(in_grouping(z, g_v, 97, 121))) - goto lab4; - z->bra = z->c; /* [, line 29 */ - if (!(eq_s(z, 1, s_3))) - goto lab4; - z->ket = z->c; /* ], line 29 */ - z->c = c; - break; - lab4: - z->c = c; - if (z->c >= z->l) - goto lab3; - z->c++; /* goto, line 29 */ - } - { - int ret; - - ret = slice_from_s(z, 1, s_4); /* <-, line 29 */ - if (ret < 0) - return ret; - } - z->B[0] = 1; /* set Y_found, line 29 */ - continue; - lab3: - z->c = c; - break; - } - z->c = c; - } - return 1; -} - -static int -r_mark_regions(struct SN_env * z) -{ - z->I[0] = z->l; - z->I[1] = z->l; - { - int c = z->c; /* do, line 35 */ - - { - int c = z->c; /* or, line 40 */ - - if (!(find_among(z, a_0, 2))) - goto lab2; /* among, line 36 */ - goto lab1; - lab2: - z->c = c; - while (1) - { /* gopast, line 40 */ - if (!(in_grouping(z, g_v, 97, 121))) - goto lab3; - break; - lab3: - if (z->c >= z->l) - goto lab0; - z->c++; /* gopast, line 40 */ - } - while (1) - { /* gopast, line 40 */ - if (!(out_grouping(z, g_v, 97, 121))) - goto lab4; - break; - lab4: - if (z->c >= z->l) - goto lab0; - z->c++; /* gopast, line 40 */ - } - } -lab1: - z->I[0] = z->c; /* setmark p1, line 41 */ - while (1) - { /* gopast, line 42 */ - if (!(in_grouping(z, g_v, 97, 121))) - goto lab5; - break; - lab5: - if (z->c >= z->l) - goto lab0; - z->c++; /* gopast, line 42 */ - } - while (1) - { /* gopast, line 42 */ - if (!(out_grouping(z, g_v, 97, 121))) - goto lab6; - break; - lab6: - if (z->c >= z->l) - goto lab0; - z->c++; /* gopast, line 42 */ - } - z->I[1] = z->c; /* setmark p2, line 42 */ -lab0: - z->c = c; - } - return 1; -} - -static int -r_shortv(struct SN_env * z) -{ - { - int m = z->l - z->c; - - (void) m; /* or, line 50 */ - if (!(out_grouping_b(z, g_v_WXY, 89, 121))) - goto lab1; - if (!(in_grouping_b(z, g_v, 97, 121))) - goto lab1; - if (!(out_grouping_b(z, g_v, 97, 121))) - goto lab1; - goto lab0; -lab1: - z->c = z->l - m; - if (!(out_grouping_b(z, g_v, 97, 121))) - return 0; - if (!(in_grouping_b(z, g_v, 97, 121))) - return 0; - if (z->c > z->lb) - return 0; /* atlimit, line 51 */ - } -lab0: - return 1; -} - -static int -r_R1(struct SN_env * z) -{ - if (!(z->I[0] <= z->c)) - return 0; - return 1; -} - -static int -r_R2(struct SN_env * z) -{ - if (!(z->I[1] <= z->c)) - return 0; - return 1; -} - -static int -r_Step_1a(struct SN_env * z) -{ - int among_var; - - { - int m = z->l - z->c; - - (void) m; /* try, line 58 */ - z->ket = z->c; /* [, line 59 */ - among_var = find_among_b(z, a_1, 3); /* substring, line 59 */ - if (!(among_var)) - { - z->c = z->l - m; - goto lab0; - } - z->bra = z->c; /* ], line 59 */ - switch (among_var) - { - case 0: - { - z->c = z->l - m; - goto lab0; - } - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 61 */ - if (ret < 0) - return ret; - } - break; - } -lab0: - ; - } - z->ket = z->c; /* [, line 64 */ - among_var = find_among_b(z, a_2, 6); /* substring, line 64 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 64 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_from_s(z, 2, s_5); /* <-, line 65 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int m = z->l - z->c; - - (void) m; /* or, line 67 */ - if (z->c <= z->lb) - goto lab2; - z->c--; /* next, line 67 */ - if (z->c > z->lb) - goto lab2; /* atlimit, line 67 */ - { - int ret; - - ret = slice_from_s(z, 2, s_6); /* <-, line 67 */ - if (ret < 0) - return ret; - } - goto lab1; - lab2: - z->c = z->l - m; - { - int ret; - - ret = slice_from_s(z, 1, s_7); /* <-, line 67 */ - if (ret < 0) - return ret; - } - } - lab1: - break; - case 3: - if (z->c <= z->lb) - return 0; - z->c--; /* next, line 68 */ - while (1) - { /* gopast, line 68 */ - if (!(in_grouping_b(z, g_v, 97, 121))) - goto lab3; - break; - lab3: - if (z->c <= z->lb) - return 0; - z->c--; /* gopast, line 68 */ - } - { - int ret; - - ret = slice_del(z); /* delete, line 68 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_Step_1b(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 74 */ - among_var = find_among_b(z, a_4, 6); /* substring, line 74 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 74 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret = r_R1(z); - - if (ret == 0) - return 0; /* call R1, line 76 */ - if (ret < 0) - return ret; - } - { - int ret; - - ret = slice_from_s(z, 2, s_8); /* <-, line 76 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int m_test = z->l - z->c; /* test, line 79 */ - - while (1) - { /* gopast, line 79 */ - if (!(in_grouping_b(z, g_v, 97, 121))) - goto lab0; - break; - lab0: - if (z->c <= z->lb) - return 0; - z->c--; /* gopast, line 79 */ - } - z->c = z->l - m_test; - } - { - int ret; - - ret = slice_del(z); /* delete, line 79 */ - if (ret < 0) - return ret; - } - { - int m_test = z->l - z->c; /* test, line 80 */ - - among_var = find_among_b(z, a_3, 13); /* substring, line 80 */ - if (!(among_var)) - return 0; - z->c = z->l - m_test; - } - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - { - int c = z->c; - - ret = insert_s(z, z->c, z->c, 1, s_9); /* <+, line 82 */ - z->c = c; - } - if (ret < 0) - return ret; - } - break; - case 2: - z->ket = z->c; /* [, line 85 */ - if (z->c <= z->lb) - return 0; - z->c--; /* next, line 85 */ - z->bra = z->c; /* ], line 85 */ - { - int ret; - - ret = slice_del(z); /* delete, line 85 */ - if (ret < 0) - return ret; - } - break; - case 3: - if (z->c != z->I[0]) - return 0; /* atmark, line 86 */ - { - int m_test = z->l - z->c; /* test, line 86 */ - - { - int ret = r_shortv(z); - - if (ret == 0) - return 0; /* call shortv, line 86 */ - if (ret < 0) - return ret; - } - z->c = z->l - m_test; - } - { - int ret; - - { - int c = z->c; - - ret = insert_s(z, z->c, z->c, 1, s_10); /* <+, line 86 */ - z->c = c; - } - if (ret < 0) - return ret; - } - break; - } - break; - } - return 1; -} - -static int -r_Step_1c(struct SN_env * z) -{ - z->ket = z->c; /* [, line 93 */ - { - int m = z->l - z->c; - - (void) m; /* or, line 93 */ - if (!(eq_s_b(z, 1, s_11))) - goto lab1; - goto lab0; -lab1: - z->c = z->l - m; - if (!(eq_s_b(z, 1, s_12))) - return 0; - } -lab0: - z->bra = z->c; /* ], line 93 */ - if (!(out_grouping_b(z, g_v, 97, 121))) - return 0; - { - int m = z->l - z->c; - - (void) m; /* not, line 94 */ - if (z->c > z->lb) - goto lab2; /* atlimit, line 94 */ - return 0; -lab2: - z->c = z->l - m; - } - { - int ret; - - ret = slice_from_s(z, 1, s_13); /* <-, line 95 */ - if (ret < 0) - return ret; - } - return 1; -} - -static int -r_Step_2(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 99 */ - among_var = find_among_b(z, a_5, 24); /* substring, line 99 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 99 */ - { - int ret = r_R1(z); - - if (ret == 0) - return 0; /* call R1, line 99 */ - if (ret < 0) - return ret; - } - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_from_s(z, 4, s_14); /* <-, line 100 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_from_s(z, 4, s_15); /* <-, line 101 */ - if (ret < 0) - return ret; - } - break; - case 3: - { - int ret; - - ret = slice_from_s(z, 4, s_16); /* <-, line 102 */ - if (ret < 0) - return ret; - } - break; - case 4: - { - int ret; - - ret = slice_from_s(z, 4, s_17); /* <-, line 103 */ - if (ret < 0) - return ret; - } - break; - case 5: - { - int ret; - - ret = slice_from_s(z, 3, s_18); /* <-, line 104 */ - if (ret < 0) - return ret; - } - break; - case 6: - { - int ret; - - ret = slice_from_s(z, 3, s_19); /* <-, line 106 */ - if (ret < 0) - return ret; - } - break; - case 7: - { - int ret; - - ret = slice_from_s(z, 3, s_20); /* <-, line 108 */ - if (ret < 0) - return ret; - } - break; - case 8: - { - int ret; - - ret = slice_from_s(z, 2, s_21); /* <-, line 110 */ - if (ret < 0) - return ret; - } - break; - case 9: - { - int ret; - - ret = slice_from_s(z, 3, s_22); /* <-, line 111 */ - if (ret < 0) - return ret; - } - break; - case 10: - { - int ret; - - ret = slice_from_s(z, 3, s_23); /* <-, line 113 */ - if (ret < 0) - return ret; - } - break; - case 11: - { - int ret; - - ret = slice_from_s(z, 3, s_24); /* <-, line 115 */ - if (ret < 0) - return ret; - } - break; - case 12: - { - int ret; - - ret = slice_from_s(z, 3, s_25); /* <-, line 117 */ - if (ret < 0) - return ret; - } - break; - case 13: - if (!(eq_s_b(z, 1, s_26))) - return 0; - { - int ret; - - ret = slice_from_s(z, 2, s_27); /* <-, line 118 */ - if (ret < 0) - return ret; - } - break; - case 14: - { - int ret; - - ret = slice_from_s(z, 3, s_28); /* <-, line 119 */ - if (ret < 0) - return ret; - } - break; - case 15: - { - int ret; - - ret = slice_from_s(z, 4, s_29); /* <-, line 120 */ - if (ret < 0) - return ret; - } - break; - case 16: - if (!(in_grouping_b(z, g_valid_LI, 99, 116))) - return 0; - { - int ret; - - ret = slice_del(z); /* delete, line 121 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_Step_3(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 126 */ - among_var = find_among_b(z, a_6, 9); /* substring, line 126 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 126 */ - { - int ret = r_R1(z); - - if (ret == 0) - return 0; /* call R1, line 126 */ - if (ret < 0) - return ret; - } - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_from_s(z, 4, s_30); /* <-, line 127 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_from_s(z, 3, s_31); /* <-, line 128 */ - if (ret < 0) - return ret; - } - break; - case 3: - { - int ret; - - ret = slice_from_s(z, 2, s_32); /* <-, line 129 */ - if (ret < 0) - return ret; - } - break; - case 4: - { - int ret; - - ret = slice_from_s(z, 2, s_33); /* <-, line 131 */ - if (ret < 0) - return ret; - } - break; - case 5: - { - int ret; - - ret = slice_del(z); /* delete, line 133 */ - if (ret < 0) - return ret; - } - break; - case 6: - { - int ret = r_R2(z); - - if (ret == 0) - return 0; /* call R2, line 135 */ - if (ret < 0) - return ret; - } - { - int ret; - - ret = slice_del(z); /* delete, line 135 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_Step_4(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 140 */ - among_var = find_among_b(z, a_7, 18); /* substring, line 140 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 140 */ - { - int ret = r_R2(z); - - if (ret == 0) - return 0; /* call R2, line 140 */ - if (ret < 0) - return ret; - } - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 143 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int m = z->l - z->c; - - (void) m; /* or, line 144 */ - if (!(eq_s_b(z, 1, s_34))) - goto lab1; - goto lab0; - lab1: - z->c = z->l - m; - if (!(eq_s_b(z, 1, s_35))) - return 0; - } - lab0: - { - int ret; - - ret = slice_del(z); /* delete, line 144 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_Step_5(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 149 */ - among_var = find_among_b(z, a_8, 2); /* substring, line 149 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 149 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int m = z->l - z->c; - - (void) m; /* or, line 150 */ - { - int ret = r_R2(z); - - if (ret == 0) - goto lab1; /* call R2, line 150 */ - if (ret < 0) - return ret; - } - goto lab0; - lab1: - z->c = z->l - m; - { - int ret = r_R1(z); - - if (ret == 0) - return 0; /* call R1, line 150 */ - if (ret < 0) - return ret; - } - { - int m = z->l - z->c; - - (void) m; /* not, line 150 */ - { - int ret = r_shortv(z); - - if (ret == 0) - goto lab2; /* call shortv, line 150 */ - if (ret < 0) - return ret; - } - return 0; - lab2: - z->c = z->l - m; - } - } - lab0: - { - int ret; - - ret = slice_del(z); /* delete, line 150 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret = r_R2(z); - - if (ret == 0) - return 0; /* call R2, line 151 */ - if (ret < 0) - return ret; - } - if (!(eq_s_b(z, 1, s_36))) - return 0; - { - int ret; - - ret = slice_del(z); /* delete, line 151 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_exception2(struct SN_env * z) -{ - z->ket = z->c; /* [, line 157 */ - if (!(find_among_b(z, a_9, 8))) - return 0; /* substring, line 157 */ - z->bra = z->c; /* ], line 157 */ - if (z->c > z->lb) - return 0; /* atlimit, line 157 */ - return 1; -} - -static int -r_exception1(struct SN_env * z) -{ - int among_var; - - z->bra = z->c; /* [, line 169 */ - among_var = find_among(z, a_10, 18); /* substring, line 169 */ - if (!(among_var)) - return 0; - z->ket = z->c; /* ], line 169 */ - if (z->c < z->l) - return 0; /* atlimit, line 169 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_from_s(z, 3, s_37); /* <-, line 173 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_from_s(z, 3, s_38); /* <-, line 174 */ - if (ret < 0) - return ret; - } - break; - case 3: - { - int ret; - - ret = slice_from_s(z, 3, s_39); /* <-, line 175 */ - if (ret < 0) - return ret; - } - break; - case 4: - { - int ret; - - ret = slice_from_s(z, 3, s_40); /* <-, line 176 */ - if (ret < 0) - return ret; - } - break; - case 5: - { - int ret; - - ret = slice_from_s(z, 3, s_41); /* <-, line 177 */ - if (ret < 0) - return ret; - } - break; - case 6: - { - int ret; - - ret = slice_from_s(z, 3, s_42); /* <-, line 181 */ - if (ret < 0) - return ret; - } - break; - case 7: - { - int ret; - - ret = slice_from_s(z, 5, s_43); /* <-, line 182 */ - if (ret < 0) - return ret; - } - break; - case 8: - { - int ret; - - ret = slice_from_s(z, 4, s_44); /* <-, line 183 */ - if (ret < 0) - return ret; - } - break; - case 9: - { - int ret; - - ret = slice_from_s(z, 5, s_45); /* <-, line 184 */ - if (ret < 0) - return ret; - } - break; - case 10: - { - int ret; - - ret = slice_from_s(z, 4, s_46); /* <-, line 185 */ - if (ret < 0) - return ret; - } - break; - case 11: - { - int ret; - - ret = slice_from_s(z, 5, s_47); /* <-, line 186 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_postlude(struct SN_env * z) -{ - if (!(z->B[0])) - return 0; /* Boolean test Y_found, line 202 */ - while (1) - { /* repeat, line 202 */ - int c = z->c; - - while (1) - { /* goto, line 202 */ - int c = z->c; - - z->bra = z->c; /* [, line 202 */ - if (!(eq_s(z, 1, s_48))) - goto lab1; - z->ket = z->c; /* ], line 202 */ - z->c = c; - break; - lab1: - z->c = c; - if (z->c >= z->l) - goto lab0; - z->c++; /* goto, line 202 */ - } - { - int ret; - - ret = slice_from_s(z, 1, s_49); /* <-, line 202 */ - if (ret < 0) - return ret; - } - continue; -lab0: - z->c = c; - break; - } - return 1; -} - -extern int -english_ISO_8859_1_stem(struct SN_env * z) -{ - { - int c = z->c; /* or, line 206 */ - - { - int ret = r_exception1(z); - - if (ret == 0) - goto lab1; /* call exception1, line 206 */ - if (ret < 0) - return ret; - } - goto lab0; -lab1: - z->c = c; - { - int c = z->c; /* not, line 207 */ - - { - int c = z->c + 3; - - if (0 > c || c > z->l) - goto lab3; - z->c = c; /* hop, line 207 */ - } - goto lab2; - lab3: - z->c = c; - } - goto lab0; -lab2: - z->c = c; - { - int c = z->c; /* do, line 208 */ - - { - int ret = r_prelude(z); - - if (ret == 0) - goto lab4; /* call prelude, line 208 */ - if (ret < 0) - return ret; - } - lab4: - z->c = c; - } - { - int c = z->c; /* do, line 209 */ - - { - int ret = r_mark_regions(z); - - if (ret == 0) - goto lab5; /* call mark_regions, line 209 */ - if (ret < 0) - return ret; - } - lab5: - z->c = c; - } - z->lb = z->c; - z->c = z->l; /* backwards, line 210 */ - - { - int m = z->l - z->c; - - (void) m; /* do, line 212 */ - { - int ret = r_Step_1a(z); - - if (ret == 0) - goto lab6; /* call Step_1a, line 212 */ - if (ret < 0) - return ret; - } - lab6: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* or, line 214 */ - { - int ret = r_exception2(z); - - if (ret == 0) - goto lab8; /* call exception2, line 214 */ - if (ret < 0) - return ret; - } - goto lab7; - lab8: - z->c = z->l - m; - { - int m = z->l - z->c; - - (void) m; /* do, line 216 */ - { - int ret = r_Step_1b(z); - - if (ret == 0) - goto lab9; /* call Step_1b, line 216 */ - if (ret < 0) - return ret; - } - lab9: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 217 */ - { - int ret = r_Step_1c(z); - - if (ret == 0) - goto lab10; /* call Step_1c, line 217 */ - if (ret < 0) - return ret; - } - lab10: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 219 */ - { - int ret = r_Step_2(z); - - if (ret == 0) - goto lab11; /* call Step_2, line 219 */ - if (ret < 0) - return ret; - } - lab11: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 220 */ - { - int ret = r_Step_3(z); - - if (ret == 0) - goto lab12; /* call Step_3, line 220 */ - if (ret < 0) - return ret; - } - lab12: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 221 */ - { - int ret = r_Step_4(z); - - if (ret == 0) - goto lab13; /* call Step_4, line 221 */ - if (ret < 0) - return ret; - } - lab13: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 223 */ - { - int ret = r_Step_5(z); - - if (ret == 0) - goto lab14; /* call Step_5, line 223 */ - if (ret < 0) - return ret; - } - lab14: - z->c = z->l - m; - } - } -lab7: - z->c = z->lb; - { - int c = z->c; /* do, line 226 */ - - { - int ret = r_postlude(z); - - if (ret == 0) - goto lab15; /* call postlude, line 226 */ - if (ret < 0) - return ret; - } - lab15: - z->c = c; - } - } -lab0: - return 1; -} - -extern struct SN_env * -english_ISO_8859_1_create_env(void) -{ - return SN_create_env(0, 2, 1); -} - -extern void -english_ISO_8859_1_close_env(struct SN_env * z) -{ - SN_close_env(z); -} diff --git a/contrib/tsearch2/snowball/english_stem.h b/contrib/tsearch2/snowball/english_stem.h deleted file mode 100644 index 6918a73dd73b..000000000000 --- a/contrib/tsearch2/snowball/english_stem.h +++ /dev/null @@ -1,18 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/snowball/english_stem.h,v 1.6 2006/03/11 04:38:30 momjian Exp $ */ - -/* This file was generated automatically by the Snowball to ANSI C compiler */ - -#ifdef __cplusplus -extern "C" -{ -#endif - -extern struct SN_env *english_ISO_8859_1_create_env(void); -extern void english_ISO_8859_1_close_env(struct SN_env * z); - -extern int english_ISO_8859_1_stem(struct SN_env * z); - -#ifdef __cplusplus -} - -#endif diff --git a/contrib/tsearch2/snowball/header.h b/contrib/tsearch2/snowball/header.h deleted file mode 100644 index 610f99886428..000000000000 --- a/contrib/tsearch2/snowball/header.h +++ /dev/null @@ -1,56 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/snowball/header.h,v 1.8 2006/07/10 22:06:11 momjian Exp $ */ - -#include - -#include "api.h" - -#define HEAD (2 * sizeof(int)) - -#define SIZE(p) ((int *)(p))[-1] -#define SET_SIZE(p, n) ((int *)(p))[-1] = n -#define CAPACITY(p) ((int *)(p))[-2] - -struct among -{ - int s_size; /* number of chars in string */ - symbol *s; /* search string */ - int substring_i; /* index to longest matching substring */ - int result; /* result of the lookup */ - int (*function) (struct SN_env *); -}; - -extern symbol *create_s(void); -extern void lose_s(symbol * p); - -extern int skip_utf8(const symbol * p, int c, int lb, int l, int n); - -extern int in_grouping_U(struct SN_env * z, unsigned char *s, int min, int max); -extern int in_grouping_b_U(struct SN_env * z, unsigned char *s, int min, int max); -extern int out_grouping_U(struct SN_env * z, unsigned char *s, int min, int max); -extern int out_grouping_b_U(struct SN_env * z, unsigned char *s, int min, int max); - -extern int in_grouping(struct SN_env * z, unsigned char *s, int min, int max); -extern int in_grouping_b(struct SN_env * z, unsigned char *s, int min, int max); -extern int out_grouping(struct SN_env * z, unsigned char *s, int min, int max); -extern int out_grouping_b(struct SN_env * z, unsigned char *s, int min, int max); - -extern int eq_s(struct SN_env * z, int s_size, symbol * s); -extern int eq_s_b(struct SN_env * z, int s_size, symbol * s); -extern int eq_v(struct SN_env * z, symbol * p); -extern int eq_v_b(struct SN_env * z, symbol * p); - -extern int find_among(struct SN_env * z, struct among * v, int v_size); -extern int find_among_b(struct SN_env * z, struct among * v, int v_size); - -extern int replace_s(struct SN_env * z, int c_bra, int c_ket, int s_size, const symbol * s, int *adjustment); -extern int slice_from_s(struct SN_env * z, int s_size, symbol * s); -extern int slice_from_v(struct SN_env * z, symbol * p); -extern int slice_del(struct SN_env * z); - -extern int insert_s(struct SN_env * z, int bra, int ket, int s_size, symbol * s); -extern int insert_v(struct SN_env * z, int bra, int ket, symbol * p); - -extern symbol *slice_to(struct SN_env * z, symbol * p); -extern symbol *assign_to(struct SN_env * z, symbol * p); - -extern void debug(struct SN_env * z, int number, int line_count); diff --git a/contrib/tsearch2/snowball/russian_stem.c b/contrib/tsearch2/snowball/russian_stem.c deleted file mode 100644 index a9558b3ab5f9..000000000000 --- a/contrib/tsearch2/snowball/russian_stem.c +++ /dev/null @@ -1,928 +0,0 @@ - -/* This file was generated automatically by the Snowball to ANSI C compiler */ - -#include "header.h" - -extern int russian_KOI8_R_stem(struct SN_env * z); -static int r_tidy_up(struct SN_env * z); -static int r_derivational(struct SN_env * z); -static int r_noun(struct SN_env * z); -static int r_verb(struct SN_env * z); -static int r_reflexive(struct SN_env * z); -static int r_adjectival(struct SN_env * z); -static int r_adjective(struct SN_env * z); -static int r_perfective_gerund(struct SN_env * z); -static int r_R2(struct SN_env * z); -static int r_mark_regions(struct SN_env * z); - -extern struct SN_env *russian_KOI8_R_create_env(void); -extern void russian_KOI8_R_close_env(struct SN_env * z); - -static symbol s_0_0[3] = {0xD7, 0xDB, 0xC9}; -static symbol s_0_1[4] = {0xC9, 0xD7, 0xDB, 0xC9}; -static symbol s_0_2[4] = {0xD9, 0xD7, 0xDB, 0xC9}; -static symbol s_0_3[1] = {0xD7}; -static symbol s_0_4[2] = {0xC9, 0xD7}; -static symbol s_0_5[2] = {0xD9, 0xD7}; -static symbol s_0_6[5] = {0xD7, 0xDB, 0xC9, 0xD3, 0xD8}; -static symbol s_0_7[6] = {0xC9, 0xD7, 0xDB, 0xC9, 0xD3, 0xD8}; -static symbol s_0_8[6] = {0xD9, 0xD7, 0xDB, 0xC9, 0xD3, 0xD8}; - -static struct among a_0[9] = -{ - /* 0 */ {3, s_0_0, -1, 1, 0}, - /* 1 */ {4, s_0_1, 0, 2, 0}, - /* 2 */ {4, s_0_2, 0, 2, 0}, - /* 3 */ {1, s_0_3, -1, 1, 0}, - /* 4 */ {2, s_0_4, 3, 2, 0}, - /* 5 */ {2, s_0_5, 3, 2, 0}, - /* 6 */ {5, s_0_6, -1, 1, 0}, - /* 7 */ {6, s_0_7, 6, 2, 0}, - /* 8 */ {6, s_0_8, 6, 2, 0} -}; - -static symbol s_1_0[2] = {0xC0, 0xC0}; -static symbol s_1_1[2] = {0xC5, 0xC0}; -static symbol s_1_2[2] = {0xCF, 0xC0}; -static symbol s_1_3[2] = {0xD5, 0xC0}; -static symbol s_1_4[2] = {0xC5, 0xC5}; -static symbol s_1_5[2] = {0xC9, 0xC5}; -static symbol s_1_6[2] = {0xCF, 0xC5}; -static symbol s_1_7[2] = {0xD9, 0xC5}; -static symbol s_1_8[2] = {0xC9, 0xC8}; -static symbol s_1_9[2] = {0xD9, 0xC8}; -static symbol s_1_10[3] = {0xC9, 0xCD, 0xC9}; -static symbol s_1_11[3] = {0xD9, 0xCD, 0xC9}; -static symbol s_1_12[2] = {0xC5, 0xCA}; -static symbol s_1_13[2] = {0xC9, 0xCA}; -static symbol s_1_14[2] = {0xCF, 0xCA}; -static symbol s_1_15[2] = {0xD9, 0xCA}; -static symbol s_1_16[2] = {0xC5, 0xCD}; -static symbol s_1_17[2] = {0xC9, 0xCD}; -static symbol s_1_18[2] = {0xCF, 0xCD}; -static symbol s_1_19[2] = {0xD9, 0xCD}; -static symbol s_1_20[3] = {0xC5, 0xC7, 0xCF}; -static symbol s_1_21[3] = {0xCF, 0xC7, 0xCF}; -static symbol s_1_22[2] = {0xC1, 0xD1}; -static symbol s_1_23[2] = {0xD1, 0xD1}; -static symbol s_1_24[3] = {0xC5, 0xCD, 0xD5}; -static symbol s_1_25[3] = {0xCF, 0xCD, 0xD5}; - -static struct among a_1[26] = -{ - /* 0 */ {2, s_1_0, -1, 1, 0}, - /* 1 */ {2, s_1_1, -1, 1, 0}, - /* 2 */ {2, s_1_2, -1, 1, 0}, - /* 3 */ {2, s_1_3, -1, 1, 0}, - /* 4 */ {2, s_1_4, -1, 1, 0}, - /* 5 */ {2, s_1_5, -1, 1, 0}, - /* 6 */ {2, s_1_6, -1, 1, 0}, - /* 7 */ {2, s_1_7, -1, 1, 0}, - /* 8 */ {2, s_1_8, -1, 1, 0}, - /* 9 */ {2, s_1_9, -1, 1, 0}, - /* 10 */ {3, s_1_10, -1, 1, 0}, - /* 11 */ {3, s_1_11, -1, 1, 0}, - /* 12 */ {2, s_1_12, -1, 1, 0}, - /* 13 */ {2, s_1_13, -1, 1, 0}, - /* 14 */ {2, s_1_14, -1, 1, 0}, - /* 15 */ {2, s_1_15, -1, 1, 0}, - /* 16 */ {2, s_1_16, -1, 1, 0}, - /* 17 */ {2, s_1_17, -1, 1, 0}, - /* 18 */ {2, s_1_18, -1, 1, 0}, - /* 19 */ {2, s_1_19, -1, 1, 0}, - /* 20 */ {3, s_1_20, -1, 1, 0}, - /* 21 */ {3, s_1_21, -1, 1, 0}, - /* 22 */ {2, s_1_22, -1, 1, 0}, - /* 23 */ {2, s_1_23, -1, 1, 0}, - /* 24 */ {3, s_1_24, -1, 1, 0}, - /* 25 */ {3, s_1_25, -1, 1, 0} -}; - -static symbol s_2_0[2] = {0xC5, 0xCD}; -static symbol s_2_1[2] = {0xCE, 0xCE}; -static symbol s_2_2[2] = {0xD7, 0xDB}; -static symbol s_2_3[3] = {0xC9, 0xD7, 0xDB}; -static symbol s_2_4[3] = {0xD9, 0xD7, 0xDB}; -static symbol s_2_5[1] = {0xDD}; -static symbol s_2_6[2] = {0xC0, 0xDD}; -static symbol s_2_7[3] = {0xD5, 0xC0, 0xDD}; - -static struct among a_2[8] = -{ - /* 0 */ {2, s_2_0, -1, 1, 0}, - /* 1 */ {2, s_2_1, -1, 1, 0}, - /* 2 */ {2, s_2_2, -1, 1, 0}, - /* 3 */ {3, s_2_3, 2, 2, 0}, - /* 4 */ {3, s_2_4, 2, 2, 0}, - /* 5 */ {1, s_2_5, -1, 1, 0}, - /* 6 */ {2, s_2_6, 5, 1, 0}, - /* 7 */ {3, s_2_7, 6, 2, 0} -}; - -static symbol s_3_0[2] = {0xD3, 0xD1}; -static symbol s_3_1[2] = {0xD3, 0xD8}; - -static struct among a_3[2] = -{ - /* 0 */ {2, s_3_0, -1, 1, 0}, - /* 1 */ {2, s_3_1, -1, 1, 0} -}; - -static symbol s_4_0[1] = {0xC0}; -static symbol s_4_1[2] = {0xD5, 0xC0}; -static symbol s_4_2[2] = {0xCC, 0xC1}; -static symbol s_4_3[3] = {0xC9, 0xCC, 0xC1}; -static symbol s_4_4[3] = {0xD9, 0xCC, 0xC1}; -static symbol s_4_5[2] = {0xCE, 0xC1}; -static symbol s_4_6[3] = {0xC5, 0xCE, 0xC1}; -static symbol s_4_7[3] = {0xC5, 0xD4, 0xC5}; -static symbol s_4_8[3] = {0xC9, 0xD4, 0xC5}; -static symbol s_4_9[3] = {0xCA, 0xD4, 0xC5}; -static symbol s_4_10[4] = {0xC5, 0xCA, 0xD4, 0xC5}; -static symbol s_4_11[4] = {0xD5, 0xCA, 0xD4, 0xC5}; -static symbol s_4_12[2] = {0xCC, 0xC9}; -static symbol s_4_13[3] = {0xC9, 0xCC, 0xC9}; -static symbol s_4_14[3] = {0xD9, 0xCC, 0xC9}; -static symbol s_4_15[1] = {0xCA}; -static symbol s_4_16[2] = {0xC5, 0xCA}; -static symbol s_4_17[2] = {0xD5, 0xCA}; -static symbol s_4_18[1] = {0xCC}; -static symbol s_4_19[2] = {0xC9, 0xCC}; -static symbol s_4_20[2] = {0xD9, 0xCC}; -static symbol s_4_21[2] = {0xC5, 0xCD}; -static symbol s_4_22[2] = {0xC9, 0xCD}; -static symbol s_4_23[2] = {0xD9, 0xCD}; -static symbol s_4_24[1] = {0xCE}; -static symbol s_4_25[2] = {0xC5, 0xCE}; -static symbol s_4_26[2] = {0xCC, 0xCF}; -static symbol s_4_27[3] = {0xC9, 0xCC, 0xCF}; -static symbol s_4_28[3] = {0xD9, 0xCC, 0xCF}; -static symbol s_4_29[2] = {0xCE, 0xCF}; -static symbol s_4_30[3] = {0xC5, 0xCE, 0xCF}; -static symbol s_4_31[3] = {0xCE, 0xCE, 0xCF}; -static symbol s_4_32[2] = {0xC0, 0xD4}; -static symbol s_4_33[3] = {0xD5, 0xC0, 0xD4}; -static symbol s_4_34[2] = {0xC5, 0xD4}; -static symbol s_4_35[3] = {0xD5, 0xC5, 0xD4}; -static symbol s_4_36[2] = {0xC9, 0xD4}; -static symbol s_4_37[2] = {0xD1, 0xD4}; -static symbol s_4_38[2] = {0xD9, 0xD4}; -static symbol s_4_39[2] = {0xD4, 0xD8}; -static symbol s_4_40[3] = {0xC9, 0xD4, 0xD8}; -static symbol s_4_41[3] = {0xD9, 0xD4, 0xD8}; -static symbol s_4_42[3] = {0xC5, 0xDB, 0xD8}; -static symbol s_4_43[3] = {0xC9, 0xDB, 0xD8}; -static symbol s_4_44[2] = {0xCE, 0xD9}; -static symbol s_4_45[3] = {0xC5, 0xCE, 0xD9}; - -static struct among a_4[46] = -{ - /* 0 */ {1, s_4_0, -1, 2, 0}, - /* 1 */ {2, s_4_1, 0, 2, 0}, - /* 2 */ {2, s_4_2, -1, 1, 0}, - /* 3 */ {3, s_4_3, 2, 2, 0}, - /* 4 */ {3, s_4_4, 2, 2, 0}, - /* 5 */ {2, s_4_5, -1, 1, 0}, - /* 6 */ {3, s_4_6, 5, 2, 0}, - /* 7 */ {3, s_4_7, -1, 1, 0}, - /* 8 */ {3, s_4_8, -1, 2, 0}, - /* 9 */ {3, s_4_9, -1, 1, 0}, - /* 10 */ {4, s_4_10, 9, 2, 0}, - /* 11 */ {4, s_4_11, 9, 2, 0}, - /* 12 */ {2, s_4_12, -1, 1, 0}, - /* 13 */ {3, s_4_13, 12, 2, 0}, - /* 14 */ {3, s_4_14, 12, 2, 0}, - /* 15 */ {1, s_4_15, -1, 1, 0}, - /* 16 */ {2, s_4_16, 15, 2, 0}, - /* 17 */ {2, s_4_17, 15, 2, 0}, - /* 18 */ {1, s_4_18, -1, 1, 0}, - /* 19 */ {2, s_4_19, 18, 2, 0}, - /* 20 */ {2, s_4_20, 18, 2, 0}, - /* 21 */ {2, s_4_21, -1, 1, 0}, - /* 22 */ {2, s_4_22, -1, 2, 0}, - /* 23 */ {2, s_4_23, -1, 2, 0}, - /* 24 */ {1, s_4_24, -1, 1, 0}, - /* 25 */ {2, s_4_25, 24, 2, 0}, - /* 26 */ {2, s_4_26, -1, 1, 0}, - /* 27 */ {3, s_4_27, 26, 2, 0}, - /* 28 */ {3, s_4_28, 26, 2, 0}, - /* 29 */ {2, s_4_29, -1, 1, 0}, - /* 30 */ {3, s_4_30, 29, 2, 0}, - /* 31 */ {3, s_4_31, 29, 1, 0}, - /* 32 */ {2, s_4_32, -1, 1, 0}, - /* 33 */ {3, s_4_33, 32, 2, 0}, - /* 34 */ {2, s_4_34, -1, 1, 0}, - /* 35 */ {3, s_4_35, 34, 2, 0}, - /* 36 */ {2, s_4_36, -1, 2, 0}, - /* 37 */ {2, s_4_37, -1, 2, 0}, - /* 38 */ {2, s_4_38, -1, 2, 0}, - /* 39 */ {2, s_4_39, -1, 1, 0}, - /* 40 */ {3, s_4_40, 39, 2, 0}, - /* 41 */ {3, s_4_41, 39, 2, 0}, - /* 42 */ {3, s_4_42, -1, 1, 0}, - /* 43 */ {3, s_4_43, -1, 2, 0}, - /* 44 */ {2, s_4_44, -1, 1, 0}, - /* 45 */ {3, s_4_45, 44, 2, 0} -}; - -static symbol s_5_0[1] = {0xC0}; -static symbol s_5_1[2] = {0xC9, 0xC0}; -static symbol s_5_2[2] = {0xD8, 0xC0}; -static symbol s_5_3[1] = {0xC1}; -static symbol s_5_4[1] = {0xC5}; -static symbol s_5_5[2] = {0xC9, 0xC5}; -static symbol s_5_6[2] = {0xD8, 0xC5}; -static symbol s_5_7[2] = {0xC1, 0xC8}; -static symbol s_5_8[2] = {0xD1, 0xC8}; -static symbol s_5_9[3] = {0xC9, 0xD1, 0xC8}; -static symbol s_5_10[1] = {0xC9}; -static symbol s_5_11[2] = {0xC5, 0xC9}; -static symbol s_5_12[2] = {0xC9, 0xC9}; -static symbol s_5_13[3] = {0xC1, 0xCD, 0xC9}; -static symbol s_5_14[3] = {0xD1, 0xCD, 0xC9}; -static symbol s_5_15[4] = {0xC9, 0xD1, 0xCD, 0xC9}; -static symbol s_5_16[1] = {0xCA}; -static symbol s_5_17[2] = {0xC5, 0xCA}; -static symbol s_5_18[3] = {0xC9, 0xC5, 0xCA}; -static symbol s_5_19[2] = {0xC9, 0xCA}; -static symbol s_5_20[2] = {0xCF, 0xCA}; -static symbol s_5_21[2] = {0xC1, 0xCD}; -static symbol s_5_22[2] = {0xC5, 0xCD}; -static symbol s_5_23[3] = {0xC9, 0xC5, 0xCD}; -static symbol s_5_24[2] = {0xCF, 0xCD}; -static symbol s_5_25[2] = {0xD1, 0xCD}; -static symbol s_5_26[3] = {0xC9, 0xD1, 0xCD}; -static symbol s_5_27[1] = {0xCF}; -static symbol s_5_28[1] = {0xD1}; -static symbol s_5_29[2] = {0xC9, 0xD1}; -static symbol s_5_30[2] = {0xD8, 0xD1}; -static symbol s_5_31[1] = {0xD5}; -static symbol s_5_32[2] = {0xC5, 0xD7}; -static symbol s_5_33[2] = {0xCF, 0xD7}; -static symbol s_5_34[1] = {0xD8}; -static symbol s_5_35[1] = {0xD9}; - -static struct among a_5[36] = -{ - /* 0 */ {1, s_5_0, -1, 1, 0}, - /* 1 */ {2, s_5_1, 0, 1, 0}, - /* 2 */ {2, s_5_2, 0, 1, 0}, - /* 3 */ {1, s_5_3, -1, 1, 0}, - /* 4 */ {1, s_5_4, -1, 1, 0}, - /* 5 */ {2, s_5_5, 4, 1, 0}, - /* 6 */ {2, s_5_6, 4, 1, 0}, - /* 7 */ {2, s_5_7, -1, 1, 0}, - /* 8 */ {2, s_5_8, -1, 1, 0}, - /* 9 */ {3, s_5_9, 8, 1, 0}, - /* 10 */ {1, s_5_10, -1, 1, 0}, - /* 11 */ {2, s_5_11, 10, 1, 0}, - /* 12 */ {2, s_5_12, 10, 1, 0}, - /* 13 */ {3, s_5_13, 10, 1, 0}, - /* 14 */ {3, s_5_14, 10, 1, 0}, - /* 15 */ {4, s_5_15, 14, 1, 0}, - /* 16 */ {1, s_5_16, -1, 1, 0}, - /* 17 */ {2, s_5_17, 16, 1, 0}, - /* 18 */ {3, s_5_18, 17, 1, 0}, - /* 19 */ {2, s_5_19, 16, 1, 0}, - /* 20 */ {2, s_5_20, 16, 1, 0}, - /* 21 */ {2, s_5_21, -1, 1, 0}, - /* 22 */ {2, s_5_22, -1, 1, 0}, - /* 23 */ {3, s_5_23, 22, 1, 0}, - /* 24 */ {2, s_5_24, -1, 1, 0}, - /* 25 */ {2, s_5_25, -1, 1, 0}, - /* 26 */ {3, s_5_26, 25, 1, 0}, - /* 27 */ {1, s_5_27, -1, 1, 0}, - /* 28 */ {1, s_5_28, -1, 1, 0}, - /* 29 */ {2, s_5_29, 28, 1, 0}, - /* 30 */ {2, s_5_30, 28, 1, 0}, - /* 31 */ {1, s_5_31, -1, 1, 0}, - /* 32 */ {2, s_5_32, -1, 1, 0}, - /* 33 */ {2, s_5_33, -1, 1, 0}, - /* 34 */ {1, s_5_34, -1, 1, 0}, - /* 35 */ {1, s_5_35, -1, 1, 0} -}; - -static symbol s_6_0[3] = {0xCF, 0xD3, 0xD4}; -static symbol s_6_1[4] = {0xCF, 0xD3, 0xD4, 0xD8}; - -static struct among a_6[2] = -{ - /* 0 */ {3, s_6_0, -1, 1, 0}, - /* 1 */ {4, s_6_1, -1, 1, 0} -}; - -static symbol s_7_0[4] = {0xC5, 0xCA, 0xDB, 0xC5}; -static symbol s_7_1[1] = {0xCE}; -static symbol s_7_2[1] = {0xD8}; -static symbol s_7_3[3] = {0xC5, 0xCA, 0xDB}; - -static struct among a_7[4] = -{ - /* 0 */ {4, s_7_0, -1, 1, 0}, - /* 1 */ {1, s_7_1, -1, 2, 0}, - /* 2 */ {1, s_7_2, -1, 3, 0}, - /* 3 */ {3, s_7_3, -1, 1, 0} -}; - -static unsigned char g_v[] = {35, 130, 34, 18}; - -static symbol s_0[] = {0xC1}; -static symbol s_1[] = {0xD1}; -static symbol s_2[] = {0xC1}; -static symbol s_3[] = {0xD1}; -static symbol s_4[] = {0xC1}; -static symbol s_5[] = {0xD1}; -static symbol s_6[] = {0xCE}; -static symbol s_7[] = {0xCE}; -static symbol s_8[] = {0xCE}; -static symbol s_9[] = {0xC9}; - -static int -r_mark_regions(struct SN_env * z) -{ - z->I[0] = z->l; - z->I[1] = z->l; - { - int c = z->c; /* do, line 63 */ - - while (1) - { /* gopast, line 64 */ - if (!(in_grouping(z, g_v, 192, 220))) - goto lab1; - break; - lab1: - if (z->c >= z->l) - goto lab0; - z->c++; /* gopast, line 64 */ - } - z->I[0] = z->c; /* setmark pV, line 64 */ - while (1) - { /* gopast, line 64 */ - if (!(out_grouping(z, g_v, 192, 220))) - goto lab2; - break; - lab2: - if (z->c >= z->l) - goto lab0; - z->c++; /* gopast, line 64 */ - } - while (1) - { /* gopast, line 65 */ - if (!(in_grouping(z, g_v, 192, 220))) - goto lab3; - break; - lab3: - if (z->c >= z->l) - goto lab0; - z->c++; /* gopast, line 65 */ - } - while (1) - { /* gopast, line 65 */ - if (!(out_grouping(z, g_v, 192, 220))) - goto lab4; - break; - lab4: - if (z->c >= z->l) - goto lab0; - z->c++; /* gopast, line 65 */ - } - z->I[1] = z->c; /* setmark p2, line 65 */ -lab0: - z->c = c; - } - return 1; -} - -static int -r_R2(struct SN_env * z) -{ - if (!(z->I[1] <= z->c)) - return 0; - return 1; -} - -static int -r_perfective_gerund(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 74 */ - among_var = find_among_b(z, a_0, 9); /* substring, line 74 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 74 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int m = z->l - z->c; - - (void) m; /* or, line 78 */ - if (!(eq_s_b(z, 1, s_0))) - goto lab1; - goto lab0; - lab1: - z->c = z->l - m; - if (!(eq_s_b(z, 1, s_1))) - return 0; - } - lab0: - { - int ret; - - ret = slice_del(z); /* delete, line 78 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_del(z); /* delete, line 85 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_adjective(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 90 */ - among_var = find_among_b(z, a_1, 26); /* substring, line 90 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 90 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 99 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_adjectival(struct SN_env * z) -{ - int among_var; - - { - int ret = r_adjective(z); - - if (ret == 0) - return 0; /* call adjective, line 104 */ - if (ret < 0) - return ret; - } - { - int m = z->l - z->c; - - (void) m; /* try, line 111 */ - z->ket = z->c; /* [, line 112 */ - among_var = find_among_b(z, a_2, 8); /* substring, line 112 */ - if (!(among_var)) - { - z->c = z->l - m; - goto lab0; - } - z->bra = z->c; /* ], line 112 */ - switch (among_var) - { - case 0: - { - z->c = z->l - m; - goto lab0; - } - case 1: - { - int m = z->l - z->c; - - (void) m; /* or, line 117 */ - if (!(eq_s_b(z, 1, s_2))) - goto lab2; - goto lab1; - lab2: - z->c = z->l - m; - if (!(eq_s_b(z, 1, s_3))) - { - z->c = z->l - m; - goto lab0; - } - } - lab1: - { - int ret; - - ret = slice_del(z); /* delete, line 117 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_del(z); /* delete, line 124 */ - if (ret < 0) - return ret; - } - break; - } -lab0: - ; - } - return 1; -} - -static int -r_reflexive(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 131 */ - among_var = find_among_b(z, a_3, 2); /* substring, line 131 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 131 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 134 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_verb(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 139 */ - among_var = find_among_b(z, a_4, 46); /* substring, line 139 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 139 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int m = z->l - z->c; - - (void) m; /* or, line 145 */ - if (!(eq_s_b(z, 1, s_4))) - goto lab1; - goto lab0; - lab1: - z->c = z->l - m; - if (!(eq_s_b(z, 1, s_5))) - return 0; - } - lab0: - { - int ret; - - ret = slice_del(z); /* delete, line 145 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_del(z); /* delete, line 153 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_noun(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 162 */ - among_var = find_among_b(z, a_5, 36); /* substring, line 162 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 162 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 169 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_derivational(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 178 */ - among_var = find_among_b(z, a_6, 2); /* substring, line 178 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 178 */ - { - int ret = r_R2(z); - - if (ret == 0) - return 0; /* call R2, line 178 */ - if (ret < 0) - return ret; - } - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 181 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_tidy_up(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 186 */ - among_var = find_among_b(z, a_7, 4); /* substring, line 186 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 186 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 190 */ - if (ret < 0) - return ret; - } - z->ket = z->c; /* [, line 191 */ - if (!(eq_s_b(z, 1, s_6))) - return 0; - z->bra = z->c; /* ], line 191 */ - if (!(eq_s_b(z, 1, s_7))) - return 0; - { - int ret; - - ret = slice_del(z); /* delete, line 191 */ - if (ret < 0) - return ret; - } - break; - case 2: - if (!(eq_s_b(z, 1, s_8))) - return 0; - { - int ret; - - ret = slice_del(z); /* delete, line 194 */ - if (ret < 0) - return ret; - } - break; - case 3: - { - int ret; - - ret = slice_del(z); /* delete, line 196 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -extern int -russian_KOI8_R_stem(struct SN_env * z) -{ - { - int c = z->c; /* do, line 203 */ - - { - int ret = r_mark_regions(z); - - if (ret == 0) - goto lab0; /* call mark_regions, line 203 */ - if (ret < 0) - return ret; - } -lab0: - z->c = c; - } - z->lb = z->c; - z->c = z->l; /* backwards, line 204 */ - - { - int m3; /* setlimit, line 204 */ - int m = z->l - z->c; - - (void) m; - if (z->c < z->I[0]) - return 0; - z->c = z->I[0]; /* tomark, line 204 */ - m3 = z->lb; - z->lb = z->c; - z->c = z->l - m; - { - int m = z->l - z->c; - - (void) m; /* do, line 205 */ - { - int m = z->l - z->c; - - (void) m; /* or, line 206 */ - { - int ret = r_perfective_gerund(z); - - if (ret == 0) - goto lab3; /* call perfective_gerund, line 206 */ - if (ret < 0) - return ret; - } - goto lab2; - lab3: - z->c = z->l - m; - { - int m = z->l - z->c; - - (void) m; /* try, line 207 */ - { - int ret = r_reflexive(z); - - if (ret == 0) - { - z->c = z->l - m; - goto lab4; - } /* call reflexive, line 207 */ - if (ret < 0) - return ret; - } - lab4: - ; - } - { - int m = z->l - z->c; - - (void) m; /* or, line 208 */ - { - int ret = r_adjectival(z); - - if (ret == 0) - goto lab6; /* call adjectival, line 208 */ - if (ret < 0) - return ret; - } - goto lab5; - lab6: - z->c = z->l - m; - { - int ret = r_verb(z); - - if (ret == 0) - goto lab7; /* call verb, line 208 */ - if (ret < 0) - return ret; - } - goto lab5; - lab7: - z->c = z->l - m; - { - int ret = r_noun(z); - - if (ret == 0) - goto lab1; /* call noun, line 208 */ - if (ret < 0) - return ret; - } - } - lab5: - ; - } - lab2: - lab1: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* try, line 211 */ - z->ket = z->c; /* [, line 211 */ - if (!(eq_s_b(z, 1, s_9))) - { - z->c = z->l - m; - goto lab8; - } - z->bra = z->c; /* ], line 211 */ - { - int ret; - - ret = slice_del(z); /* delete, line 211 */ - if (ret < 0) - return ret; - } - lab8: - ; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 214 */ - { - int ret = r_derivational(z); - - if (ret == 0) - goto lab9; /* call derivational, line 214 */ - if (ret < 0) - return ret; - } - lab9: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 215 */ - { - int ret = r_tidy_up(z); - - if (ret == 0) - goto lab10; /* call tidy_up, line 215 */ - if (ret < 0) - return ret; - } - lab10: - z->c = z->l - m; - } - z->lb = m3; - } - z->c = z->lb; - return 1; -} - -extern struct SN_env * -russian_KOI8_R_create_env(void) -{ - return SN_create_env(0, 2, 0); -} - -extern void -russian_KOI8_R_close_env(struct SN_env * z) -{ - SN_close_env(z); -} diff --git a/contrib/tsearch2/snowball/russian_stem_UTF8.c b/contrib/tsearch2/snowball/russian_stem_UTF8.c deleted file mode 100644 index 994ff267cab6..000000000000 --- a/contrib/tsearch2/snowball/russian_stem_UTF8.c +++ /dev/null @@ -1,942 +0,0 @@ - -/* This file was generated automatically by the Snowball to ANSI C compiler */ - -#include "header.h" - -extern int russian_UTF_8_stem(struct SN_env * z); -static int r_tidy_up(struct SN_env * z); -static int r_derivational(struct SN_env * z); -static int r_noun(struct SN_env * z); -static int r_verb(struct SN_env * z); -static int r_reflexive(struct SN_env * z); -static int r_adjectival(struct SN_env * z); -static int r_adjective(struct SN_env * z); -static int r_perfective_gerund(struct SN_env * z); -static int r_R2(struct SN_env * z); -static int r_mark_regions(struct SN_env * z); - -extern struct SN_env *russian_UTF_8_create_env(void); -extern void russian_UTF_8_close_env(struct SN_env * z); - -static symbol s_0_0[10] = {0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8, 0xD1, 0x81, 0xD1, 0x8C}; -static symbol s_0_1[12] = {0xD1, 0x8B, 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8, 0xD1, 0x81, 0xD1, 0x8C}; -static symbol s_0_2[12] = {0xD0, 0xB8, 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8, 0xD1, 0x81, 0xD1, 0x8C}; -static symbol s_0_3[2] = {0xD0, 0xB2}; -static symbol s_0_4[4] = {0xD1, 0x8B, 0xD0, 0xB2}; -static symbol s_0_5[4] = {0xD0, 0xB8, 0xD0, 0xB2}; -static symbol s_0_6[6] = {0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8}; -static symbol s_0_7[8] = {0xD1, 0x8B, 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8}; -static symbol s_0_8[8] = {0xD0, 0xB8, 0xD0, 0xB2, 0xD1, 0x88, 0xD0, 0xB8}; - -static struct among a_0[9] = -{ - /* 0 */ {10, s_0_0, -1, 1, 0}, - /* 1 */ {12, s_0_1, 0, 2, 0}, - /* 2 */ {12, s_0_2, 0, 2, 0}, - /* 3 */ {2, s_0_3, -1, 1, 0}, - /* 4 */ {4, s_0_4, 3, 2, 0}, - /* 5 */ {4, s_0_5, 3, 2, 0}, - /* 6 */ {6, s_0_6, -1, 1, 0}, - /* 7 */ {8, s_0_7, 6, 2, 0}, - /* 8 */ {8, s_0_8, 6, 2, 0} -}; - -static symbol s_1_0[6] = {0xD0, 0xB5, 0xD0, 0xBC, 0xD1, 0x83}; -static symbol s_1_1[6] = {0xD0, 0xBE, 0xD0, 0xBC, 0xD1, 0x83}; -static symbol s_1_2[4] = {0xD1, 0x8B, 0xD1, 0x85}; -static symbol s_1_3[4] = {0xD0, 0xB8, 0xD1, 0x85}; -static symbol s_1_4[4] = {0xD1, 0x83, 0xD1, 0x8E}; -static symbol s_1_5[4] = {0xD1, 0x8E, 0xD1, 0x8E}; -static symbol s_1_6[4] = {0xD0, 0xB5, 0xD1, 0x8E}; -static symbol s_1_7[4] = {0xD0, 0xBE, 0xD1, 0x8E}; -static symbol s_1_8[4] = {0xD1, 0x8F, 0xD1, 0x8F}; -static symbol s_1_9[4] = {0xD0, 0xB0, 0xD1, 0x8F}; -static symbol s_1_10[4] = {0xD1, 0x8B, 0xD0, 0xB5}; -static symbol s_1_11[4] = {0xD0, 0xB5, 0xD0, 0xB5}; -static symbol s_1_12[4] = {0xD0, 0xB8, 0xD0, 0xB5}; -static symbol s_1_13[4] = {0xD0, 0xBE, 0xD0, 0xB5}; -static symbol s_1_14[6] = {0xD1, 0x8B, 0xD0, 0xBC, 0xD0, 0xB8}; -static symbol s_1_15[6] = {0xD0, 0xB8, 0xD0, 0xBC, 0xD0, 0xB8}; -static symbol s_1_16[4] = {0xD1, 0x8B, 0xD0, 0xB9}; -static symbol s_1_17[4] = {0xD0, 0xB5, 0xD0, 0xB9}; -static symbol s_1_18[4] = {0xD0, 0xB8, 0xD0, 0xB9}; -static symbol s_1_19[4] = {0xD0, 0xBE, 0xD0, 0xB9}; -static symbol s_1_20[4] = {0xD1, 0x8B, 0xD0, 0xBC}; -static symbol s_1_21[4] = {0xD0, 0xB5, 0xD0, 0xBC}; -static symbol s_1_22[4] = {0xD0, 0xB8, 0xD0, 0xBC}; -static symbol s_1_23[4] = {0xD0, 0xBE, 0xD0, 0xBC}; -static symbol s_1_24[6] = {0xD0, 0xB5, 0xD0, 0xB3, 0xD0, 0xBE}; -static symbol s_1_25[6] = {0xD0, 0xBE, 0xD0, 0xB3, 0xD0, 0xBE}; - -static struct among a_1[26] = -{ - /* 0 */ {6, s_1_0, -1, 1, 0}, - /* 1 */ {6, s_1_1, -1, 1, 0}, - /* 2 */ {4, s_1_2, -1, 1, 0}, - /* 3 */ {4, s_1_3, -1, 1, 0}, - /* 4 */ {4, s_1_4, -1, 1, 0}, - /* 5 */ {4, s_1_5, -1, 1, 0}, - /* 6 */ {4, s_1_6, -1, 1, 0}, - /* 7 */ {4, s_1_7, -1, 1, 0}, - /* 8 */ {4, s_1_8, -1, 1, 0}, - /* 9 */ {4, s_1_9, -1, 1, 0}, - /* 10 */ {4, s_1_10, -1, 1, 0}, - /* 11 */ {4, s_1_11, -1, 1, 0}, - /* 12 */ {4, s_1_12, -1, 1, 0}, - /* 13 */ {4, s_1_13, -1, 1, 0}, - /* 14 */ {6, s_1_14, -1, 1, 0}, - /* 15 */ {6, s_1_15, -1, 1, 0}, - /* 16 */ {4, s_1_16, -1, 1, 0}, - /* 17 */ {4, s_1_17, -1, 1, 0}, - /* 18 */ {4, s_1_18, -1, 1, 0}, - /* 19 */ {4, s_1_19, -1, 1, 0}, - /* 20 */ {4, s_1_20, -1, 1, 0}, - /* 21 */ {4, s_1_21, -1, 1, 0}, - /* 22 */ {4, s_1_22, -1, 1, 0}, - /* 23 */ {4, s_1_23, -1, 1, 0}, - /* 24 */ {6, s_1_24, -1, 1, 0}, - /* 25 */ {6, s_1_25, -1, 1, 0} -}; - -static symbol s_2_0[4] = {0xD0, 0xB2, 0xD1, 0x88}; -static symbol s_2_1[6] = {0xD1, 0x8B, 0xD0, 0xB2, 0xD1, 0x88}; -static symbol s_2_2[6] = {0xD0, 0xB8, 0xD0, 0xB2, 0xD1, 0x88}; -static symbol s_2_3[2] = {0xD1, 0x89}; -static symbol s_2_4[4] = {0xD1, 0x8E, 0xD1, 0x89}; -static symbol s_2_5[6] = {0xD1, 0x83, 0xD1, 0x8E, 0xD1, 0x89}; -static symbol s_2_6[4] = {0xD0, 0xB5, 0xD0, 0xBC}; -static symbol s_2_7[4] = {0xD0, 0xBD, 0xD0, 0xBD}; - -static struct among a_2[8] = -{ - /* 0 */ {4, s_2_0, -1, 1, 0}, - /* 1 */ {6, s_2_1, 0, 2, 0}, - /* 2 */ {6, s_2_2, 0, 2, 0}, - /* 3 */ {2, s_2_3, -1, 1, 0}, - /* 4 */ {4, s_2_4, 3, 1, 0}, - /* 5 */ {6, s_2_5, 4, 2, 0}, - /* 6 */ {4, s_2_6, -1, 1, 0}, - /* 7 */ {4, s_2_7, -1, 1, 0} -}; - -static symbol s_3_0[4] = {0xD1, 0x81, 0xD1, 0x8C}; -static symbol s_3_1[4] = {0xD1, 0x81, 0xD1, 0x8F}; - -static struct among a_3[2] = -{ - /* 0 */ {4, s_3_0, -1, 1, 0}, - /* 1 */ {4, s_3_1, -1, 1, 0} -}; - -static symbol s_4_0[4] = {0xD1, 0x8B, 0xD1, 0x82}; -static symbol s_4_1[4] = {0xD1, 0x8E, 0xD1, 0x82}; -static symbol s_4_2[6] = {0xD1, 0x83, 0xD1, 0x8E, 0xD1, 0x82}; -static symbol s_4_3[4] = {0xD1, 0x8F, 0xD1, 0x82}; -static symbol s_4_4[4] = {0xD0, 0xB5, 0xD1, 0x82}; -static symbol s_4_5[6] = {0xD1, 0x83, 0xD0, 0xB5, 0xD1, 0x82}; -static symbol s_4_6[4] = {0xD0, 0xB8, 0xD1, 0x82}; -static symbol s_4_7[4] = {0xD0, 0xBD, 0xD1, 0x8B}; -static symbol s_4_8[6] = {0xD0, 0xB5, 0xD0, 0xBD, 0xD1, 0x8B}; -static symbol s_4_9[4] = {0xD1, 0x82, 0xD1, 0x8C}; -static symbol s_4_10[6] = {0xD1, 0x8B, 0xD1, 0x82, 0xD1, 0x8C}; -static symbol s_4_11[6] = {0xD0, 0xB8, 0xD1, 0x82, 0xD1, 0x8C}; -static symbol s_4_12[6] = {0xD0, 0xB5, 0xD1, 0x88, 0xD1, 0x8C}; -static symbol s_4_13[6] = {0xD0, 0xB8, 0xD1, 0x88, 0xD1, 0x8C}; -static symbol s_4_14[2] = {0xD1, 0x8E}; -static symbol s_4_15[4] = {0xD1, 0x83, 0xD1, 0x8E}; -static symbol s_4_16[4] = {0xD0, 0xBB, 0xD0, 0xB0}; -static symbol s_4_17[6] = {0xD1, 0x8B, 0xD0, 0xBB, 0xD0, 0xB0}; -static symbol s_4_18[6] = {0xD0, 0xB8, 0xD0, 0xBB, 0xD0, 0xB0}; -static symbol s_4_19[4] = {0xD0, 0xBD, 0xD0, 0xB0}; -static symbol s_4_20[6] = {0xD0, 0xB5, 0xD0, 0xBD, 0xD0, 0xB0}; -static symbol s_4_21[6] = {0xD0, 0xB5, 0xD1, 0x82, 0xD0, 0xB5}; -static symbol s_4_22[6] = {0xD0, 0xB8, 0xD1, 0x82, 0xD0, 0xB5}; -static symbol s_4_23[6] = {0xD0, 0xB9, 0xD1, 0x82, 0xD0, 0xB5}; -static symbol s_4_24[8] = {0xD1, 0x83, 0xD0, 0xB9, 0xD1, 0x82, 0xD0, 0xB5}; -static symbol s_4_25[8] = {0xD0, 0xB5, 0xD0, 0xB9, 0xD1, 0x82, 0xD0, 0xB5}; -static symbol s_4_26[4] = {0xD0, 0xBB, 0xD0, 0xB8}; -static symbol s_4_27[6] = {0xD1, 0x8B, 0xD0, 0xBB, 0xD0, 0xB8}; -static symbol s_4_28[6] = {0xD0, 0xB8, 0xD0, 0xBB, 0xD0, 0xB8}; -static symbol s_4_29[2] = {0xD0, 0xB9}; -static symbol s_4_30[4] = {0xD1, 0x83, 0xD0, 0xB9}; -static symbol s_4_31[4] = {0xD0, 0xB5, 0xD0, 0xB9}; -static symbol s_4_32[2] = {0xD0, 0xBB}; -static symbol s_4_33[4] = {0xD1, 0x8B, 0xD0, 0xBB}; -static symbol s_4_34[4] = {0xD0, 0xB8, 0xD0, 0xBB}; -static symbol s_4_35[4] = {0xD1, 0x8B, 0xD0, 0xBC}; -static symbol s_4_36[4] = {0xD0, 0xB5, 0xD0, 0xBC}; -static symbol s_4_37[4] = {0xD0, 0xB8, 0xD0, 0xBC}; -static symbol s_4_38[2] = {0xD0, 0xBD}; -static symbol s_4_39[4] = {0xD0, 0xB5, 0xD0, 0xBD}; -static symbol s_4_40[4] = {0xD0, 0xBB, 0xD0, 0xBE}; -static symbol s_4_41[6] = {0xD1, 0x8B, 0xD0, 0xBB, 0xD0, 0xBE}; -static symbol s_4_42[6] = {0xD0, 0xB8, 0xD0, 0xBB, 0xD0, 0xBE}; -static symbol s_4_43[4] = {0xD0, 0xBD, 0xD0, 0xBE}; -static symbol s_4_44[6] = {0xD0, 0xB5, 0xD0, 0xBD, 0xD0, 0xBE}; -static symbol s_4_45[6] = {0xD0, 0xBD, 0xD0, 0xBD, 0xD0, 0xBE}; - -static struct among a_4[46] = -{ - /* 0 */ {4, s_4_0, -1, 2, 0}, - /* 1 */ {4, s_4_1, -1, 1, 0}, - /* 2 */ {6, s_4_2, 1, 2, 0}, - /* 3 */ {4, s_4_3, -1, 2, 0}, - /* 4 */ {4, s_4_4, -1, 1, 0}, - /* 5 */ {6, s_4_5, 4, 2, 0}, - /* 6 */ {4, s_4_6, -1, 2, 0}, - /* 7 */ {4, s_4_7, -1, 1, 0}, - /* 8 */ {6, s_4_8, 7, 2, 0}, - /* 9 */ {4, s_4_9, -1, 1, 0}, - /* 10 */ {6, s_4_10, 9, 2, 0}, - /* 11 */ {6, s_4_11, 9, 2, 0}, - /* 12 */ {6, s_4_12, -1, 1, 0}, - /* 13 */ {6, s_4_13, -1, 2, 0}, - /* 14 */ {2, s_4_14, -1, 2, 0}, - /* 15 */ {4, s_4_15, 14, 2, 0}, - /* 16 */ {4, s_4_16, -1, 1, 0}, - /* 17 */ {6, s_4_17, 16, 2, 0}, - /* 18 */ {6, s_4_18, 16, 2, 0}, - /* 19 */ {4, s_4_19, -1, 1, 0}, - /* 20 */ {6, s_4_20, 19, 2, 0}, - /* 21 */ {6, s_4_21, -1, 1, 0}, - /* 22 */ {6, s_4_22, -1, 2, 0}, - /* 23 */ {6, s_4_23, -1, 1, 0}, - /* 24 */ {8, s_4_24, 23, 2, 0}, - /* 25 */ {8, s_4_25, 23, 2, 0}, - /* 26 */ {4, s_4_26, -1, 1, 0}, - /* 27 */ {6, s_4_27, 26, 2, 0}, - /* 28 */ {6, s_4_28, 26, 2, 0}, - /* 29 */ {2, s_4_29, -1, 1, 0}, - /* 30 */ {4, s_4_30, 29, 2, 0}, - /* 31 */ {4, s_4_31, 29, 2, 0}, - /* 32 */ {2, s_4_32, -1, 1, 0}, - /* 33 */ {4, s_4_33, 32, 2, 0}, - /* 34 */ {4, s_4_34, 32, 2, 0}, - /* 35 */ {4, s_4_35, -1, 2, 0}, - /* 36 */ {4, s_4_36, -1, 1, 0}, - /* 37 */ {4, s_4_37, -1, 2, 0}, - /* 38 */ {2, s_4_38, -1, 1, 0}, - /* 39 */ {4, s_4_39, 38, 2, 0}, - /* 40 */ {4, s_4_40, -1, 1, 0}, - /* 41 */ {6, s_4_41, 40, 2, 0}, - /* 42 */ {6, s_4_42, 40, 2, 0}, - /* 43 */ {4, s_4_43, -1, 1, 0}, - /* 44 */ {6, s_4_44, 43, 2, 0}, - /* 45 */ {6, s_4_45, 43, 1, 0} -}; - -static symbol s_5_0[2] = {0xD1, 0x83}; -static symbol s_5_1[4] = {0xD1, 0x8F, 0xD1, 0x85}; -static symbol s_5_2[6] = {0xD0, 0xB8, 0xD1, 0x8F, 0xD1, 0x85}; -static symbol s_5_3[4] = {0xD0, 0xB0, 0xD1, 0x85}; -static symbol s_5_4[2] = {0xD1, 0x8B}; -static symbol s_5_5[2] = {0xD1, 0x8C}; -static symbol s_5_6[2] = {0xD1, 0x8E}; -static symbol s_5_7[4] = {0xD1, 0x8C, 0xD1, 0x8E}; -static symbol s_5_8[4] = {0xD0, 0xB8, 0xD1, 0x8E}; -static symbol s_5_9[2] = {0xD1, 0x8F}; -static symbol s_5_10[4] = {0xD1, 0x8C, 0xD1, 0x8F}; -static symbol s_5_11[4] = {0xD0, 0xB8, 0xD1, 0x8F}; -static symbol s_5_12[2] = {0xD0, 0xB0}; -static symbol s_5_13[4] = {0xD0, 0xB5, 0xD0, 0xB2}; -static symbol s_5_14[4] = {0xD0, 0xBE, 0xD0, 0xB2}; -static symbol s_5_15[2] = {0xD0, 0xB5}; -static symbol s_5_16[4] = {0xD1, 0x8C, 0xD0, 0xB5}; -static symbol s_5_17[4] = {0xD0, 0xB8, 0xD0, 0xB5}; -static symbol s_5_18[2] = {0xD0, 0xB8}; -static symbol s_5_19[4] = {0xD0, 0xB5, 0xD0, 0xB8}; -static symbol s_5_20[4] = {0xD0, 0xB8, 0xD0, 0xB8}; -static symbol s_5_21[6] = {0xD1, 0x8F, 0xD0, 0xBC, 0xD0, 0xB8}; -static symbol s_5_22[8] = {0xD0, 0xB8, 0xD1, 0x8F, 0xD0, 0xBC, 0xD0, 0xB8}; -static symbol s_5_23[6] = {0xD0, 0xB0, 0xD0, 0xBC, 0xD0, 0xB8}; -static symbol s_5_24[2] = {0xD0, 0xB9}; -static symbol s_5_25[4] = {0xD0, 0xB5, 0xD0, 0xB9}; -static symbol s_5_26[6] = {0xD0, 0xB8, 0xD0, 0xB5, 0xD0, 0xB9}; -static symbol s_5_27[4] = {0xD0, 0xB8, 0xD0, 0xB9}; -static symbol s_5_28[4] = {0xD0, 0xBE, 0xD0, 0xB9}; -static symbol s_5_29[4] = {0xD1, 0x8F, 0xD0, 0xBC}; -static symbol s_5_30[6] = {0xD0, 0xB8, 0xD1, 0x8F, 0xD0, 0xBC}; -static symbol s_5_31[4] = {0xD0, 0xB0, 0xD0, 0xBC}; -static symbol s_5_32[4] = {0xD0, 0xB5, 0xD0, 0xBC}; -static symbol s_5_33[6] = {0xD0, 0xB8, 0xD0, 0xB5, 0xD0, 0xBC}; -static symbol s_5_34[4] = {0xD0, 0xBE, 0xD0, 0xBC}; -static symbol s_5_35[2] = {0xD0, 0xBE}; - -static struct among a_5[36] = -{ - /* 0 */ {2, s_5_0, -1, 1, 0}, - /* 1 */ {4, s_5_1, -1, 1, 0}, - /* 2 */ {6, s_5_2, 1, 1, 0}, - /* 3 */ {4, s_5_3, -1, 1, 0}, - /* 4 */ {2, s_5_4, -1, 1, 0}, - /* 5 */ {2, s_5_5, -1, 1, 0}, - /* 6 */ {2, s_5_6, -1, 1, 0}, - /* 7 */ {4, s_5_7, 6, 1, 0}, - /* 8 */ {4, s_5_8, 6, 1, 0}, - /* 9 */ {2, s_5_9, -1, 1, 0}, - /* 10 */ {4, s_5_10, 9, 1, 0}, - /* 11 */ {4, s_5_11, 9, 1, 0}, - /* 12 */ {2, s_5_12, -1, 1, 0}, - /* 13 */ {4, s_5_13, -1, 1, 0}, - /* 14 */ {4, s_5_14, -1, 1, 0}, - /* 15 */ {2, s_5_15, -1, 1, 0}, - /* 16 */ {4, s_5_16, 15, 1, 0}, - /* 17 */ {4, s_5_17, 15, 1, 0}, - /* 18 */ {2, s_5_18, -1, 1, 0}, - /* 19 */ {4, s_5_19, 18, 1, 0}, - /* 20 */ {4, s_5_20, 18, 1, 0}, - /* 21 */ {6, s_5_21, 18, 1, 0}, - /* 22 */ {8, s_5_22, 21, 1, 0}, - /* 23 */ {6, s_5_23, 18, 1, 0}, - /* 24 */ {2, s_5_24, -1, 1, 0}, - /* 25 */ {4, s_5_25, 24, 1, 0}, - /* 26 */ {6, s_5_26, 25, 1, 0}, - /* 27 */ {4, s_5_27, 24, 1, 0}, - /* 28 */ {4, s_5_28, 24, 1, 0}, - /* 29 */ {4, s_5_29, -1, 1, 0}, - /* 30 */ {6, s_5_30, 29, 1, 0}, - /* 31 */ {4, s_5_31, -1, 1, 0}, - /* 32 */ {4, s_5_32, -1, 1, 0}, - /* 33 */ {6, s_5_33, 32, 1, 0}, - /* 34 */ {4, s_5_34, -1, 1, 0}, - /* 35 */ {2, s_5_35, -1, 1, 0} -}; - -static symbol s_6_0[6] = {0xD0, 0xBE, 0xD1, 0x81, 0xD1, 0x82}; -static symbol s_6_1[8] = {0xD0, 0xBE, 0xD1, 0x81, 0xD1, 0x82, 0xD1, 0x8C}; - -static struct among a_6[2] = -{ - /* 0 */ {6, s_6_0, -1, 1, 0}, - /* 1 */ {8, s_6_1, -1, 1, 0} -}; - -static symbol s_7_0[6] = {0xD0, 0xB5, 0xD0, 0xB9, 0xD1, 0x88}; -static symbol s_7_1[2] = {0xD1, 0x8C}; -static symbol s_7_2[8] = {0xD0, 0xB5, 0xD0, 0xB9, 0xD1, 0x88, 0xD0, 0xB5}; -static symbol s_7_3[2] = {0xD0, 0xBD}; - -static struct among a_7[4] = -{ - /* 0 */ {6, s_7_0, -1, 1, 0}, - /* 1 */ {2, s_7_1, -1, 3, 0}, - /* 2 */ {8, s_7_2, -1, 1, 0}, - /* 3 */ {2, s_7_3, -1, 2, 0} -}; - -static unsigned char g_v[] = {33, 65, 8, 232}; - -static symbol s_0[] = {0xD0, 0xB0}; -static symbol s_1[] = {0xD1, 0x8F}; -static symbol s_2[] = {0xD0, 0xB0}; -static symbol s_3[] = {0xD1, 0x8F}; -static symbol s_4[] = {0xD0, 0xB0}; -static symbol s_5[] = {0xD1, 0x8F}; -static symbol s_6[] = {0xD0, 0xBD}; -static symbol s_7[] = {0xD0, 0xBD}; -static symbol s_8[] = {0xD0, 0xBD}; -static symbol s_9[] = {0xD0, 0xB8}; - -static int -r_mark_regions(struct SN_env * z) -{ - z->I[0] = z->l; - z->I[1] = z->l; - { - int c = z->c; /* do, line 61 */ - - while (1) - { /* gopast, line 62 */ - if (!(in_grouping_U(z, g_v, 1072, 1103))) - goto lab1; - break; - lab1: - { - int c = skip_utf8(z->p, z->c, 0, z->l, 1); - - if (c < 0) - goto lab0; - z->c = c; /* gopast, line 62 */ - } - } - z->I[0] = z->c; /* setmark pV, line 62 */ - while (1) - { /* gopast, line 62 */ - if (!(out_grouping_U(z, g_v, 1072, 1103))) - goto lab2; - break; - lab2: - { - int c = skip_utf8(z->p, z->c, 0, z->l, 1); - - if (c < 0) - goto lab0; - z->c = c; /* gopast, line 62 */ - } - } - while (1) - { /* gopast, line 63 */ - if (!(in_grouping_U(z, g_v, 1072, 1103))) - goto lab3; - break; - lab3: - { - int c = skip_utf8(z->p, z->c, 0, z->l, 1); - - if (c < 0) - goto lab0; - z->c = c; /* gopast, line 63 */ - } - } - while (1) - { /* gopast, line 63 */ - if (!(out_grouping_U(z, g_v, 1072, 1103))) - goto lab4; - break; - lab4: - { - int c = skip_utf8(z->p, z->c, 0, z->l, 1); - - if (c < 0) - goto lab0; - z->c = c; /* gopast, line 63 */ - } - } - z->I[1] = z->c; /* setmark p2, line 63 */ -lab0: - z->c = c; - } - return 1; -} - -static int -r_R2(struct SN_env * z) -{ - if (!(z->I[1] <= z->c)) - return 0; - return 1; -} - -static int -r_perfective_gerund(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 72 */ - among_var = find_among_b(z, a_0, 9); /* substring, line 72 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 72 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int m = z->l - z->c; - - (void) m; /* or, line 76 */ - if (!(eq_s_b(z, 2, s_0))) - goto lab1; - goto lab0; - lab1: - z->c = z->l - m; - if (!(eq_s_b(z, 2, s_1))) - return 0; - } - lab0: - { - int ret; - - ret = slice_del(z); /* delete, line 76 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_del(z); /* delete, line 83 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_adjective(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 88 */ - among_var = find_among_b(z, a_1, 26); /* substring, line 88 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 88 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 97 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_adjectival(struct SN_env * z) -{ - int among_var; - - { - int ret = r_adjective(z); - - if (ret == 0) - return 0; /* call adjective, line 102 */ - if (ret < 0) - return ret; - } - { - int m = z->l - z->c; - - (void) m; /* try, line 109 */ - z->ket = z->c; /* [, line 110 */ - among_var = find_among_b(z, a_2, 8); /* substring, line 110 */ - if (!(among_var)) - { - z->c = z->l - m; - goto lab0; - } - z->bra = z->c; /* ], line 110 */ - switch (among_var) - { - case 0: - { - z->c = z->l - m; - goto lab0; - } - case 1: - { - int m = z->l - z->c; - - (void) m; /* or, line 115 */ - if (!(eq_s_b(z, 2, s_2))) - goto lab2; - goto lab1; - lab2: - z->c = z->l - m; - if (!(eq_s_b(z, 2, s_3))) - { - z->c = z->l - m; - goto lab0; - } - } - lab1: - { - int ret; - - ret = slice_del(z); /* delete, line 115 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_del(z); /* delete, line 122 */ - if (ret < 0) - return ret; - } - break; - } -lab0: - ; - } - return 1; -} - -static int -r_reflexive(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 129 */ - among_var = find_among_b(z, a_3, 2); /* substring, line 129 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 129 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 132 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_verb(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 137 */ - among_var = find_among_b(z, a_4, 46); /* substring, line 137 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 137 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int m = z->l - z->c; - - (void) m; /* or, line 143 */ - if (!(eq_s_b(z, 2, s_4))) - goto lab1; - goto lab0; - lab1: - z->c = z->l - m; - if (!(eq_s_b(z, 2, s_5))) - return 0; - } - lab0: - { - int ret; - - ret = slice_del(z); /* delete, line 143 */ - if (ret < 0) - return ret; - } - break; - case 2: - { - int ret; - - ret = slice_del(z); /* delete, line 151 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_noun(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 160 */ - among_var = find_among_b(z, a_5, 36); /* substring, line 160 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 160 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 167 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_derivational(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 176 */ - among_var = find_among_b(z, a_6, 2); /* substring, line 176 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 176 */ - { - int ret = r_R2(z); - - if (ret == 0) - return 0; /* call R2, line 176 */ - if (ret < 0) - return ret; - } - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 179 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -static int -r_tidy_up(struct SN_env * z) -{ - int among_var; - - z->ket = z->c; /* [, line 184 */ - among_var = find_among_b(z, a_7, 4); /* substring, line 184 */ - if (!(among_var)) - return 0; - z->bra = z->c; /* ], line 184 */ - switch (among_var) - { - case 0: - return 0; - case 1: - { - int ret; - - ret = slice_del(z); /* delete, line 188 */ - if (ret < 0) - return ret; - } - z->ket = z->c; /* [, line 189 */ - if (!(eq_s_b(z, 2, s_6))) - return 0; - z->bra = z->c; /* ], line 189 */ - if (!(eq_s_b(z, 2, s_7))) - return 0; - { - int ret; - - ret = slice_del(z); /* delete, line 189 */ - if (ret < 0) - return ret; - } - break; - case 2: - if (!(eq_s_b(z, 2, s_8))) - return 0; - { - int ret; - - ret = slice_del(z); /* delete, line 192 */ - if (ret < 0) - return ret; - } - break; - case 3: - { - int ret; - - ret = slice_del(z); /* delete, line 194 */ - if (ret < 0) - return ret; - } - break; - } - return 1; -} - -extern int -russian_UTF_8_stem(struct SN_env * z) -{ - { - int c = z->c; /* do, line 201 */ - - { - int ret = r_mark_regions(z); - - if (ret == 0) - goto lab0; /* call mark_regions, line 201 */ - if (ret < 0) - return ret; - } -lab0: - z->c = c; - } - z->lb = z->c; - z->c = z->l; /* backwards, line 202 */ - - { - int m3; /* setlimit, line 202 */ - int m = z->l - z->c; - - (void) m; - if (z->c < z->I[0]) - return 0; - z->c = z->I[0]; /* tomark, line 202 */ - m3 = z->lb; - z->lb = z->c; - z->c = z->l - m; - { - int m = z->l - z->c; - - (void) m; /* do, line 203 */ - { - int m = z->l - z->c; - - (void) m; /* or, line 204 */ - { - int ret = r_perfective_gerund(z); - - if (ret == 0) - goto lab3; /* call perfective_gerund, line 204 */ - if (ret < 0) - return ret; - } - goto lab2; - lab3: - z->c = z->l - m; - { - int m = z->l - z->c; - - (void) m; /* try, line 205 */ - { - int ret = r_reflexive(z); - - if (ret == 0) - { - z->c = z->l - m; - goto lab4; - } /* call reflexive, line 205 */ - if (ret < 0) - return ret; - } - lab4: - ; - } - { - int m = z->l - z->c; - - (void) m; /* or, line 206 */ - { - int ret = r_adjectival(z); - - if (ret == 0) - goto lab6; /* call adjectival, line 206 */ - if (ret < 0) - return ret; - } - goto lab5; - lab6: - z->c = z->l - m; - { - int ret = r_verb(z); - - if (ret == 0) - goto lab7; /* call verb, line 206 */ - if (ret < 0) - return ret; - } - goto lab5; - lab7: - z->c = z->l - m; - { - int ret = r_noun(z); - - if (ret == 0) - goto lab1; /* call noun, line 206 */ - if (ret < 0) - return ret; - } - } - lab5: - ; - } - lab2: - lab1: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* try, line 209 */ - z->ket = z->c; /* [, line 209 */ - if (!(eq_s_b(z, 2, s_9))) - { - z->c = z->l - m; - goto lab8; - } - z->bra = z->c; /* ], line 209 */ - { - int ret; - - ret = slice_del(z); /* delete, line 209 */ - if (ret < 0) - return ret; - } - lab8: - ; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 212 */ - { - int ret = r_derivational(z); - - if (ret == 0) - goto lab9; /* call derivational, line 212 */ - if (ret < 0) - return ret; - } - lab9: - z->c = z->l - m; - } - { - int m = z->l - z->c; - - (void) m; /* do, line 213 */ - { - int ret = r_tidy_up(z); - - if (ret == 0) - goto lab10; /* call tidy_up, line 213 */ - if (ret < 0) - return ret; - } - lab10: - z->c = z->l - m; - } - z->lb = m3; - } - z->c = z->lb; - return 1; -} - -extern struct SN_env *russian_UTF_8_create_env(void) -{ - return SN_create_env(0, 2, 0); -} - -extern void russian_UTF_8_close_env(struct SN_env * z) -{ - SN_close_env(z); -} diff --git a/contrib/tsearch2/snowball/russian_stem_UTF8.h b/contrib/tsearch2/snowball/russian_stem_UTF8.h deleted file mode 100644 index 0beb0b9719a9..000000000000 --- a/contrib/tsearch2/snowball/russian_stem_UTF8.h +++ /dev/null @@ -1,17 +0,0 @@ - -/* This file was generated automatically by the Snowball to ANSI C compiler */ - -#ifdef __cplusplus -extern "C" -{ -#endif - - extern struct SN_env *russian_UTF_8_create_env(void); - extern void russian_UTF_8_close_env(struct SN_env * z); - - extern int russian_UTF_8_stem(struct SN_env * z); - -#ifdef __cplusplus -} - -#endif diff --git a/contrib/tsearch2/snowball/utilities.c b/contrib/tsearch2/snowball/utilities.c deleted file mode 100644 index f06e5bb7a17c..000000000000 --- a/contrib/tsearch2/snowball/utilities.c +++ /dev/null @@ -1,656 +0,0 @@ - -#include -#include -#include - -#include "header.h" - -#define unless(C) if(!(C)) - -#define CREATE_SIZE 1 - -extern symbol * -create_s(void) -{ - symbol *p; - void *mem = malloc(HEAD + (CREATE_SIZE + 1) * sizeof(symbol)); - - if (mem == NULL) - return NULL; - p = (symbol *) (HEAD + (char *) mem); - CAPACITY(p) = CREATE_SIZE; - SET_SIZE(p, CREATE_SIZE); - return p; -} - -extern void -lose_s(symbol * p) -{ - if (p == NULL) - return; - free((char *) p - HEAD); -} - -/* - new_p = X_skip_utf8(p, c, lb, l, n); skips n characters forwards from p + c - if n +ve, or n characters backwards from p +c - 1 if n -ve. new_p is the new - position, or 0 on failure. - - -- used to implement hop and next in the utf8 case. -*/ - -extern int -skip_utf8(const symbol * p, int c, int lb, int l, int n) -{ - int b; - - if (n >= 0) - { - for (; n > 0; n--) - { - if (c >= l) - return -1; - b = p[c++]; - if (b >= 0xC0) - { /* 1100 0000 */ - while (c < l) - { - b = p[c]; - if (b >= 0xC0 || b < 0x80) - break; - /* break unless b is 10------ */ - c++; - } - } - } - } - else - { - for (; n < 0; n++) - { - if (c <= lb) - return -1; - b = p[--c]; - if (b >= 0x80) - { /* 1000 0000 */ - while (c > lb) - { - b = p[c]; - if (b >= 0xC0) - break; /* 1100 0000 */ - c--; - } - } - } - } - return c; -} - -/* Code for character groupings: utf8 cases */ - -static int -get_utf8(const symbol * p, int c, int l, int *slot) -{ - int b0, - b1; - - if (c >= l) - return 0; - b0 = p[c++]; - if (b0 < 0xC0 || c == l) - { /* 1100 0000 */ - *slot = b0; - return 1; - } - b1 = p[c++]; - if (b0 < 0xE0 || c == l) - { /* 1110 0000 */ - *slot = (b0 & 0x1F) << 6 | (b1 & 0x3F); - return 2; - } - *slot = (b0 & 0xF) << 12 | (b1 & 0x3F) << 6 | (*p & 0x3F); - return 3; -} - -static int -get_b_utf8(const symbol * p, int c, int lb, int *slot) -{ - int b0, - b1; - - if (c <= lb) - return 0; - b0 = p[--c]; - if (b0 < 0x80 || c == lb) - { /* 1000 0000 */ - *slot = b0; - return 1; - } - b1 = p[--c]; - if (b1 >= 0xC0 || c == lb) - { /* 1100 0000 */ - *slot = (b1 & 0x1F) << 6 | (b0 & 0x3F); - return 2; - } - *slot = (*p & 0xF) << 12 | (b1 & 0x3F) << 6 | (b0 & 0x3F); - return 3; -} - -extern int -in_grouping_U(struct SN_env * z, unsigned char *s, int min, int max) -{ - int ch; - int w = get_utf8(z->p, z->c, z->l, &ch); - - unless(w) return 0; - if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) - return 0; - z->c += w; - return 1; -} - -extern int -in_grouping_b_U(struct SN_env * z, unsigned char *s, int min, int max) -{ - int ch; - int w = get_b_utf8(z->p, z->c, z->lb, &ch); - - unless(w) return 0; - if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) - return 0; - z->c -= w; - return 1; -} - -extern int -out_grouping_U(struct SN_env * z, unsigned char *s, int min, int max) -{ - int ch; - int w = get_utf8(z->p, z->c, z->l, &ch); - - unless(w) return 0; - unless(ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0; - z->c += w; - return 1; -} - -extern int -out_grouping_b_U(struct SN_env * z, unsigned char *s, int min, int max) -{ - int ch; - int w = get_b_utf8(z->p, z->c, z->lb, &ch); - - unless(w) return 0; - unless(ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0; - z->c -= w; - return 1; -} - -/* Code for character groupings: non-utf8 cases */ - -extern int -in_grouping(struct SN_env * z, unsigned char *s, int min, int max) -{ - int ch; - - if (z->c >= z->l) - return 0; - ch = z->p[z->c]; - if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) - return 0; - z->c++; - return 1; -} - -extern int -in_grouping_b(struct SN_env * z, unsigned char *s, int min, int max) -{ - int ch; - - if (z->c <= z->lb) - return 0; - ch = z->p[z->c - 1]; - if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) - return 0; - z->c--; - return 1; -} - -extern int -out_grouping(struct SN_env * z, unsigned char *s, int min, int max) -{ - int ch; - - if (z->c >= z->l) - return 0; - ch = z->p[z->c]; - unless(ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0; - z->c++; - return 1; -} - -extern int -out_grouping_b(struct SN_env * z, unsigned char *s, int min, int max) -{ - int ch; - - if (z->c <= z->lb) - return 0; - ch = z->p[z->c - 1]; - unless(ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0; - z->c--; - return 1; -} - -extern int -eq_s(struct SN_env * z, int s_size, symbol * s) -{ - if (z->l - z->c < s_size || memcmp(z->p + z->c, s, s_size * sizeof(symbol)) != 0) - return 0; - z->c += s_size; - return 1; -} - -extern int -eq_s_b(struct SN_env * z, int s_size, symbol * s) -{ - if (z->c - z->lb < s_size || memcmp(z->p + z->c - s_size, s, s_size * sizeof(symbol)) != 0) - return 0; - z->c -= s_size; - return 1; -} - -extern int -eq_v(struct SN_env * z, symbol * p) -{ - return eq_s(z, SIZE(p), p); -} - -extern int -eq_v_b(struct SN_env * z, symbol * p) -{ - return eq_s_b(z, SIZE(p), p); -} - -extern int -find_among(struct SN_env * z, struct among * v, int v_size) -{ - - int i = 0; - int j = v_size; - - int c = z->c; - int l = z->l; - symbol *q = z->p + c; - - struct among *w; - - int common_i = 0; - int common_j = 0; - - int first_key_inspected = 0; - - while (1) - { - int k = i + ((j - i) >> 1); - int diff = 0; - int common = common_i < common_j ? common_i : common_j; /* smaller */ - - w = v + k; - { - int i; - - for (i = common; i < w->s_size; i++) - { - if (c + common == l) - { - diff = -1; - break; - } - diff = q[common] - w->s[i]; - if (diff != 0) - break; - common++; - } - } - if (diff < 0) - { - j = k; - common_j = common; - } - else - { - i = k; - common_i = common; - } - if (j - i <= 1) - { - if (i > 0) - break; /* v->s has been inspected */ - if (j == i) - break; /* only one item in v */ - - /* - * - but now we need to go round once more to get v->s inspected. - * This looks messy, but is actually the optimal approach. - */ - - if (first_key_inspected) - break; - first_key_inspected = 1; - } - } - while (1) - { - w = v + i; - if (common_i >= w->s_size) - { - z->c = c + w->s_size; - if (w->function == 0) - return w->result; - { - int res = w->function(z); - - z->c = c + w->s_size; - if (res) - return w->result; - } - } - i = w->substring_i; - if (i < 0) - return 0; - } -} - -/* find_among_b is for backwards processing. Same comments apply */ - -extern int -find_among_b(struct SN_env * z, struct among * v, int v_size) -{ - - int i = 0; - int j = v_size; - - int c = z->c; - int lb = z->lb; - symbol *q = z->p + c - 1; - - struct among *w; - - int common_i = 0; - int common_j = 0; - - int first_key_inspected = 0; - - while (1) - { - int k = i + ((j - i) >> 1); - int diff = 0; - int common = common_i < common_j ? common_i : common_j; - - w = v + k; - { - int i; - - for (i = w->s_size - 1 - common; i >= 0; i--) - { - if (c - common == lb) - { - diff = -1; - break; - } - diff = q[-common] - w->s[i]; - if (diff != 0) - break; - common++; - } - } - if (diff < 0) - { - j = k; - common_j = common; - } - else - { - i = k; - common_i = common; - } - if (j - i <= 1) - { - if (i > 0) - break; - if (j == i) - break; - if (first_key_inspected) - break; - first_key_inspected = 1; - } - } - while (1) - { - w = v + i; - if (common_i >= w->s_size) - { - z->c = c - w->s_size; - if (w->function == 0) - return w->result; - { - int res = w->function(z); - - z->c = c - w->s_size; - if (res) - return w->result; - } - } - i = w->substring_i; - if (i < 0) - return 0; - } -} - - -/* Increase the size of the buffer pointed to by p to at least n symbols. - * If insufficient memory, returns NULL and frees the old buffer. - */ -static symbol * -increase_size(symbol * p, int n) -{ - symbol *q; - int new_size = n + 20; - void *mem = realloc((char *) p - HEAD, - HEAD + (new_size + 1) * sizeof(symbol)); - - if (mem == NULL) - { - lose_s(p); - return NULL; - } - q = (symbol *) (HEAD + (char *) mem); - CAPACITY(q) = new_size; - return q; -} - -/* to replace symbols between c_bra and c_ket in z->p by the - s_size symbols at s. - Returns 0 on success, -1 on error. - Also, frees z->p (and sets it to NULL) on error. -*/ -extern int -replace_s(struct SN_env * z, int c_bra, int c_ket, int s_size, const symbol * s, int *adjptr) -{ - int adjustment; - int len; - - if (z->p == NULL) - { - z->p = create_s(); - if (z->p == NULL) - return -1; - } - adjustment = s_size - (c_ket - c_bra); - len = SIZE(z->p); - if (adjustment != 0) - { - if (adjustment + len > CAPACITY(z->p)) - { - z->p = increase_size(z->p, adjustment + len); - if (z->p == NULL) - return -1; - } - memmove(z->p + c_ket + adjustment, - z->p + c_ket, - (len - c_ket) * sizeof(symbol)); - SET_SIZE(z->p, adjustment + len); - z->l += adjustment; - if (z->c >= c_ket) - z->c += adjustment; - else if (z->c > c_bra) - z->c = c_bra; - } - unless(s_size == 0) memmove(z->p + c_bra, s, s_size * sizeof(symbol)); - if (adjptr != NULL) - *adjptr = adjustment; - return 0; -} - -static int -slice_check(struct SN_env * z) -{ - - if (z->bra < 0 || - z->bra > z->ket || - z->ket > z->l || - z->p == NULL || - z->l > SIZE(z->p)) /* this line could be removed */ - { -#if 0 - fprintf(stderr, "faulty slice operation:\n"); - debug(z, -1, 0); -#endif - return -1; - } - return 0; -} - -extern int -slice_from_s(struct SN_env * z, int s_size, symbol * s) -{ - if (slice_check(z)) - return -1; - return replace_s(z, z->bra, z->ket, s_size, s, NULL); -} - -extern int -slice_from_v(struct SN_env * z, symbol * p) -{ - return slice_from_s(z, SIZE(p), p); -} - -extern int -slice_del(struct SN_env * z) -{ - return slice_from_s(z, 0, 0); -} - -extern int -insert_s(struct SN_env * z, int bra, int ket, int s_size, symbol * s) -{ - int adjustment; - - if (replace_s(z, bra, ket, s_size, s, &adjustment)) - return -1; - if (bra <= z->bra) - z->bra += adjustment; - if (bra <= z->ket) - z->ket += adjustment; - return 0; -} - -extern int -insert_v(struct SN_env * z, int bra, int ket, symbol * p) -{ - int adjustment; - - if (replace_s(z, bra, ket, SIZE(p), p, &adjustment)) - return -1; - if (bra <= z->bra) - z->bra += adjustment; - if (bra <= z->ket) - z->ket += adjustment; - return 0; -} - -extern symbol * -slice_to(struct SN_env * z, symbol * p) -{ - if (slice_check(z)) - { - lose_s(p); - return NULL; - } - { - int len = z->ket - z->bra; - - if (CAPACITY(p) < len) - { - p = increase_size(p, len); - if (p == NULL) - return NULL; - } - memmove(p, z->p + z->bra, len * sizeof(symbol)); - SET_SIZE(p, len); - } - return p; -} - -extern symbol * -assign_to(struct SN_env * z, symbol * p) -{ - int len = z->l; - - if (CAPACITY(p) < len) - { - p = increase_size(p, len); - if (p == NULL) - return NULL; - } - memmove(p, z->p, len * sizeof(symbol)); - SET_SIZE(p, len); - return p; -} - -#if 0 -extern void -debug(struct SN_env * z, int number, int line_count) -{ - int i; - int limit = SIZE(z->p); - - /* if (number >= 0) printf("%3d (line %4d): '", number, line_count); */ - if (number >= 0) - printf("%3d (line %4d): [%d]'", number, line_count, limit); - for (i = 0; i <= limit; i++) - { - if (z->lb == i) - printf("{"); - if (z->bra == i) - printf("["); - if (z->c == i) - printf("|"); - if (z->ket == i) - printf("]"); - if (z->l == i) - printf("}"); - if (i < limit) - { - int ch = z->p[i]; - - if (ch == 0) - ch = '#'; - printf("%c", ch); - } - } - printf("'\n"); -} - -#endif diff --git a/contrib/tsearch2/sql/tsearch2.sql b/contrib/tsearch2/sql/tsearch2.sql index 27b29661d27e..8f95affc1a3b 100644 --- a/contrib/tsearch2/sql/tsearch2.sql +++ b/contrib/tsearch2/sql/tsearch2.sql @@ -90,9 +90,9 @@ Moscow moskva | moscow \set ECHO all alter table test_tsquery add column keyword tsquery; -update test_tsquery set keyword = to_tsquery('default', txtkeyword); +update test_tsquery set keyword = to_tsquery('english', txtkeyword); alter table test_tsquery add column sample tsquery; -update test_tsquery set sample = to_tsquery('default', txtsample::text); +update test_tsquery set sample = to_tsquery('english', txtsample::text); create unique index bt_tsq on test_tsquery (keyword); @@ -127,12 +127,12 @@ select keyword from test_tsquery where keyword @> 'new'; select keyword from test_tsquery where keyword @> 'moscow'; select keyword from test_tsquery where keyword <@ 'new'; select keyword from test_tsquery where keyword <@ 'moscow'; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow & hotel') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'bar & new & qq & foo & york') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow') as query where query @> keyword; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow & hotel') as query where query @> keyword; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'bar & new & qq & foo & york') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where query @> keyword; create index qq on test_tsquery using gist (keyword gist_tp_tsquery_ops); set enable_seqscan='off'; @@ -141,48 +141,48 @@ select keyword from test_tsquery where keyword @> 'new'; select keyword from test_tsquery where keyword @> 'moscow'; select keyword from test_tsquery where keyword <@ 'new'; select keyword from test_tsquery where keyword <@ 'moscow'; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow & hotel') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'bar & new & qq & foo & york') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow') as query where query @> keyword; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'moscow & hotel') as query where query @> keyword; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('default', 'bar & new & qq & foo & york') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where keyword <@ query; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where query @> keyword; +select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where query @> keyword; set enable_seqscan='on'; select lexize('simple', 'ASD56 hsdkf'); -select lexize('en_stem', 'SKIES Problems identity'); +select lexize('english_stem', 'SKIES Problems identity'); select * from token_type('default'); select * from parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty'); -SELECT to_tsvector('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 +SELECT to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty'); -SELECT length(to_tsvector('default', '345 qw')); +SELECT length(to_tsvector('english', '345 qw')); -SELECT length(to_tsvector('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 +SELECT length(to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty')); -select to_tsquery('default', 'qwe & sKies '); +select to_tsquery('english', 'qwe & sKies '); select to_tsquery('simple', 'qwe & sKies '); -select to_tsquery('default', '''the wether'':dc & '' sKies '':BC '); -select to_tsquery('default', 'asd&(and|fghj)'); -select to_tsquery('default', '(asd&and)|fghj'); -select to_tsquery('default', '(asd&!and)|fghj'); -select to_tsquery('default', '(the|and&(i&1))&fghj'); - -select plainto_tsquery('default', 'the and z 1))& fghj'); -select plainto_tsquery('default', 'foo bar') && plainto_tsquery('default', 'asd'); -select plainto_tsquery('default', 'foo bar') || plainto_tsquery('default', 'asd fg'); -select plainto_tsquery('default', 'foo bar') || !!plainto_tsquery('default', 'asd fg'); -select plainto_tsquery('default', 'foo bar') && 'asd | fg'; +select to_tsquery('english', '''the wether'':dc & '' sKies '':BC '); +select to_tsquery('english', 'asd&(and|fghj)'); +select to_tsquery('english', '(asd&and)|fghj'); +select to_tsquery('english', '(asd&!and)|fghj'); +select to_tsquery('english', '(the|and&(i&1))&fghj'); + +select plainto_tsquery('english', 'the and z 1))& fghj'); +select plainto_tsquery('english', 'foo bar') && plainto_tsquery('english', 'asd'); +select plainto_tsquery('english', 'foo bar') || plainto_tsquery('english', 'asd fg'); +select plainto_tsquery('english', 'foo bar') || !!plainto_tsquery('english', 'asd fg'); +select plainto_tsquery('english', 'foo bar') && 'asd | fg'; select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca'; select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:B'; @@ -211,7 +211,7 @@ SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; -select set_curcfg('default'); +select set_curcfg('english'); CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON test_tsvector @@ -227,11 +227,7 @@ UPDATE test_tsvector SET t = null WHERE t = '345 qwerty'; SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); -drop trigger tsvectorupdate on test_tsvector; -create function wow(text) returns text as 'select $1 || '' copyright''; ' language sql; -create trigger tsvectorupdate before update or insert on test_tsvector -for each row execute procedure tsearch2(a, wow, t); -insert into test_tsvector (t) values ('345 qwerty'); +insert into test_tsvector (t) values ('345 qwerty copyright'); select count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); select count(*) FROM test_tsvector WHERE a @@ to_tsquery('copyright'); @@ -253,8 +249,7 @@ select * from stat('select a from test_tsvector','c') order by ndoc desc, nentry select * from stat('select a from test_tsvector','d') order by ndoc desc, nentry desc, word; select * from stat('select a from test_tsvector','ad') order by ndoc desc, nentry desc, word; -select reset_tsearch(); -select to_tsquery('default', 'skies & books'); +select to_tsquery('english', 'skies & books'); select rank_cd(to_tsvector('Erosion It took the sea a thousand years, A thousand years to trace @@ -286,36 +281,6 @@ The sculpture of these granite seams, Upon a woman s face. E. J. Pratt (1882 1964) '), to_tsquery('sea')); -select get_covers(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('sea&thousand&years')); - -select get_covers(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('granite&sea')); - -select get_covers(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('sea')); - select headline('Erosion It took the sea a thousand years, A thousand years to trace The granite features of this cliff @@ -361,7 +326,7 @@ ff-bg ', to_tsquery('sea&foo'), 'HighlightAll=true'); --check debug -select * from ts_debug('Tsearch module for PostgreSQL 7.3.3'); +select * from public.ts_debug('Tsearch module for PostgreSQL 7.3.3'); --check ordering insert into test_tsvector values (null, null); diff --git a/contrib/tsearch2/stopword.c b/contrib/tsearch2/stopword.c deleted file mode 100644 index 5f6d56bce30f..000000000000 --- a/contrib/tsearch2/stopword.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * stopword library - * Teodor Sigaev - */ -#include "postgres.h" - -#include "common.h" -#include "dict.h" -#include "ts_locale.h" - -#define STOPBUFLEN 4096 - -void -freestoplist(StopList * s) -{ - char **ptr = s->stop; - - if (ptr) - while (*ptr && s->len > 0) - { - free(*ptr); - ptr++; - s->len--; - free(s->stop); - } - memset(s, 0, sizeof(StopList)); -} - -void -readstoplist(text *in, StopList * s) -{ - char **stop = NULL; - - s->len = 0; - if (in && VARSIZE(in) - VARHDRSZ > 0) - { - char *filename = to_absfilename(text2char(in)); - FILE *hin; - char buf[STOPBUFLEN], *pbuf; - int reallen = 0; - - if ((hin = fopen(filename, "r")) == NULL) - ereport(ERROR, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("could not open file \"%s\": %m", - filename))); - - while (fgets(buf, sizeof(buf), hin)) - { - pbuf = buf; - while( *pbuf && !isspace((unsigned char) *pbuf ) ) - pbuf++; - *pbuf = '\0'; - - pg_verifymbstr(buf, strlen(buf), false); - if (*buf == '\0' || *buf=='\n' || *buf=='\r') - continue; - - if (s->len >= reallen) - { - char **tmp; - - reallen = (reallen) ? reallen * 2 : 16; - tmp = (char **) realloc((void *) stop, sizeof(char *) * reallen); - if (!tmp) - { - freestoplist(s); - fclose(hin); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - stop = tmp; - } - - if (s->wordop) - { - pbuf = s->wordop(buf); - stop[s->len] = strdup(pbuf); - pfree(pbuf); - } else - stop[s->len] = strdup(buf); - - if (!stop[s->len]) - { - freestoplist(s); - fclose(hin); - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - - (s->len)++; - } - fclose(hin); - pfree(filename); - } - s->stop = stop; -} - -static int -comparestr(const void *a, const void *b) -{ - return strcmp(*(char **) a, *(char **) b); -} - -void -sortstoplist(StopList * s) -{ - if (s->stop && s->len > 0) - qsort(s->stop, s->len, sizeof(char *), comparestr); -} - -bool -searchstoplist(StopList * s, char *key) -{ - return (s->stop && s->len > 0 && bsearch(&key, s->stop, s->len, sizeof(char *), comparestr)) ? true : false; -} diff --git a/contrib/tsearch2/stopword/russian.stop b/contrib/tsearch2/stopword/russian.stop deleted file mode 100755 index 1877e3ab5bee..000000000000 --- a/contrib/tsearch2/stopword/russian.stop +++ /dev/null @@ -1,151 +0,0 @@ -É -× -×Ï -ÎÅ -ÞÔÏ -ÏÎ -ÎÁ -Ñ -Ó -ÓÏ -ËÁË -Á -ÔÏ -×ÓÅ -ÏÎÁ -ÔÁË -ÅÇÏ -ÎÏ -ÄÁ -ÔÙ -Ë -Õ -ÖÅ -×Ù -ÚÁ -ÂÙ -ÐÏ -ÔÏÌØËÏ -ÅÅ -ÍÎÅ -ÂÙÌÏ -×ÏÔ -ÏÔ -ÍÅÎÑ -ÅÝÅ -ÎÅÔ -Ï -ÉÚ -ÅÍÕ -ÔÅÐÅÒØ -ËÏÇÄÁ -ÄÁÖÅ -ÎÕ -×ÄÒÕÇ -ÌÉ -ÅÓÌÉ -ÕÖÅ -ÉÌÉ -ÎÉ -ÂÙÔØ -ÂÙÌ -ÎÅÇÏ -ÄÏ -×ÁÓ -ÎÉÂÕÄØ -ÏÐÑÔØ -ÕÖ -×ÁÍ -×ÅÄØ -ÔÁÍ -ÐÏÔÏÍ -ÓÅÂÑ -ÎÉÞÅÇÏ -ÅÊ -ÍÏÖÅÔ -ÏÎÉ -ÔÕÔ -ÇÄÅ -ÅÓÔØ -ÎÁÄÏ -ÎÅÊ -ÄÌÑ -ÍÙ -ÔÅÂÑ -ÉÈ -ÞÅÍ -ÂÙÌÁ -ÓÁÍ -ÞÔÏ -ÂÅÚ -ÂÕÄÔÏ -ÞÅÇÏ -ÒÁÚ -ÔÏÖÅ -ÓÅÂÅ -ÐÏÄ -ÂÕÄÅÔ -Ö -ÔÏÇÄÁ -ËÔÏ -ÜÔÏÔ -ÔÏÇÏ -ÐÏÔÏÍÕ -ÜÔÏÇÏ -ËÁËÏÊ -ÓÏ×ÓÅÍ -ÎÉÍ -ÚÄÅÓØ -ÜÔÏÍ -ÏÄÉÎ -ÐÏÞÔÉ -ÍÏÊ -ÔÅÍ -ÞÔÏÂÙ -ÎÅÅ -ÓÅÊÞÁÓ -ÂÙÌÉ -ËÕÄÁ -ÚÁÞÅÍ -×ÓÅÈ -ÎÉËÏÇÄÁ -ÍÏÖÎÏ -ÐÒÉ -ÎÁËÏÎÅà -Ä×Á -Ï -ÄÒÕÇÏÊ -ÈÏÔØ -ÐÏÓÌÅ -ÎÁÄ -ÂÏÌØÛÅ -ÔÏÔ -ÞÅÒÅÚ -ÜÔÉ -ÎÁÓ -ÐÒÏ -×ÓÅÇÏ -ÎÉÈ -ËÁËÁÑ -ÍÎÏÇÏ -ÒÁÚ×Å -ÔÒÉ -ÜÔÕ -ÍÏÑ -×ÐÒÏÞÅÍ -ÈÏÒÏÛÏ -Ó×ÏÀ -ÜÔÏÊ -ÐÅÒÅÄ -ÉÎÏÇÄÁ -ÌÕÞÛÅ -ÞÕÔØ -ÔÏÍ -ÎÅÌØÚÑ -ÔÁËÏÊ -ÉÍ -ÂÏÌÅÅ -×ÓÅÇÄÁ -ËÏÎÅÞÎÏ -×ÓÀ -ÍÅÖÄÕ diff --git a/contrib/tsearch2/thesaurus b/contrib/tsearch2/thesaurus deleted file mode 100755 index 95423508c118..000000000000 --- a/contrib/tsearch2/thesaurus +++ /dev/null @@ -1,21 +0,0 @@ -# -# Theasurus config file. Character ':' splits -# string to part, example: -# sample-words : substitute-words -# -# Any substitute-word can be marked by preceding '*' character, -# which means do not lexize this word -# Docs: http://www.sai.msu.su/~megera/oddmuse/index.cgi/Thesaurus_dictionary - -#one two three : *123 -#one two : *12 -#one : *1 -#two : *2 - -#foo bar : blah blah -#f bar : fbar -#e bar : ebar -#g bar bar : gbarbar -#asd:sdffff -#qwerty:qwer wert erty - diff --git a/contrib/tsearch2/ts_cfg.c b/contrib/tsearch2/ts_cfg.c deleted file mode 100644 index db85711e8889..000000000000 --- a/contrib/tsearch2/ts_cfg.c +++ /dev/null @@ -1,648 +0,0 @@ -/* - * interface functions to tscfg - * Teodor Sigaev - */ -#include "postgres.h" - -#include -#include - -#include "catalog/pg_type.h" -#include "executor/spi.h" -#include "fmgr.h" -#include "utils/array.h" -#include "utils/memutils.h" - -#include "ts_cfg.h" -#include "dict.h" -#include "wparser.h" -#include "snmap.h" -#include "common.h" -#include "tsvector.h" - -PG_MODULE_MAGIC; - -#define IGNORE_LONGLEXEME 1 - -/*********top interface**********/ - -static Oid current_cfg_id = 0; - -void -init_cfg(Oid id, TSCfgInfo * cfg) -{ - Oid arg[2]; - bool isnull; - Datum pars[2]; - int stat, - i, - j; - text *ptr; - text *prsname = NULL; - char *nsp = get_namespace(TSNSP_FunctionOid); - char buf[1024]; - MemoryContext oldcontext; - void *plan; - - arg[0] = OIDOID; - arg[1] = OIDOID; - pars[0] = ObjectIdGetDatum(id); - pars[1] = ObjectIdGetDatum(id); - - memset(cfg, 0, sizeof(TSCfgInfo)); - SPI_connect(); - - sprintf(buf, "select prs_name from %s.pg_ts_cfg where oid = $1", nsp); - plan = SPI_prepare(buf, 1, arg); - if (!plan) - ts_error(ERROR, "SPI_prepare() failed"); - - stat = SPI_execp(plan, pars, " ", 1); - if (stat < 0) - ts_error(ERROR, "SPI_execp return %d", stat); - if (SPI_processed > 0) - { - prsname = DatumGetTextP(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); - oldcontext = MemoryContextSwitchTo(TopMemoryContext); - prsname = ptextdup(prsname); - MemoryContextSwitchTo(oldcontext); - - cfg->id = id; - } - else - ts_error(ERROR, "No tsearch cfg with id %d", id); - - SPI_freeplan(plan); - - arg[0] = TEXTOID; - sprintf(buf, "select lt.tokid, map.dict_name from %s.pg_ts_cfgmap as map, %s.pg_ts_cfg as cfg, %s.token_type( $1 ) as lt where lt.alias = map.tok_alias and map.ts_name = cfg.ts_name and cfg.oid= $2 order by lt.tokid desc;", nsp, nsp, nsp); - plan = SPI_prepare(buf, 2, arg); - if (!plan) - ts_error(ERROR, "SPI_prepare() failed"); - - pars[0] = PointerGetDatum(prsname); - stat = SPI_execp(plan, pars, " ", 0); - if (stat < 0) - ts_error(ERROR, "SPI_execp return %d", stat); - if (SPI_processed <= 0) - ts_error(ERROR, "No parser with id %d", id); - - for (i = 0; i < SPI_processed; i++) - { - int lexid = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull)); - ArrayType *toasted_a = (ArrayType *) DatumGetPointer(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull)); - ArrayType *a; - - if (!cfg->map) - { - cfg->len = lexid + 1; - cfg->map = (ListDictionary *) malloc(sizeof(ListDictionary) * cfg->len); - if (!cfg->map) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - memset(cfg->map, 0, sizeof(ListDictionary) * cfg->len); - } - - if (isnull) - continue; - - a = (ArrayType *) PG_DETOAST_DATUM(PointerGetDatum(toasted_a)); - - if (ARR_NDIM(a) != 1) - ts_error(ERROR, "Wrong dimension"); - if (ARRNELEMS(a) < 1) - continue; - if (ARR_HASNULL(a)) - ts_error(ERROR, "Array must not contain nulls"); - - cfg->map[lexid].len = ARRNELEMS(a); - cfg->map[lexid].dict_id = (Datum *) malloc(sizeof(Datum) * cfg->map[lexid].len); - if (!cfg->map[lexid].dict_id) - ts_error(ERROR, "No memory"); - - memset(cfg->map[lexid].dict_id, 0, sizeof(Datum) * cfg->map[lexid].len); - ptr = (text *) ARR_DATA_PTR(a); - oldcontext = MemoryContextSwitchTo(TopMemoryContext); - for (j = 0; j < cfg->map[lexid].len; j++) - { - cfg->map[lexid].dict_id[j] = PointerGetDatum(ptextdup(ptr)); - ptr = NEXTVAL(ptr); - } - MemoryContextSwitchTo(oldcontext); - - if (a != toasted_a) - pfree(a); - } - - SPI_freeplan(plan); - SPI_finish(); - cfg->prs_id = name2id_prs(prsname); - pfree(prsname); - pfree(nsp); - for (i = 0; i < cfg->len; i++) - { - for (j = 0; j < cfg->map[i].len; j++) - { - ptr = (text *) DatumGetPointer(cfg->map[i].dict_id[j]); - cfg->map[i].dict_id[j] = ObjectIdGetDatum(name2id_dict(ptr)); - pfree(ptr); - } - } -} - -typedef struct -{ - TSCfgInfo *last_cfg; - int len; - int reallen; - TSCfgInfo *list; - SNMap name2id_map; -} CFGList; - -static CFGList CList = {NULL, 0, 0, NULL, {0, 0, NULL}}; - -void -reset_cfg(void) -{ - freeSNMap(&(CList.name2id_map)); - if (CList.list) - { - int i, - j; - - for (i = 0; i < CList.len; i++) - if (CList.list[i].map) - { - for (j = 0; j < CList.list[i].len; j++) - if (CList.list[i].map[j].dict_id) - free(CList.list[i].map[j].dict_id); - free(CList.list[i].map); - } - free(CList.list); - } - memset(&CList, 0, sizeof(CFGList)); -} - -static int -comparecfg(const void *a, const void *b) -{ - if (((TSCfgInfo *) a)->id == ((TSCfgInfo *) b)->id) - return 0; - return (((TSCfgInfo *) a)->id < ((TSCfgInfo *) b)->id) ? -1 : 1; -} - -TSCfgInfo * -findcfg(Oid id) -{ - /* last used cfg */ - if (CList.last_cfg && CList.last_cfg->id == id) - return CList.last_cfg; - - /* already used cfg */ - if (CList.len != 0) - { - TSCfgInfo key; - - key.id = id; - CList.last_cfg = bsearch(&key, CList.list, CList.len, sizeof(TSCfgInfo), comparecfg); - if (CList.last_cfg != NULL) - return CList.last_cfg; - } - - /* last chance */ - if (CList.len == CList.reallen) - { - TSCfgInfo *tmp; - int reallen = (CList.reallen) ? 2 * CList.reallen : 16; - - tmp = (TSCfgInfo *) realloc(CList.list, sizeof(TSCfgInfo) * reallen); - if (!tmp) - ts_error(ERROR, "No memory"); - CList.reallen = reallen; - CList.list = tmp; - } - init_cfg(id, &(CList.list[CList.len]) ); - CList.last_cfg = &(CList.list[CList.len]); - CList.len++; - qsort(CList.list, CList.len, sizeof(TSCfgInfo), comparecfg); - return findcfg(id); /* qsort changed order!! */ ; -} - - -Oid -name2id_cfg(text *name) -{ - Oid arg[1]; - bool isnull; - Datum pars[1]; - int stat; - Oid id = findSNMap_t(&(CList.name2id_map), name); - void *plan; - char *nsp; - char buf[1024]; - - arg[0] = TEXTOID; - pars[0] = PointerGetDatum(name); - - if (id) - return id; - - nsp = get_namespace(TSNSP_FunctionOid); - SPI_connect(); - sprintf(buf, "select oid from %s.pg_ts_cfg where ts_name = $1", nsp); - plan = SPI_prepare(buf, 1, arg); - if (!plan) - /* internal error */ - elog(ERROR, "SPI_prepare() failed"); - - stat = SPI_execp(plan, pars, " ", 1); - if (stat < 0) - /* internal error */ - elog(ERROR, "SPI_execp return %d", stat); - if (SPI_processed > 0) - { - id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); - if (isnull) - ereport(ERROR, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("null id for tsearch config"))); - } - else - ereport(ERROR, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("no tsearch config"))); - - SPI_freeplan(plan); - SPI_finish(); - addSNMap_t(&(CList.name2id_map), name, id); - return id; -} - -void -parsetext_v2(TSCfgInfo * cfg, PRSTEXT * prs, char *buf, int4 buflen) -{ - int type, - lenlemm; - char *lemm = NULL; - WParserInfo *prsobj = findprs(cfg->prs_id); - LexizeData ldata; - TSLexeme *norms; - - prsobj->prs = (void *) DatumGetPointer( - FunctionCall2( - &(prsobj->start_info), - PointerGetDatum(buf), - Int32GetDatum(buflen) - ) - ); - - LexizeInit(&ldata, cfg); - - do - { - type = DatumGetInt32(FunctionCall3( - &(prsobj->getlexeme_info), - PointerGetDatum(prsobj->prs), - PointerGetDatum(&lemm), - PointerGetDatum(&lenlemm))); - - if (type > 0 && lenlemm >= MAXSTRLEN) - { -#ifdef IGNORE_LONGLEXEME - ereport(NOTICE, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("A word you are indexing is too long. It will be ignored."))); - continue; -#else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("A word you are indexing is too long"))); -#endif - } - - LexizeAddLemm(&ldata, type, lemm, lenlemm); - - while ((norms = LexizeExec(&ldata, NULL)) != NULL) - { - TSLexeme *ptr = norms; - - prs->pos++; /* set pos */ - - while (ptr->lexeme) - { - if (prs->curwords == prs->lenwords) - { - prs->lenwords *= 2; - prs->words = (TSWORD *) repalloc((void *) prs->words, prs->lenwords * sizeof(TSWORD)); - } - - if (ptr->flags & TSL_ADDPOS) - prs->pos++; - prs->words[prs->curwords].len = strlen(ptr->lexeme); - prs->words[prs->curwords].word = ptr->lexeme; - prs->words[prs->curwords].nvariant = ptr->nvariant; - prs->words[prs->curwords].alen = 0; - prs->words[prs->curwords].pos.pos = LIMITPOS(prs->pos); - ptr++; - prs->curwords++; - } - pfree(norms); - } - } while (type > 0); - - FunctionCall1( - &(prsobj->end_info), - PointerGetDatum(prsobj->prs) - ); -} - -static void -hladdword(HLPRSTEXT * prs, char *buf, int4 buflen, int type) -{ - while (prs->curwords >= prs->lenwords) - { - prs->lenwords *= 2; - prs->words = (HLWORD *) repalloc((void *) prs->words, prs->lenwords * sizeof(HLWORD)); - } - memset(&(prs->words[prs->curwords]), 0, sizeof(HLWORD)); - prs->words[prs->curwords].type = (uint8) type; - prs->words[prs->curwords].len = buflen; - prs->words[prs->curwords].word = palloc(buflen); - memcpy(prs->words[prs->curwords].word, buf, buflen); - prs->curwords++; -} - -static void -hlfinditem(HLPRSTEXT * prs, QUERYTYPE * query, char *buf, int buflen) -{ - int i; - ITEM *item = GETQUERY(query); - HLWORD *word; - - while (prs->curwords + query->size >= prs->lenwords) - { - prs->lenwords *= 2; - prs->words = (HLWORD *) repalloc((void *) prs->words, prs->lenwords * sizeof(HLWORD)); - } - - word = &(prs->words[prs->curwords - 1]); - for (i = 0; i < query->size; i++) - { - if (item->type == VAL && item->length == buflen && strncmp(GETOPERAND(query) + item->distance, buf, buflen) == 0) - { - if (word->item) - { - memcpy(&(prs->words[prs->curwords]), word, sizeof(HLWORD)); - prs->words[prs->curwords].item = item; - prs->words[prs->curwords].repeated = 1; - prs->curwords++; - } - else - word->item = item; - } - item++; - } -} - -static void -addHLParsedLex(HLPRSTEXT * prs, QUERYTYPE * query, ParsedLex * lexs, TSLexeme * norms) -{ - ParsedLex *tmplexs; - TSLexeme *ptr; - - while (lexs) - { - - if (lexs->type > 0) - hladdword(prs, lexs->lemm, lexs->lenlemm, lexs->type); - - ptr = norms; - while (ptr && ptr->lexeme) - { - hlfinditem(prs, query, ptr->lexeme, strlen(ptr->lexeme)); - ptr++; - } - - tmplexs = lexs->next; - pfree(lexs); - lexs = tmplexs; - } - - if (norms) - { - ptr = norms; - while (ptr->lexeme) - { - pfree(ptr->lexeme); - ptr++; - } - pfree(norms); - } -} - -void -hlparsetext(TSCfgInfo * cfg, HLPRSTEXT * prs, QUERYTYPE * query, char *buf, int4 buflen) -{ - int type, - lenlemm; - char *lemm = NULL; - WParserInfo *prsobj = findprs(cfg->prs_id); - LexizeData ldata; - TSLexeme *norms; - ParsedLex *lexs; - - prsobj->prs = (void *) DatumGetPointer( - FunctionCall2( - &(prsobj->start_info), - PointerGetDatum(buf), - Int32GetDatum(buflen) - ) - ); - - LexizeInit(&ldata, cfg); - - do - { - type = DatumGetInt32(FunctionCall3( - &(prsobj->getlexeme_info), - PointerGetDatum(prsobj->prs), - PointerGetDatum(&lemm), - PointerGetDatum(&lenlemm))); - - if (type > 0 && lenlemm >= MAXSTRLEN) - { -#ifdef IGNORE_LONGLEXEME - ereport(NOTICE, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("A word you are indexing is too long. It will be ignored."))); - continue; -#else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("A word you are indexing is too long"))); -#endif - } - - LexizeAddLemm(&ldata, type, lemm, lenlemm); - - do - { - if ((norms = LexizeExec(&ldata, &lexs)) != NULL) - addHLParsedLex(prs, query, lexs, norms); - else - addHLParsedLex(prs, query, lexs, NULL); - } while (norms); - - } while (type > 0); - - FunctionCall1( - &(prsobj->end_info), - PointerGetDatum(prsobj->prs) - ); -} - -text * -genhl(HLPRSTEXT * prs) -{ - text *out; - int len = 128; - char *ptr; - HLWORD *wrd = prs->words; - - out = (text *) palloc(len); - ptr = ((char *) out) + VARHDRSZ; - - while (wrd - prs->words < prs->curwords) - { - while (wrd->len + prs->stopsellen + prs->startsellen + (ptr - ((char *) out)) >= len) - { - int dist = ptr - ((char *) out); - - len *= 2; - out = (text *) repalloc(out, len); - ptr = ((char *) out) + dist; - } - - if (wrd->in && !wrd->repeated) - { - if (wrd->replace) - { - *ptr = ' '; - ptr++; - } - else - { - if (wrd->selected) - { - memcpy(ptr, prs->startsel, prs->startsellen); - ptr += prs->startsellen; - } - memcpy(ptr, wrd->word, wrd->len); - ptr += wrd->len; - if (wrd->selected) - { - memcpy(ptr, prs->stopsel, prs->stopsellen); - ptr += prs->stopsellen; - } - } - } - else if (!wrd->repeated) - pfree(wrd->word); - - wrd++; - } - - SET_VARSIZE(out, ptr - ((char *) out)); - return out; -} - -int -get_currcfg(void) -{ - Oid arg[1] = {TEXTOID}; - const char *curlocale; - Datum pars[1]; - bool isnull; - int stat; - char buf[1024]; - char *nsp; - void *plan; - - if (current_cfg_id > 0) - return current_cfg_id; - - nsp = get_namespace(TSNSP_FunctionOid); - SPI_connect(); - sprintf(buf, "select oid from %s.pg_ts_cfg where locale = $1 ", nsp); - pfree(nsp); - plan = SPI_prepare(buf, 1, arg); - if (!plan) - /* internal error */ - elog(ERROR, "SPI_prepare() failed"); - - curlocale = setlocale(LC_CTYPE, NULL); - pars[0] = PointerGetDatum(char2text((char *) curlocale)); - stat = SPI_execp(plan, pars, " ", 1); - - if (stat < 0) - /* internal error */ - elog(ERROR, "SPI_execp return %d", stat); - if (SPI_processed > 0) - current_cfg_id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); - else - ereport(ERROR, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("could not find tsearch config by locale"))); - - pfree(DatumGetPointer(pars[0])); - SPI_freeplan(plan); - SPI_finish(); - return current_cfg_id; -} - -PG_FUNCTION_INFO_V1(set_curcfg); -Datum set_curcfg(PG_FUNCTION_ARGS); -Datum -set_curcfg(PG_FUNCTION_ARGS) -{ - SET_FUNCOID(); - findcfg(PG_GETARG_OID(0)); - current_cfg_id = PG_GETARG_OID(0); - PG_RETURN_VOID(); -} - -PG_FUNCTION_INFO_V1(set_curcfg_byname); -Datum set_curcfg_byname(PG_FUNCTION_ARGS); -Datum -set_curcfg_byname(PG_FUNCTION_ARGS) -{ - text *name = PG_GETARG_TEXT_P(0); - - SET_FUNCOID(); - DirectFunctionCall1( - set_curcfg, - ObjectIdGetDatum(name2id_cfg(name)) - ); - PG_FREE_IF_COPY(name, 0); - PG_RETURN_VOID(); -} - -PG_FUNCTION_INFO_V1(show_curcfg); -Datum show_curcfg(PG_FUNCTION_ARGS); -Datum -show_curcfg(PG_FUNCTION_ARGS) -{ - SET_FUNCOID(); - PG_RETURN_OID(get_currcfg()); -} - -PG_FUNCTION_INFO_V1(reset_tsearch); -Datum reset_tsearch(PG_FUNCTION_ARGS); -Datum -reset_tsearch(PG_FUNCTION_ARGS) -{ - SET_FUNCOID(); - ts_error(NOTICE, "TSearch cache cleaned"); - PG_RETURN_VOID(); -} diff --git a/contrib/tsearch2/ts_cfg.h b/contrib/tsearch2/ts_cfg.h deleted file mode 100644 index 7bffdbcdd611..000000000000 --- a/contrib/tsearch2/ts_cfg.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef __TS_CFG_H__ -#define __TS_CFG_H__ - -#include "postgres.h" -#include "query.h" - - -typedef struct -{ - int len; - Datum *dict_id; -} ListDictionary; - -typedef struct -{ - Oid id; - Oid prs_id; - int len; - ListDictionary *map; -} TSCfgInfo; - -Oid name2id_cfg(text *name); -TSCfgInfo *findcfg(Oid id); -void init_cfg(Oid id, TSCfgInfo * cfg); -void reset_cfg(void); - -typedef struct -{ - uint16 len; - uint16 nvariant; - union - { - uint16 pos; - uint16 *apos; - } pos; - char *word; - uint32 alen; -} TSWORD; - -typedef struct -{ - TSWORD *words; - int4 lenwords; - int4 curwords; - int4 pos; -} PRSTEXT; - -typedef struct -{ - uint32 selected:1, - in:1, - replace:1, - repeated:1, - unused:4, - type:8, - len:16; - char *word; - ITEM *item; -} HLWORD; - -typedef struct -{ - HLWORD *words; - int4 lenwords; - int4 curwords; - char *startsel; - char *stopsel; - int2 startsellen; - int2 stopsellen; -} HLPRSTEXT; - -void hlparsetext(TSCfgInfo * cfg, HLPRSTEXT * prs, QUERYTYPE * query, char *buf, int4 buflen); -text *genhl(HLPRSTEXT * prs); - -void parsetext_v2(TSCfgInfo * cfg, PRSTEXT * prs, char *buf, int4 buflen); -int get_currcfg(void); - -#endif diff --git a/contrib/tsearch2/ts_lexize.c b/contrib/tsearch2/ts_lexize.c deleted file mode 100644 index f2e4904eb7b3..000000000000 --- a/contrib/tsearch2/ts_lexize.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * lexize stream of lexemes - * Teodor Sigaev - */ -#include "postgres.h" - -#include -#include - -#include "ts_cfg.h" -#include "dict.h" - -void -LexizeInit(LexizeData * ld, TSCfgInfo * cfg) -{ - ld->cfg = cfg; - ld->curDictId = InvalidOid; - ld->posDict = 0; - ld->towork.head = ld->towork.tail = ld->curSub = NULL; - ld->waste.head = ld->waste.tail = NULL; - ld->lastRes = NULL; - ld->tmpRes = NULL; -} - -static void -LPLAddTail(ListParsedLex * list, ParsedLex * newpl) -{ - if (list->tail) - { - list->tail->next = newpl; - list->tail = newpl; - } - else - list->head = list->tail = newpl; - newpl->next = NULL; -} - -static ParsedLex * -LPLRemoveHead(ListParsedLex * list) -{ - ParsedLex *res = list->head; - - if (list->head) - list->head = list->head->next; - - if (list->head == NULL) - list->tail = NULL; - - return res; -} - - -void -LexizeAddLemm(LexizeData * ld, int type, char *lemm, int lenlemm) -{ - ParsedLex *newpl = (ParsedLex *) palloc(sizeof(ParsedLex)); - - newpl = (ParsedLex *) palloc(sizeof(ParsedLex)); - newpl->type = type; - newpl->lemm = lemm; - newpl->lenlemm = lenlemm; - LPLAddTail(&ld->towork, newpl); - ld->curSub = ld->towork.tail; -} - -static void -RemoveHead(LexizeData * ld) -{ - LPLAddTail(&ld->waste, LPLRemoveHead(&ld->towork)); - - ld->posDict = 0; -} - -static void -setCorrLex(LexizeData * ld, ParsedLex ** correspondLexem) -{ - if (correspondLexem) - { - *correspondLexem = ld->waste.head; - } - else - { - ParsedLex *tmp, - *ptr = ld->waste.head; - - while (ptr) - { - tmp = ptr->next; - pfree(ptr); - ptr = tmp; - } - } - ld->waste.head = ld->waste.tail = NULL; -} - -static void -moveToWaste(LexizeData * ld, ParsedLex * stop) -{ - bool go = true; - - while (ld->towork.head && go) - { - if (ld->towork.head == stop) - { - ld->curSub = stop->next; - go = false; - } - RemoveHead(ld); - } -} - -static void -setNewTmpRes(LexizeData * ld, ParsedLex * lex, TSLexeme * res) -{ - if (ld->tmpRes) - { - TSLexeme *ptr; - - for (ptr = ld->tmpRes; ptr->lexeme; ptr++) - pfree(ptr->lexeme); - pfree(ld->tmpRes); - } - ld->tmpRes = res; - ld->lastRes = lex; -} - -TSLexeme * -LexizeExec(LexizeData * ld, ParsedLex ** correspondLexem) -{ - int i; - ListDictionary *map; - DictInfo *dict; - TSLexeme *res; - - if (ld->curDictId == InvalidOid) - { - /* - * usial mode: dictionary wants only one word, but we should keep in - * mind that we should go through all stack - */ - - while (ld->towork.head) - { - ParsedLex *curVal = ld->towork.head; - - map = ld->cfg->map + curVal->type; - - if (curVal->type == 0 || curVal->type >= ld->cfg->len || map->len == 0) - { - /* skip this type of lexeme */ - RemoveHead(ld); - continue; - } - - for (i = ld->posDict; i < map->len; i++) - { - dict = finddict(DatumGetObjectId(map->dict_id[i])); - - ld->dictState.isend = ld->dictState.getnext = false; - ld->dictState.private = NULL; - res = (TSLexeme *) DatumGetPointer(FunctionCall4( - &(dict->lexize_info), - PointerGetDatum(dict->dictionary), - PointerGetDatum(curVal->lemm), - Int32GetDatum(curVal->lenlemm), - PointerGetDatum(&ld->dictState) - )); - - if (ld->dictState.getnext) - { - /* - * dictinary wants next word, so setup and store current - * position and go to multiword mode - */ - - ld->curDictId = DatumGetObjectId(map->dict_id[i]); - ld->posDict = i + 1; - ld->curSub = curVal->next; - if (res) - setNewTmpRes(ld, curVal, res); - return LexizeExec(ld, correspondLexem); - } - - if (!res) /* dictionary doesn't know this lexeme */ - continue; - - RemoveHead(ld); - setCorrLex(ld, correspondLexem); - return res; - } - - RemoveHead(ld); - } - } - else - { /* curDictId is valid */ - dict = finddict(ld->curDictId); - - /* - * Dictionary ld->curDictId asks us about following words - */ - - while (ld->curSub) - { - ParsedLex *curVal = ld->curSub; - - map = ld->cfg->map + curVal->type; - - if (curVal->type != 0) - { - bool dictExists = false; - - if (curVal->type >= ld->cfg->len || map->len == 0) - { - /* skip this type of lexeme */ - ld->curSub = curVal->next; - continue; - } - - /* - * We should be sure that current type of lexeme is recognized - * by our dictinonary: we just check is it exist in list of - * dictionaries ? - */ - for (i = 0; i < map->len && !dictExists; i++) - if (ld->curDictId == DatumGetObjectId(map->dict_id[i])) - dictExists = true; - - if (!dictExists) - { - /* - * Dictionary can't work with current tpe of lexeme, - * return to basic mode and redo all stored lexemes - */ - ld->curDictId = InvalidOid; - return LexizeExec(ld, correspondLexem); - } - } - - ld->dictState.isend = (curVal->type == 0) ? true : false; - ld->dictState.getnext = false; - - res = (TSLexeme *) DatumGetPointer(FunctionCall4( - &(dict->lexize_info), - PointerGetDatum(dict->dictionary), - PointerGetDatum(curVal->lemm), - Int32GetDatum(curVal->lenlemm), - PointerGetDatum(&ld->dictState) - )); - - if (ld->dictState.getnext) - { - /* Dictionary wants one more */ - ld->curSub = curVal->next; - if (res) - setNewTmpRes(ld, curVal, res); - continue; - } - - if (res || ld->tmpRes) - { - /* - * Dictionary normalizes lexemes, so we remove from stack all - * used lexemes , return to basic mode and redo end of stack - * (if it exists) - */ - if (res) - { - moveToWaste(ld, ld->curSub); - } - else - { - res = ld->tmpRes; - moveToWaste(ld, ld->lastRes); - } - - /* reset to initial state */ - ld->curDictId = InvalidOid; - ld->posDict = 0; - ld->lastRes = NULL; - ld->tmpRes = NULL; - setCorrLex(ld, correspondLexem); - return res; - } - - /* - * Dict don't want next lexem and didn't recognize anything, redo - * from ld->towork.head - */ - ld->curDictId = InvalidOid; - return LexizeExec(ld, correspondLexem); - } - } - - setCorrLex(ld, correspondLexem); - return NULL; -} diff --git a/contrib/tsearch2/ts_locale.c b/contrib/tsearch2/ts_locale.c deleted file mode 100644 index 5bc40507e36c..000000000000 --- a/contrib/tsearch2/ts_locale.c +++ /dev/null @@ -1,191 +0,0 @@ -#include "ts_locale.h" - -#include "utils/builtins.h" -#include "utils/pg_locale.h" -#include "mb/pg_wchar.h" - - -#ifdef TS_USE_WIDE - -#ifdef WIN32 - -size_t -wchar2char(char *to, const wchar_t *from, size_t len) -{ - if (len == 0) - return 0; - - if (GetDatabaseEncoding() == PG_UTF8) - { - int r; - - r = WideCharToMultiByte(CP_UTF8, 0, from, -1, to, len, - NULL, NULL); - - if (r == 0) - ereport(ERROR, - (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), - errmsg("UTF-16 to UTF-8 translation failed: %lu", - GetLastError()))); - Assert(r <= len); - - return r; - } - - return wcstombs(to, from, len); -} -#endif /* WIN32 */ - -size_t -char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen) -{ - if (tolen == 0) - return 0; - -#ifdef WIN32 - if (GetDatabaseEncoding() == PG_UTF8) - { - int r; - - r = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen); - - if (!r) - { - pg_verifymbstr(from, strlen(from), false); - ereport(ERROR, - (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), - errmsg("invalid multibyte character for locale"), - errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding."))); - } - - Assert(r <= tolen); - - return r; - } - else -#endif /* WIN32 */ - if ( lc_ctype_is_c() ) - { - /* - * pg_mb2wchar_with_len always adds trailing '\0', so - * 'to' should be allocated with sufficient space - */ - return pg_mb2wchar_with_len(from, (pg_wchar *)to, tolen); - } - - return mbstowcs(to, from, tolen); -} - -int -_t_isalpha(const char *ptr) -{ - wchar_t character[2]; - - if (lc_ctype_is_c()) - return isalpha(TOUCHAR(ptr)); - - char2wchar(character, 1, ptr, 1); - - return iswalpha((wint_t) *character); -} - -int -_t_isprint(const char *ptr) -{ - wchar_t character[2]; - - if (lc_ctype_is_c()) - return isprint(TOUCHAR(ptr)); - - char2wchar(character, 1, ptr, 1); - - return iswprint((wint_t) *character); -} -#endif /* TS_USE_WIDE */ - -char * -lowerstr(char *str) -{ - char *ptr = str; - char *out; - int len = strlen(str); - - if ( len == 0 ) - return pstrdup(""); - -#ifdef TS_USE_WIDE - - /* - * Use wide char code only when max encoding length > 1 and ctype != C. - * Some operating systems fail with multi-byte encodings and a C locale. - * Also, for a C locale there is no need to process as multibyte. From - * backend/utils/adt/oracle_compat.c Teodor - */ - if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) - { - wchar_t *wstr, - *wptr; - int wlen; - - /* - *alloc number of wchar_t for worst case, len contains - * number of bytes <= number of characters and - * alloc 1 wchar_t for 0, because wchar2char(wcstombs in really) - * wants zero-terminated string - */ - wptr = wstr = (wchar_t *) palloc(sizeof(wchar_t) * (len+1)); - - /* - * str SHOULD be cstring, so wlen contains number - * of converted character - */ - wlen = char2wchar(wstr, len, str, len); - if ( wlen < 0 ) - ereport(ERROR, - (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), - errmsg("translation failed from server encoding to wchar_t"))); - - Assert(wlen<=len); - wstr[wlen] = 0; - - while (*wptr) - { - *wptr = towlower((wint_t) *wptr); - wptr++; - } - - /* - * Alloc result string for worst case + '\0' - */ - len = sizeof(char)*pg_database_encoding_max_length()*(wlen+1); - out = (char*)palloc(len); - - /* - * wlen now is number of bytes which is always >= number of characters - */ - wlen = wchar2char(out, wstr, len); - pfree(wstr); - - if ( wlen < 0 ) - ereport(ERROR, - (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), - errmsg("translation failed from wchar_t to server encoding %d", errno))); - Assert(wlen<=len); - out[wlen]='\0'; - } - else -#endif - { - char *outptr; - - outptr = out = (char*)palloc( sizeof(char) * (len+1) ); - while (*ptr) - { - *outptr++ = tolower(*(unsigned char *) ptr); - ptr++; - } - *outptr = '\0'; - } - - return out; -} diff --git a/contrib/tsearch2/ts_locale.h b/contrib/tsearch2/ts_locale.h deleted file mode 100644 index 603631f021b1..000000000000 --- a/contrib/tsearch2/ts_locale.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef __TSLOCALE_H__ -#define __TSLOCALE_H__ - -#include "postgres.h" -#include "utils/pg_locale.h" -#include "mb/pg_wchar.h" - -#include -#include - -/* - * towlower() and friends should be in , but some pre-C99 systems - * declare them in . - */ -#ifdef HAVE_WCHAR_H -#include -#endif -#ifdef HAVE_WCTYPE_H -#include -#endif - -#if defined(HAVE_WCSTOMBS) && defined(HAVE_TOWLOWER) -#define TS_USE_WIDE -#endif - -#ifdef TS_USE_WIDE -#endif /* TS_USE_WIDE */ - - -#define TOUCHAR(x) (*((unsigned char*)(x))) - -#ifdef TS_USE_WIDE -size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen); - -#ifdef WIN32 - -extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen); - -#else /* WIN32 */ - -/* correct wcstombs */ -#define wchar2char wcstombs - -#endif /* WIN32 */ - -#define t_isdigit(x) ( pg_mblen(x)==1 && isdigit( TOUCHAR(x) ) ) -#define t_isspace(x) ( pg_mblen(x)==1 && isspace( TOUCHAR(x) ) ) -extern int _t_isalpha(const char *ptr); - -#define t_isalpha(x) ( (pg_mblen(x)==1) ? isalpha( TOUCHAR(x) ) : _t_isalpha(x) ) -extern int _t_isprint(const char *ptr); - -#define t_isprint(x) ( (pg_mblen(x)==1) ? isprint( TOUCHAR(x) ) : _t_isprint(x) ) -/* - * t_iseq() should be called only for ASCII symbols - */ -#define t_iseq(x,c) ( (pg_mblen(x)==1) ? ( TOUCHAR(x) == ((unsigned char)(c)) ) : false ) - -#define COPYCHAR(d,s) do { \ - int lll = pg_mblen( s ); \ - \ - while( lll-- ) \ - TOUCHAR((d)+lll) = TOUCHAR((s)+lll); \ -} while(0) - -#else /* not def TS_USE_WIDE */ - -#define t_isdigit(x) isdigit( TOUCHAR(x) ) -#define t_isspace(x) isspace( TOUCHAR(x) ) -#define t_isalpha(x) isalpha( TOUCHAR(x) ) -#define t_isprint(x) isprint( TOUCHAR(x) ) -#define t_iseq(x,c) ( TOUCHAR(x) == ((unsigned char)(c)) ) - -#define COPYCHAR(d,s) TOUCHAR(d) = TOUCHAR(s) -#endif - -char *lowerstr(char *str); - -#endif /* __TSLOCALE_H__ */ diff --git a/contrib/tsearch2/ts_stat.c b/contrib/tsearch2/ts_stat.c deleted file mode 100644 index d728dd579605..000000000000 --- a/contrib/tsearch2/ts_stat.c +++ /dev/null @@ -1,567 +0,0 @@ -/* - * stat functions - */ - -#include "tsvector.h" -#include "ts_stat.h" -#include "funcapi.h" -#include "catalog/pg_type.h" -#include "executor/spi.h" -#include "common.h" -#include "ts_locale.h" - -PG_FUNCTION_INFO_V1(tsstat_in); -Datum tsstat_in(PG_FUNCTION_ARGS); -Datum -tsstat_in(PG_FUNCTION_ARGS) -{ - tsstat *stat = palloc(STATHDRSIZE); - - SET_VARSIZE(stat, STATHDRSIZE); - stat->size = 0; - stat->weight = 0; - PG_RETURN_POINTER(stat); -} - -PG_FUNCTION_INFO_V1(tsstat_out); -Datum tsstat_out(PG_FUNCTION_ARGS); -Datum -tsstat_out(PG_FUNCTION_ARGS) -{ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("tsstat_out not implemented"))); - PG_RETURN_NULL(); -} - -static int -check_weight(tsvector * txt, WordEntry * wptr, int8 weight) -{ - int len = POSDATALEN(txt, wptr); - int num = 0; - WordEntryPos *ptr = POSDATAPTR(txt, wptr); - - while (len--) - { - if (weight & (1 << WEP_GETWEIGHT(*ptr))) - num++; - ptr++; - } - return num; -} - -static WordEntry ** -SEI_realloc(WordEntry ** in, uint32 *len) -{ - if (*len == 0 || in == NULL) - { - *len = 8; - in = palloc(sizeof(WordEntry *) * (*len)); - } - else - { - *len *= 2; - in = repalloc(in, sizeof(WordEntry *) * (*len)); - } - return in; -} - -static int -compareStatWord(StatEntry * a, WordEntry * b, tsstat * stat, tsvector * txt) -{ - if (a->len == b->len) - return strncmp( - STATSTRPTR(stat) + a->pos, - STRPTR(txt) + b->pos, - a->len - ); - return (a->len > b->len) ? 1 : -1; -} - -static tsstat * -formstat(tsstat * stat, tsvector * txt, WordEntry ** entry, uint32 len) -{ - tsstat *newstat; - uint32 totallen, - nentry; - uint32 slen = 0; - WordEntry **ptr = entry; - char *curptr; - StatEntry *sptr, - *nptr; - - while (ptr - entry < len) - { - slen += (*ptr)->len; - ptr++; - } - - nentry = stat->size + len; - slen += STATSTRSIZE(stat); - totallen = CALCSTATSIZE(nentry, slen); - newstat = palloc(totallen); - SET_VARSIZE(newstat, totallen); - newstat->weight = stat->weight; - newstat->size = nentry; - - memcpy(STATSTRPTR(newstat), STATSTRPTR(stat), STATSTRSIZE(stat)); - curptr = STATSTRPTR(newstat) + STATSTRSIZE(stat); - - ptr = entry; - sptr = STATPTR(stat); - nptr = STATPTR(newstat); - - if (len == 1) - { - StatEntry *StopLow = STATPTR(stat); - StatEntry *StopHigh = (StatEntry *) STATSTRPTR(stat); - - while (StopLow < StopHigh) - { - sptr = StopLow + (StopHigh - StopLow) / 2; - if (compareStatWord(sptr, *ptr, stat, txt) < 0) - StopLow = sptr + 1; - else - StopHigh = sptr; - } - nptr = STATPTR(newstat) + (StopLow - STATPTR(stat)); - memcpy(STATPTR(newstat), STATPTR(stat), sizeof(StatEntry) * (StopLow - STATPTR(stat))); - if ((*ptr)->haspos) - nptr->nentry = (stat->weight) ? check_weight(txt, *ptr, stat->weight) : POSDATALEN(txt, *ptr); - else - nptr->nentry = 1; - nptr->ndoc = 1; - nptr->len = (*ptr)->len; - memcpy(curptr, STRPTR(txt) + (*ptr)->pos, nptr->len); - nptr->pos = curptr - STATSTRPTR(newstat); - memcpy(nptr + 1, StopLow, sizeof(StatEntry) * (((StatEntry *) STATSTRPTR(stat)) - StopLow)); - } - else - { - while (sptr - STATPTR(stat) < stat->size && ptr - entry < len) - { - if (compareStatWord(sptr, *ptr, stat, txt) < 0) - { - memcpy(nptr, sptr, sizeof(StatEntry)); - sptr++; - } - else - { - if ((*ptr)->haspos) - nptr->nentry = (stat->weight) ? check_weight(txt, *ptr, stat->weight) : POSDATALEN(txt, *ptr); - else - nptr->nentry = 1; - nptr->ndoc = 1; - nptr->len = (*ptr)->len; - memcpy(curptr, STRPTR(txt) + (*ptr)->pos, nptr->len); - nptr->pos = curptr - STATSTRPTR(newstat); - curptr += nptr->len; - ptr++; - } - nptr++; - } - - memcpy(nptr, sptr, sizeof(StatEntry) * (stat->size - (sptr - STATPTR(stat)))); - - while (ptr - entry < len) - { - if ((*ptr)->haspos) - nptr->nentry = (stat->weight) ? check_weight(txt, *ptr, stat->weight) : POSDATALEN(txt, *ptr); - else - nptr->nentry = 1; - nptr->ndoc = 1; - nptr->len = (*ptr)->len; - memcpy(curptr, STRPTR(txt) + (*ptr)->pos, nptr->len); - nptr->pos = curptr - STATSTRPTR(newstat); - curptr += nptr->len; - ptr++; - nptr++; - } - } - - return newstat; -} - -PG_FUNCTION_INFO_V1(ts_accum); -Datum ts_accum(PG_FUNCTION_ARGS); -Datum -ts_accum(PG_FUNCTION_ARGS) -{ - tsstat *newstat, - *stat = (tsstat *) PG_GETARG_POINTER(0); - tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - WordEntry **newentry = NULL; - uint32 len = 0, - cur = 0; - StatEntry *sptr; - WordEntry *wptr; - int n = 0; - - if (stat == NULL || PG_ARGISNULL(0)) - { /* Init in first */ - stat = palloc(STATHDRSIZE); - SET_VARSIZE(stat, STATHDRSIZE); - stat->size = 0; - stat->weight = 0; - } - - /* simple check of correctness */ - if (txt == NULL || PG_ARGISNULL(1) || txt->size == 0) - { - PG_FREE_IF_COPY(txt, 1); - PG_RETURN_POINTER(stat); - } - - sptr = STATPTR(stat); - wptr = ARRPTR(txt); - - if (stat->size < 100 * txt->size) - { /* merge */ - while (sptr - STATPTR(stat) < stat->size && wptr - ARRPTR(txt) < txt->size) - { - int cmp = compareStatWord(sptr, wptr, stat, txt); - - if (cmp < 0) - sptr++; - else if (cmp == 0) - { - if (stat->weight == 0) - { - sptr->ndoc++; - sptr->nentry += (wptr->haspos) ? POSDATALEN(txt, wptr) : 1; - } - else if (wptr->haspos && (n = check_weight(txt, wptr, stat->weight)) != 0) - { - sptr->ndoc++; - sptr->nentry += n; - } - sptr++; - wptr++; - } - else - { - if (stat->weight == 0 || check_weight(txt, wptr, stat->weight) != 0) - { - if (cur == len) - newentry = SEI_realloc(newentry, &len); - newentry[cur] = wptr; - cur++; - } - wptr++; - } - } - - while (wptr - ARRPTR(txt) < txt->size) - { - if (stat->weight == 0 || check_weight(txt, wptr, stat->weight) != 0) - { - if (cur == len) - newentry = SEI_realloc(newentry, &len); - newentry[cur] = wptr; - cur++; - } - wptr++; - } - } - else - { /* search */ - while (wptr - ARRPTR(txt) < txt->size) - { - StatEntry *StopLow = STATPTR(stat); - StatEntry *StopHigh = (StatEntry *) STATSTRPTR(stat); - int cmp; - - while (StopLow < StopHigh) - { - sptr = StopLow + (StopHigh - StopLow) / 2; - cmp = compareStatWord(sptr, wptr, stat, txt); - if (cmp == 0) - { - if (stat->weight == 0) - { - sptr->ndoc++; - sptr->nentry += (wptr->haspos) ? POSDATALEN(txt, wptr) : 1; - } - else if (wptr->haspos && (n = check_weight(txt, wptr, stat->weight)) != 0) - { - sptr->ndoc++; - sptr->nentry += n; - } - break; - } - else if (cmp < 0) - StopLow = sptr + 1; - else - StopHigh = sptr; - } - - if (StopLow >= StopHigh) - { /* not found */ - if (stat->weight == 0 || check_weight(txt, wptr, stat->weight) != 0) - { - if (cur == len) - newentry = SEI_realloc(newentry, &len); - newentry[cur] = wptr; - cur++; - } - } - wptr++; - } - } - - - if (cur == 0) - { /* no new words */ - PG_FREE_IF_COPY(txt, 1); - PG_RETURN_POINTER(stat); - } - - newstat = formstat(stat, txt, newentry, cur); - pfree(newentry); - PG_FREE_IF_COPY(txt, 1); - /* pfree(stat); */ - - PG_RETURN_POINTER(newstat); -} - -typedef struct -{ - uint32 cur; - tsvector *stat; -} StatStorage; - -static void -ts_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, - tsstat * stat) -{ - TupleDesc tupdesc; - MemoryContext oldcontext; - StatStorage *st; - - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - st = palloc(sizeof(StatStorage)); - st->cur = 0; - st->stat = palloc(VARSIZE(stat)); - memcpy(st->stat, stat, VARSIZE(stat)); - funcctx->user_fctx = (void *) st; - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - tupdesc = CreateTupleDescCopy(tupdesc); - funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); - MemoryContextSwitchTo(oldcontext); -} - - -static Datum -ts_process_call(FuncCallContext *funcctx) -{ - StatStorage *st; - - st = (StatStorage *) funcctx->user_fctx; - - if (st->cur < st->stat->size) - { - Datum result; - char *values[3]; - char ndoc[16]; - char nentry[16]; - StatEntry *entry = STATPTR(st->stat) + st->cur; - HeapTuple tuple; - - values[1] = ndoc; - sprintf(ndoc, "%d", entry->ndoc); - values[2] = nentry; - sprintf(nentry, "%d", entry->nentry); - values[0] = palloc(entry->len + 1); - memcpy(values[0], STATSTRPTR(st->stat) + entry->pos, entry->len); - (values[0])[entry->len] = '\0'; - - tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); - result = HeapTupleGetDatum(tuple); - - pfree(values[0]); - st->cur++; - return result; - } - else - { - pfree(st->stat); - pfree(st); - } - - return (Datum) 0; -} - -PG_FUNCTION_INFO_V1(ts_accum_finish); -Datum ts_accum_finish(PG_FUNCTION_ARGS); -Datum -ts_accum_finish(PG_FUNCTION_ARGS) -{ - FuncCallContext *funcctx; - Datum result; - - if (SRF_IS_FIRSTCALL()) - { - funcctx = SRF_FIRSTCALL_INIT(); - ts_setup_firstcall(fcinfo, funcctx, (tsstat *) PG_GETARG_POINTER(0)); - } - - funcctx = SRF_PERCALL_SETUP(); - if ((result = ts_process_call(funcctx)) != (Datum) 0) - SRF_RETURN_NEXT(funcctx, result); - SRF_RETURN_DONE(funcctx); -} - -static Oid tiOid = InvalidOid; - -static void -get_ti_Oid(void) -{ - int ret; - bool isnull; - - if ((ret = SPI_exec("select oid from pg_type where typname='tsvector'", 1)) < 0) - /* internal error */ - elog(ERROR, "SPI_exec to get tsvector oid returns %d", ret); - - if (SPI_processed < 1) - /* internal error */ - elog(ERROR, "there is no tsvector type"); - tiOid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); - if (tiOid == InvalidOid) - /* internal error */ - elog(ERROR, "tsvector type has InvalidOid"); -} - -static tsstat * -ts_stat_sql(text *txt, text *ws) -{ - char *query = text2char(txt); - int i; - tsstat *newstat, - *stat; - bool isnull; - Portal portal; - void *plan; - - if (tiOid == InvalidOid) - get_ti_Oid(); - - if ((plan = SPI_prepare(query, 0, NULL)) == NULL) - /* internal error */ - elog(ERROR, "SPI_prepare('%s') returns NULL", query); - - if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, false)) == NULL) - /* internal error */ - elog(ERROR, "SPI_cursor_open('%s') returns NULL", query); - - SPI_cursor_fetch(portal, true, 100); - - if (SPI_tuptable->tupdesc->natts != 1) - /* internal error */ - elog(ERROR, "number of fields doesn't equal to 1"); - - if (SPI_gettypeid(SPI_tuptable->tupdesc, 1) != tiOid) - /* internal error */ - elog(ERROR, "column isn't of tsvector type"); - - stat = palloc(STATHDRSIZE); - SET_VARSIZE(stat, STATHDRSIZE); - stat->size = 0; - stat->weight = 0; - - if (ws) - { - char *buf; - - buf = VARDATA(ws); - while (buf - VARDATA(ws) < VARSIZE(ws) - VARHDRSZ) - { - if (pg_mblen(buf) == 1) - { - switch (*buf) - { - case 'A': - case 'a': - stat->weight |= 1 << 3; - break; - case 'B': - case 'b': - stat->weight |= 1 << 2; - break; - case 'C': - case 'c': - stat->weight |= 1 << 1; - break; - case 'D': - case 'd': - stat->weight |= 1; - break; - default: - stat->weight |= 0; - } - } - buf += pg_mblen(buf); - } - } - - while (SPI_processed > 0) - { - for (i = 0; i < SPI_processed; i++) - { - Datum data = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull); - - if (!isnull) - { - newstat = (tsstat *) DatumGetPointer(DirectFunctionCall2( - ts_accum, - PointerGetDatum(stat), - data - )); - if (stat != newstat && stat) - pfree(stat); - stat = newstat; - } - } - - SPI_freetuptable(SPI_tuptable); - SPI_cursor_fetch(portal, true, 100); - } - - SPI_freetuptable(SPI_tuptable); - SPI_cursor_close(portal); - SPI_freeplan(plan); - pfree(query); - - return stat; -} - -PG_FUNCTION_INFO_V1(ts_stat); -Datum ts_stat(PG_FUNCTION_ARGS); -Datum -ts_stat(PG_FUNCTION_ARGS) -{ - FuncCallContext *funcctx; - Datum result; - - if (SRF_IS_FIRSTCALL()) - { - tsstat *stat; - text *txt = PG_GETARG_TEXT_P(0); - text *ws = (PG_NARGS() > 1) ? PG_GETARG_TEXT_P(1) : NULL; - - funcctx = SRF_FIRSTCALL_INIT(); - SPI_connect(); - stat = ts_stat_sql(txt, ws); - PG_FREE_IF_COPY(txt, 0); - if (PG_NARGS() > 1) - PG_FREE_IF_COPY(ws, 1); - ts_setup_firstcall(fcinfo, funcctx, stat); - SPI_finish(); - } - - funcctx = SRF_PERCALL_SETUP(); - if ((result = ts_process_call(funcctx)) != (Datum) 0) - SRF_RETURN_NEXT(funcctx, result); - SRF_RETURN_DONE(funcctx); -} diff --git a/contrib/tsearch2/ts_stat.h b/contrib/tsearch2/ts_stat.h deleted file mode 100644 index e1ba14b99bcb..000000000000 --- a/contrib/tsearch2/ts_stat.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __TXTIDX_STAT_H__ -#define __TXTIDX_STAT_H__ - -#include "postgres.h" - -#include "access/gist.h" -#include "access/itup.h" -#include "utils/builtins.h" -#include "storage/bufpage.h" -#include "tsvector.h" - -typedef struct -{ - uint32 len; - uint32 pos; - uint32 ndoc; - uint32 nentry; -} StatEntry; - -typedef struct -{ - int32 vl_len_; /* varlena header (do not touch directly!) */ - int4 size; - int4 weight; - char data[1]; -} tsstat; - -#define STATHDRSIZE (sizeof(int4) * 4) -#define CALCSTATSIZE(x, lenstr) ( (x) * sizeof(StatEntry) + STATHDRSIZE + (lenstr) ) -#define STATPTR(x) ( (StatEntry*) ( (char*)(x) + STATHDRSIZE ) ) -#define STATSTRPTR(x) ( (char*)(x) + STATHDRSIZE + ( sizeof(StatEntry) * ((tsvector*)(x))->size ) ) -#define STATSTRSIZE(x) ( VARSIZE((tsvector*)(x)) - STATHDRSIZE - ( sizeof(StatEntry) * ((tsvector*)(x))->size ) ) - -#endif diff --git a/contrib/tsearch2/tsearch.sql.in b/contrib/tsearch2/tsearch.sql.in deleted file mode 100644 index 08afa7629012..000000000000 --- a/contrib/tsearch2/tsearch.sql.in +++ /dev/null @@ -1,1243 +0,0 @@ --- Adjust this setting to control where the objects get CREATEd. -SET search_path = public; - -BEGIN; - ---dict conf -CREATE TABLE pg_ts_dict ( - dict_name text not null primary key, - dict_init regprocedure, - dict_initoption text, - dict_lexize regprocedure not null, - dict_comment text -) with oids; - ---dict interface -CREATE FUNCTION lexize(oid, text) - RETURNS _text - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION lexize(text, text) - RETURNS _text - as 'MODULE_PATHNAME', 'lexize_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION lexize(text) - RETURNS _text - as 'MODULE_PATHNAME', 'lexize_bycurrent' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curdict(int) - RETURNS void - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curdict(text) - RETURNS void - as 'MODULE_PATHNAME', 'set_curdict_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---built-in dictionaries -CREATE FUNCTION dex_init(internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION dex_lexize(internal,internal,int4) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -insert into pg_ts_dict select - 'simple', - 'dex_init(internal)', - null, - 'dex_lexize(internal,internal,int4)', - 'Simple example of dictionary.' -; - -CREATE FUNCTION snb_en_init(internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION snb_lexize(internal,internal,int4) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -insert into pg_ts_dict select - 'en_stem', - 'snb_en_init(internal)', - 'contrib/english.stop', - 'snb_lexize(internal,internal,int4)', - 'English Stemmer. Snowball.' -; - -CREATE FUNCTION snb_ru_init_koi8(internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -insert into pg_ts_dict select - 'ru_stem_koi8', - 'snb_ru_init_koi8(internal)', - 'contrib/russian.stop', - 'snb_lexize(internal,internal,int4)', - 'Russian Stemmer. Snowball. KOI8 Encoding' -; - -CREATE FUNCTION snb_ru_init_utf8(internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -insert into pg_ts_dict select - 'ru_stem_utf8', - 'snb_ru_init_utf8(internal)', - 'contrib/russian.stop.utf8', - 'snb_lexize(internal,internal,int4)', - 'Russian Stemmer. Snowball. UTF8 Encoding' -; - -CREATE FUNCTION spell_init(internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION spell_lexize(internal,internal,int4) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -insert into pg_ts_dict select - 'ispell_template', - 'spell_init(internal)', - null, - 'spell_lexize(internal,internal,int4)', - 'ISpell interface. Must have .dict and .aff files' -; - -CREATE FUNCTION syn_init(internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION syn_lexize(internal,internal,int4) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -insert into pg_ts_dict select - 'synonym', - 'syn_init(internal)', - null, - 'syn_lexize(internal,internal,int4)', - 'Example of synonym dictionary' -; - -CREATE FUNCTION thesaurus_init(internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION thesaurus_lexize(internal,internal,int4,internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -insert into pg_ts_dict select - 'thesaurus_template', - 'thesaurus_init(internal)', - null, - 'thesaurus_lexize(internal,internal,int4,internal)', - 'Thesaurus template, must be pointed Dictionary and DictFile' -; - ---dict conf -CREATE TABLE pg_ts_parser ( - prs_name text not null primary key, - prs_start regprocedure not null, - prs_nexttoken regprocedure not null, - prs_end regprocedure not null, - prs_headline regprocedure not null, - prs_lextype regprocedure not null, - prs_comment text -) with oids; - ---sql-level interface -CREATE TYPE tokentype - as (tokid int4, alias text, descr text); - -CREATE FUNCTION token_type(int4) - RETURNS setof tokentype - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION token_type(text) - RETURNS setof tokentype - as 'MODULE_PATHNAME', 'token_type_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION token_type() - RETURNS setof tokentype - as 'MODULE_PATHNAME', 'token_type_current' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curprs(int) - RETURNS void - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curprs(text) - RETURNS void - as 'MODULE_PATHNAME', 'set_curprs_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE TYPE tokenout - as (tokid int4, token text); - -CREATE FUNCTION parse(oid,text) - RETURNS setof tokenout - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION parse(text,text) - RETURNS setof tokenout - as 'MODULE_PATHNAME', 'parse_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION parse(text) - RETURNS setof tokenout - as 'MODULE_PATHNAME', 'parse_current' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---default parser -CREATE FUNCTION prsd_start(internal,int4) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION prsd_getlexeme(internal,internal,internal) - RETURNS int4 - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION prsd_end(internal) - RETURNS void - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION prsd_lextype(internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE FUNCTION prsd_headline(internal,internal,internal) - RETURNS internal - as 'MODULE_PATHNAME' - LANGUAGE C; - -insert into pg_ts_parser select - 'default', - 'prsd_start(internal,int4)', - 'prsd_getlexeme(internal,internal,internal)', - 'prsd_end(internal)', - 'prsd_headline(internal,internal,internal)', - 'prsd_lextype(internal)', - 'Parser from OpenFTS v0.34' -; - ---tsearch config - -CREATE TABLE pg_ts_cfg ( - ts_name text not null primary key, - prs_name text not null, - locale text -) with oids; - -CREATE TABLE pg_ts_cfgmap ( - ts_name text not null, - tok_alias text not null, - dict_name text[], - primary key (ts_name,tok_alias) -) with oids; - -CREATE FUNCTION set_curcfg(int) - RETURNS void - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curcfg(text) - RETURNS void - as 'MODULE_PATHNAME', 'set_curcfg_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION show_curcfg() - RETURNS oid - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -insert into pg_ts_cfg values ('default', 'default','C'); -insert into pg_ts_cfg values ('default_russian', 'default','ru_RU.KOI8-R'); -insert into pg_ts_cfg values ('utf8_russian', 'default','ru_RU.UTF-8'); -insert into pg_ts_cfg values ('simple', 'default'); - -insert into pg_ts_cfgmap values ('default', 'lword', '{en_stem}'); -insert into pg_ts_cfgmap values ('default', 'nlword', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'word', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'email', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'url', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'host', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'sfloat', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'version', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'part_hword', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'nlpart_hword', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'lpart_hword', '{en_stem}'); -insert into pg_ts_cfgmap values ('default', 'hword', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'lhword', '{en_stem}'); -insert into pg_ts_cfgmap values ('default', 'nlhword', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'uri', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'file', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'float', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'int', '{simple}'); -insert into pg_ts_cfgmap values ('default', 'uint', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'lword', '{en_stem}'); -insert into pg_ts_cfgmap values ('default_russian', 'nlword', '{ru_stem_koi8}'); -insert into pg_ts_cfgmap values ('default_russian', 'word', '{ru_stem_koi8}'); -insert into pg_ts_cfgmap values ('default_russian', 'email', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'url', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'host', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'sfloat', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'version', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'part_hword', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'nlpart_hword', '{ru_stem_koi8}'); -insert into pg_ts_cfgmap values ('default_russian', 'lpart_hword', '{en_stem}'); -insert into pg_ts_cfgmap values ('default_russian', 'hword', '{ru_stem_koi8}'); -insert into pg_ts_cfgmap values ('default_russian', 'lhword', '{en_stem}'); -insert into pg_ts_cfgmap values ('default_russian', 'nlhword', '{ru_stem_koi8}'); -insert into pg_ts_cfgmap values ('default_russian', 'uri', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'file', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'float', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'int', '{simple}'); -insert into pg_ts_cfgmap values ('default_russian', 'uint', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'lword', '{en_stem}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'nlword', '{ru_stem_utf8}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'word', '{ru_stem_utf8}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'email', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'url', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'host', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'sfloat', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'version', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'part_hword', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'nlpart_hword', '{ru_stem_utf8}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'lpart_hword', '{en_stem}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'hword', '{ru_stem_utf8}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'lhword', '{en_stem}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'nlhword', '{ru_stem_utf8}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'uri', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'file', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'float', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'int', '{simple}'); -insert into pg_ts_cfgmap values ('utf8_russian', 'uint', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'lword', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'nlword', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'word', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'email', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'url', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'host', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'sfloat', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'version', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'part_hword', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'nlpart_hword', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'lpart_hword', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'hword', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'lhword', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'nlhword', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'uri', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'file', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'float', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'int', '{simple}'); -insert into pg_ts_cfgmap values ('simple', 'uint', '{simple}'); - ---tsvector type -CREATE FUNCTION tsvector_in(cstring) -RETURNS tsvector -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION tsvector_out(tsvector) -RETURNS cstring -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE TYPE tsvector ( - INTERNALLENGTH = -1, - INPUT = tsvector_in, - OUTPUT = tsvector_out, - STORAGE = extended -); - -CREATE FUNCTION length(tsvector) -RETURNS int4 -AS 'MODULE_PATHNAME', 'tsvector_length' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsvector(oid, text) -RETURNS tsvector -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsvector(text, text) -RETURNS tsvector -AS 'MODULE_PATHNAME', 'to_tsvector_name' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsvector(text) -RETURNS tsvector -AS 'MODULE_PATHNAME', 'to_tsvector_current' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION strip(tsvector) -RETURNS tsvector -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION setweight(tsvector,"char") -RETURNS tsvector -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION concat(tsvector,tsvector) -RETURNS tsvector -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OPERATOR || ( - LEFTARG = tsvector, - RIGHTARG = tsvector, - PROCEDURE = concat -); - ---query type -CREATE FUNCTION tsquery_in(cstring) -RETURNS tsquery -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION tsquery_out(tsquery) -RETURNS cstring -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE TYPE tsquery ( - INTERNALLENGTH = -1, - ALIGNMENT = int4, - INPUT = tsquery_in, - OUTPUT = tsquery_out -); - -CREATE FUNCTION querytree(tsquery) -RETURNS text -AS 'MODULE_PATHNAME', 'tsquerytree' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION to_tsquery(oid, text) -RETURNS tsquery -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsquery(text, text) -RETURNS tsquery -AS 'MODULE_PATHNAME','to_tsquery_name' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsquery(text) -RETURNS tsquery -AS 'MODULE_PATHNAME','to_tsquery_current' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION plainto_tsquery(oid, text) -RETURNS tsquery -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION plainto_tsquery(text, text) -RETURNS tsquery -AS 'MODULE_PATHNAME','plainto_tsquery_name' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION plainto_tsquery(text) -RETURNS tsquery -AS 'MODULE_PATHNAME','plainto_tsquery_current' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - ---operations -CREATE FUNCTION exectsq(tsvector, tsquery) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -COMMENT ON FUNCTION exectsq(tsvector, tsquery) IS 'boolean operation with text index'; - -CREATE FUNCTION rexectsq(tsquery, tsvector) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -COMMENT ON FUNCTION rexectsq(tsquery, tsvector) IS 'boolean operation with text index'; - -CREATE OPERATOR @@ ( - LEFTARG = tsvector, - RIGHTARG = tsquery, - PROCEDURE = exectsq, - COMMUTATOR = '@@', - RESTRICT = contsel, - JOIN = contjoinsel -); -CREATE OPERATOR @@ ( - LEFTARG = tsquery, - RIGHTARG = tsvector, - PROCEDURE = rexectsq, - COMMUTATOR = '@@', - RESTRICT = contsel, - JOIN = contjoinsel -); - ---Trigger -CREATE FUNCTION tsearch2() -RETURNS trigger -AS 'MODULE_PATHNAME' -LANGUAGE C; - ---Relevation -CREATE FUNCTION rank(float4[], tsvector, tsquery) -RETURNS float4 -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank(float4[], tsvector, tsquery, int4) -RETURNS float4 -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank(tsvector, tsquery) -RETURNS float4 -AS 'MODULE_PATHNAME', 'rank_def' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank(tsvector, tsquery, int4) -RETURNS float4 -AS 'MODULE_PATHNAME', 'rank_def' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank_cd(float4[], tsvector, tsquery) -RETURNS float4 -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank_cd(float4[], tsvector, tsquery, int4) -RETURNS float4 -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank_cd(tsvector, tsquery) -RETURNS float4 -AS 'MODULE_PATHNAME', 'rank_cd_def' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank_cd(tsvector, tsquery, int4) -RETURNS float4 -AS 'MODULE_PATHNAME', 'rank_cd_def' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(oid, text, tsquery, text) -RETURNS text -AS 'MODULE_PATHNAME', 'headline' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(oid, text, tsquery) -RETURNS text -AS 'MODULE_PATHNAME', 'headline' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(text, text, tsquery, text) -RETURNS text -AS 'MODULE_PATHNAME', 'headline_byname' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(text, text, tsquery) -RETURNS text -AS 'MODULE_PATHNAME', 'headline_byname' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(text, tsquery, text) -RETURNS text -AS 'MODULE_PATHNAME', 'headline_current' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(text, tsquery) -RETURNS text -AS 'MODULE_PATHNAME', 'headline_current' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - ---GiST ---GiST key type -CREATE FUNCTION gtsvector_in(cstring) -RETURNS gtsvector -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION gtsvector_out(gtsvector) -RETURNS cstring -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE TYPE gtsvector ( - INTERNALLENGTH = -1, - INPUT = gtsvector_in, - OUTPUT = gtsvector_out -); - --- support FUNCTIONs -CREATE FUNCTION gtsvector_consistent(gtsvector,internal,int4) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsvector_compress(internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsvector_decompress(internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsvector_penalty(internal,internal,internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION gtsvector_picksplit(internal, internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsvector_union(internal, internal) -RETURNS _int4 -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsvector_same(gtsvector, gtsvector, internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C; - --- CREATE the OPERATOR class -CREATE OPERATOR CLASS gist_tsvector_ops -DEFAULT FOR TYPE tsvector USING gist -AS - OPERATOR 1 @@ (tsvector, tsquery) RECHECK , - FUNCTION 1 gtsvector_consistent (gtsvector, internal, int4), - FUNCTION 2 gtsvector_union (internal, internal), - FUNCTION 3 gtsvector_compress (internal), - FUNCTION 4 gtsvector_decompress (internal), - FUNCTION 5 gtsvector_penalty (internal, internal, internal), - FUNCTION 6 gtsvector_picksplit (internal, internal), - FUNCTION 7 gtsvector_same (gtsvector, gtsvector, internal), - STORAGE gtsvector; - - ---stat info -CREATE TYPE statinfo - as (word text, ndoc int4, nentry int4); - ---CREATE FUNCTION tsstat_in(cstring) ---RETURNS tsstat ---AS 'MODULE_PATHNAME' ---LANGUAGE C RETURNS NULL ON NULL INPUT; --- ---CREATE FUNCTION tsstat_out(tsstat) ---RETURNS cstring ---AS 'MODULE_PATHNAME' ---LANGUAGE C RETURNS NULL ON NULL INPUT; --- ---CREATE TYPE tsstat ( --- INTERNALLENGTH = -1, --- INPUT = tsstat_in, --- OUTPUT = tsstat_out, --- STORAGE = plain ---); --- ---CREATE FUNCTION ts_accum(tsstat,tsvector) ---RETURNS tsstat ---AS 'MODULE_PATHNAME' ---LANGUAGE C RETURNS NULL ON NULL INPUT; --- ---CREATE FUNCTION ts_accum_finish(tsstat) --- RETURNS setof statinfo --- as 'MODULE_PATHNAME' --- LANGUAGE C --- RETURNS NULL ON NULL INPUT; --- ---CREATE AGGREGATE stat ( --- BASETYPE=tsvector, --- SFUNC=ts_accum, --- STYPE=tsstat, --- FINALFUNC = ts_accum_finish, --- initcond = '' ---); - -CREATE FUNCTION stat(text) - RETURNS setof statinfo - as 'MODULE_PATHNAME', 'ts_stat' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION stat(text,text) - RETURNS setof statinfo - as 'MODULE_PATHNAME', 'ts_stat' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---reset - just for debuging -CREATE FUNCTION reset_tsearch() - RETURNS void - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---get cover (debug for rank_cd) -CREATE FUNCTION get_covers(tsvector,tsquery) - RETURNS text - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---debug function -create type tsdebug as ( - ts_name text, - tok_type text, - description text, - token text, - dict_name text[], - "tsvector" tsvector -); - -CREATE FUNCTION _get_parser_from_curcfg() -RETURNS text as -' select prs_name from pg_ts_cfg where oid = show_curcfg() ' -LANGUAGE SQL RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION ts_debug(text) -RETURNS setof tsdebug as ' -select - m.ts_name, - t.alias as tok_type, - t.descr as description, - p.token, - m.dict_name, - strip(to_tsvector(p.token)) as tsvector -from - parse( _get_parser_from_curcfg(), $1 ) as p, - token_type() as t, - pg_ts_cfgmap as m, - pg_ts_cfg as c -where - t.tokid=p.tokid and - t.alias = m.tok_alias and - m.ts_name=c.ts_name and - c.oid=show_curcfg() -' LANGUAGE SQL RETURNS NULL ON NULL INPUT; - ---compare functions -CREATE FUNCTION tsvector_cmp(tsvector,tsvector) -RETURNS int4 -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsvector_lt(tsvector,tsvector) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsvector_le(tsvector,tsvector) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsvector_eq(tsvector,tsvector) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsvector_ge(tsvector,tsvector) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsvector_gt(tsvector,tsvector) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsvector_ne(tsvector,tsvector) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OPERATOR < ( - LEFTARG = tsvector, - RIGHTARG = tsvector, - PROCEDURE = tsvector_lt, - COMMUTATOR = '>', - NEGATOR = '>=', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR <= ( - LEFTARG = tsvector, - RIGHTARG = tsvector, - PROCEDURE = tsvector_le, - COMMUTATOR = '>=', - NEGATOR = '>', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR >= ( - LEFTARG = tsvector, - RIGHTARG = tsvector, - PROCEDURE = tsvector_ge, - COMMUTATOR = '<=', - NEGATOR = '<', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR > ( - LEFTARG = tsvector, - RIGHTARG = tsvector, - PROCEDURE = tsvector_gt, - COMMUTATOR = '<', - NEGATOR = '<=', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR = ( - LEFTARG = tsvector, - RIGHTARG = tsvector, - PROCEDURE = tsvector_eq, - COMMUTATOR = '=', - NEGATOR = '<>', - RESTRICT = eqsel, - JOIN = eqjoinsel, - SORT1 = '<', - SORT2 = '<' -); - -CREATE OPERATOR <> ( - LEFTARG = tsvector, - RIGHTARG = tsvector, - PROCEDURE = tsvector_ne, - COMMUTATOR = '<>', - NEGATOR = '=', - RESTRICT = neqsel, - JOIN = neqjoinsel -); - -CREATE OPERATOR CLASS tsvector_ops - DEFAULT FOR TYPE tsvector USING btree AS - OPERATOR 1 < , - OPERATOR 2 <= , - OPERATOR 3 = , - OPERATOR 4 >= , - OPERATOR 5 > , - FUNCTION 1 tsvector_cmp(tsvector, tsvector); - -----------------Compare functions and operators for tsquery -CREATE OR REPLACE FUNCTION tsquery_cmp(tsquery,tsquery) -RETURNS int4 -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION tsquery_lt(tsquery,tsquery) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION tsquery_le(tsquery,tsquery) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION tsquery_eq(tsquery,tsquery) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION tsquery_ge(tsquery,tsquery) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION tsquery_gt(tsquery,tsquery) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION tsquery_ne(tsquery,tsquery) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - - -CREATE OPERATOR < ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsquery_lt, - COMMUTATOR = '>', - NEGATOR = '>=', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR <= ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsquery_le, - COMMUTATOR = '>=', - NEGATOR = '>', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR >= ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsquery_ge, - COMMUTATOR = '<=', - NEGATOR = '<', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR > ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsquery_gt, - COMMUTATOR = '<', - NEGATOR = '<=', - RESTRICT = contsel, - JOIN = contjoinsel -); - - -CREATE OPERATOR = ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsquery_eq, - COMMUTATOR = '=', - NEGATOR = '<>', - RESTRICT = eqsel, - JOIN = eqjoinsel, - SORT1 = '<', - SORT2 = '<' -); - -CREATE OPERATOR <> ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsquery_ne, - COMMUTATOR = '<>', - NEGATOR = '=', - RESTRICT = neqsel, - JOIN = neqjoinsel -); - -CREATE OPERATOR CLASS tsquery_ops - DEFAULT FOR TYPE tsquery USING btree AS - OPERATOR 1 < , - OPERATOR 2 <= , - OPERATOR 3 = , - OPERATOR 4 >= , - OPERATOR 5 > , - FUNCTION 1 tsquery_cmp(tsquery, tsquery); - -CREATE OR REPLACE FUNCTION numnode(tsquery) - RETURNS int4 - as 'MODULE_PATHNAME', 'tsquery_numnode' - LANGUAGE C - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION tsquery_and(tsquery,tsquery) - RETURNS tsquery - as 'MODULE_PATHNAME', 'tsquery_and' - LANGUAGE C - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OPERATOR && ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsquery_and, - COMMUTATOR = '&&', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OR REPLACE FUNCTION tsquery_or(tsquery,tsquery) - RETURNS tsquery - as 'MODULE_PATHNAME', 'tsquery_or' - LANGUAGE C - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OPERATOR || ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsquery_or, - COMMUTATOR = '||', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OR REPLACE FUNCTION tsquery_not(tsquery) - RETURNS tsquery - as 'MODULE_PATHNAME', 'tsquery_not' - LANGUAGE C - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OPERATOR !! ( - RIGHTARG = tsquery, - PROCEDURE = tsquery_not -); - ---------------rewrite subsystem - -CREATE OR REPLACE FUNCTION rewrite(tsquery, text) - RETURNS tsquery - as 'MODULE_PATHNAME', 'tsquery_rewrite' - LANGUAGE C - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION rewrite(tsquery, tsquery, tsquery) - RETURNS tsquery - as 'MODULE_PATHNAME', 'tsquery_rewrite_query' - LANGUAGE C - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION rewrite_accum(tsquery,tsquery[]) - RETURNS tsquery - AS 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE OR REPLACE FUNCTION rewrite_finish(tsquery) - RETURNS tsquery - as 'MODULE_PATHNAME' - LANGUAGE C; - -CREATE AGGREGATE rewrite ( - BASETYPE=tsquery[], - SFUNC=rewrite_accum, - STYPE=tsquery, - FINALFUNC = rewrite_finish -); - -CREATE OR REPLACE FUNCTION tsq_mcontains(tsquery, tsquery) - RETURNS bool - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OR REPLACE FUNCTION tsq_mcontained(tsquery, tsquery) - RETURNS bool - as 'MODULE_PATHNAME' - LANGUAGE C - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OPERATOR @> ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsq_mcontains, - COMMUTATOR = '<@', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR <@ ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsq_mcontained, - COMMUTATOR = '@>', - RESTRICT = contsel, - JOIN = contjoinsel -); - --- obsolete: -CREATE OPERATOR @ ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsq_mcontains, - COMMUTATOR = '~', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR ~ ( - LEFTARG = tsquery, - RIGHTARG = tsquery, - PROCEDURE = tsq_mcontained, - COMMUTATOR = '@', - RESTRICT = contsel, - JOIN = contjoinsel -); - ------------gist support of rewrite------------------ - -CREATE FUNCTION gtsq_in(cstring) -RETURNS gtsq -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION gtsq_out(gtsq) -RETURNS cstring -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE TYPE gtsq ( - INTERNALLENGTH = 8, - INPUT = gtsq_in, - OUTPUT = gtsq_out -); - -CREATE FUNCTION gtsq_consistent(gtsq,internal,int4) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsq_compress(internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsq_decompress(internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsq_penalty(internal,internal,internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION gtsq_picksplit(internal, internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsq_union(bytea, internal) -RETURNS _int4 -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE FUNCTION gtsq_same(gtsq, gtsq, internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE OPERATOR CLASS gist_tp_tsquery_ops -DEFAULT FOR TYPE tsquery USING gist -AS - OPERATOR 7 @> (tsquery, tsquery) RECHECK, - OPERATOR 8 <@ (tsquery, tsquery) RECHECK, - OPERATOR 13 @ (tsquery, tsquery) RECHECK, - OPERATOR 14 ~ (tsquery, tsquery) RECHECK, - FUNCTION 1 gtsq_consistent (gtsq, internal, int4), - FUNCTION 2 gtsq_union (bytea, internal), - FUNCTION 3 gtsq_compress (internal), - FUNCTION 4 gtsq_decompress (internal), - FUNCTION 5 gtsq_penalty (internal, internal, internal), - FUNCTION 6 gtsq_picksplit (internal, internal), - FUNCTION 7 gtsq_same (gtsq, gtsq, internal), - STORAGE gtsq; - ---GIN support function -CREATE FUNCTION gin_extract_tsvector(tsvector,internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION gin_extract_tsquery(tsquery,internal,internal) -RETURNS internal -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION gin_ts_consistent(internal,internal,tsquery) -RETURNS bool -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE OPERATOR @@@ ( - LEFTARG = tsvector, - RIGHTARG = tsquery, - PROCEDURE = exectsq, - COMMUTATOR = '@@@', - RESTRICT = contsel, - JOIN = contjoinsel -); -CREATE OPERATOR @@@ ( - LEFTARG = tsquery, - RIGHTARG = tsvector, - PROCEDURE = rexectsq, - COMMUTATOR = '@@@', - RESTRICT = contsel, - JOIN = contjoinsel -); - -CREATE OPERATOR CLASS gin_tsvector_ops -DEFAULT FOR TYPE tsvector USING gin -AS - OPERATOR 1 @@ (tsvector, tsquery), - OPERATOR 2 @@@ (tsvector, tsquery) RECHECK, - FUNCTION 1 bttextcmp(text, text), - FUNCTION 2 gin_extract_tsvector(tsvector,internal), - FUNCTION 3 gin_extract_tsquery(tsquery,internal,internal), - FUNCTION 4 gin_ts_consistent(internal,internal,tsquery), - STORAGE text; - - ---example of ISpell dictionary ---update pg_ts_dict set dict_initoption='DictFile="/usr/local/share/ispell/russian.dict" ,AffFile ="/usr/local/share/ispell/russian.aff", StopFile="/usr/local/share/ispell/russian.stop"' where dict_name='ispell_template'; - ---example of synonym dict ---update pg_ts_dict set dict_initoption='/usr/local/share/ispell/english.syn' where dict_name='synonym'; - ---example of thesaurus dict ---update pg_ts_dict set dict_initoption='DictFile="contrib/thesaurus", Dictionary="en_stem"' where dict_name='thesaurus_template'; ---update pg_ts_cfgmap set dict_name = '{thesaurus_template,en_stem}' where dict_name = '{en_stem}'; -END; diff --git a/contrib/tsearch2/tsearch2.c b/contrib/tsearch2/tsearch2.c new file mode 100644 index 000000000000..c0ba00eafe7b --- /dev/null +++ b/contrib/tsearch2/tsearch2.c @@ -0,0 +1,571 @@ +/*------------------------------------------------------------------------- + * + * tsearch2.c + * Backwards-compatibility package for old contrib/tsearch2 API + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/contrib/tsearch2/tsearch2.c,v 1.5.2.1 2009/01/28 18:32:55 teodor Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/namespace.h" +#include "catalog/pg_type.h" +#include "commands/trigger.h" +#include "fmgr.h" +#include "tsearch/ts_utils.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/syscache.h" + +PG_MODULE_MAGIC; + +static Oid current_dictionary_oid = InvalidOid; +static Oid current_parser_oid = InvalidOid; + +/* insert given value at argument position 0 */ +#define INSERT_ARGUMENT0(argument, isnull) \ + do { \ + int i; \ + for (i = fcinfo->nargs; i > 0; i--) \ + { \ + fcinfo->arg[i] = fcinfo->arg[i-1]; \ + fcinfo->argnull[i] = fcinfo->argnull[i-1]; \ + } \ + fcinfo->arg[0] = (argument); \ + fcinfo->argnull[0] = (isnull); \ + fcinfo->nargs++; \ + } while (0) + +#define TextPGetCString(t) \ + DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(t))) +#define CStringGetTextP(c) \ + DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(c))) + +#define TextGetObjectId(infunction, text) \ + DatumGetObjectId(DirectFunctionCall1(infunction, \ + DirectFunctionCall1(textout, PointerGetDatum(text)))) + +#define UNSUPPORTED_FUNCTION(name) \ + Datum name(PG_FUNCTION_ARGS); \ + Datum \ + name(PG_FUNCTION_ARGS) \ + { \ + ereport(ERROR, \ + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\ + errmsg("function %s is no longer supported", \ + format_procedure(fcinfo->flinfo->fn_oid)), \ + errhint("Switch to new tsearch functionality."))); \ + /* keep compiler quiet */ \ + PG_RETURN_NULL(); \ + } \ + PG_FUNCTION_INFO_V1(name) + +static Oid GetCurrentDict(void); +static Oid GetCurrentParser(void); + +Datum tsa_lexize_byname(PG_FUNCTION_ARGS); +Datum tsa_lexize_bycurrent(PG_FUNCTION_ARGS); +Datum tsa_set_curdict(PG_FUNCTION_ARGS); +Datum tsa_set_curdict_byname(PG_FUNCTION_ARGS); +Datum tsa_token_type_current(PG_FUNCTION_ARGS); +Datum tsa_set_curprs(PG_FUNCTION_ARGS); +Datum tsa_set_curprs_byname(PG_FUNCTION_ARGS); +Datum tsa_parse_current(PG_FUNCTION_ARGS); +Datum tsa_set_curcfg(PG_FUNCTION_ARGS); +Datum tsa_set_curcfg_byname(PG_FUNCTION_ARGS); +Datum tsa_to_tsvector_name(PG_FUNCTION_ARGS); +Datum tsa_to_tsquery_name(PG_FUNCTION_ARGS); +Datum tsa_plainto_tsquery_name(PG_FUNCTION_ARGS); +Datum tsa_headline_byname(PG_FUNCTION_ARGS); +Datum tsa_ts_stat(PG_FUNCTION_ARGS); +Datum tsa_tsearch2(PG_FUNCTION_ARGS); +Datum tsa_rewrite_accum(PG_FUNCTION_ARGS); +Datum tsa_rewrite_finish(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(tsa_lexize_byname); +PG_FUNCTION_INFO_V1(tsa_lexize_bycurrent); +PG_FUNCTION_INFO_V1(tsa_set_curdict); +PG_FUNCTION_INFO_V1(tsa_set_curdict_byname); +PG_FUNCTION_INFO_V1(tsa_token_type_current); +PG_FUNCTION_INFO_V1(tsa_set_curprs); +PG_FUNCTION_INFO_V1(tsa_set_curprs_byname); +PG_FUNCTION_INFO_V1(tsa_parse_current); +PG_FUNCTION_INFO_V1(tsa_set_curcfg); +PG_FUNCTION_INFO_V1(tsa_set_curcfg_byname); +PG_FUNCTION_INFO_V1(tsa_to_tsvector_name); +PG_FUNCTION_INFO_V1(tsa_to_tsquery_name); +PG_FUNCTION_INFO_V1(tsa_plainto_tsquery_name); +PG_FUNCTION_INFO_V1(tsa_headline_byname); +PG_FUNCTION_INFO_V1(tsa_ts_stat); +PG_FUNCTION_INFO_V1(tsa_tsearch2); +PG_FUNCTION_INFO_V1(tsa_rewrite_accum); +PG_FUNCTION_INFO_V1(tsa_rewrite_finish); + + +/* + * List of unsupported functions + * + * The parser and dictionary functions are defined only so that the former + * contents of pg_ts_parser and pg_ts_dict can be loaded into the system, + * for ease of reference while creating the new tsearch configuration. + */ + +UNSUPPORTED_FUNCTION(tsa_dex_init); +UNSUPPORTED_FUNCTION(tsa_dex_lexize); + +UNSUPPORTED_FUNCTION(tsa_snb_en_init); +UNSUPPORTED_FUNCTION(tsa_snb_lexize); +UNSUPPORTED_FUNCTION(tsa_snb_ru_init_koi8); +UNSUPPORTED_FUNCTION(tsa_snb_ru_init_utf8); +UNSUPPORTED_FUNCTION(tsa_snb_ru_init); + +UNSUPPORTED_FUNCTION(tsa_spell_init); +UNSUPPORTED_FUNCTION(tsa_spell_lexize); + +UNSUPPORTED_FUNCTION(tsa_syn_init); +UNSUPPORTED_FUNCTION(tsa_syn_lexize); + +UNSUPPORTED_FUNCTION(tsa_thesaurus_init); +UNSUPPORTED_FUNCTION(tsa_thesaurus_lexize); + +UNSUPPORTED_FUNCTION(tsa_prsd_start); +UNSUPPORTED_FUNCTION(tsa_prsd_getlexeme); +UNSUPPORTED_FUNCTION(tsa_prsd_end); +UNSUPPORTED_FUNCTION(tsa_prsd_lextype); +UNSUPPORTED_FUNCTION(tsa_prsd_headline); + +UNSUPPORTED_FUNCTION(tsa_reset_tsearch); +UNSUPPORTED_FUNCTION(tsa_get_covers); + + +/* + * list of redefined functions + */ + +/* lexize(text, text) */ +Datum +tsa_lexize_byname(PG_FUNCTION_ARGS) +{ + text *dictname = PG_GETARG_TEXT_P(0); + Datum arg1 = PG_GETARG_DATUM(1); + + return DirectFunctionCall2(ts_lexize, + ObjectIdGetDatum(TextGetObjectId(regdictionaryin, dictname)), + arg1); +} + +/* lexize(text) */ +Datum +tsa_lexize_bycurrent(PG_FUNCTION_ARGS) +{ + Datum arg0 = PG_GETARG_DATUM(0); + Oid id = GetCurrentDict(); + + return DirectFunctionCall2(ts_lexize, + ObjectIdGetDatum(id), + arg0); +} + +/* set_curdict(int) */ +Datum +tsa_set_curdict(PG_FUNCTION_ARGS) +{ + Oid dict_oid = PG_GETARG_OID(0); + + if (!SearchSysCacheExists(TSDICTOID, + ObjectIdGetDatum(dict_oid), + 0, 0, 0)) + elog(ERROR, "cache lookup failed for text search dictionary %u", + dict_oid); + + current_dictionary_oid = dict_oid; + + PG_RETURN_VOID(); +} + +/* set_curdict(text) */ +Datum +tsa_set_curdict_byname(PG_FUNCTION_ARGS) +{ + text *name = PG_GETARG_TEXT_P(0); + Oid dict_oid; + + dict_oid = TSDictionaryGetDictid(stringToQualifiedNameList(TextPGetCString(name)), false); + + current_dictionary_oid = dict_oid; + + PG_RETURN_VOID(); +} + +/* token_type() */ +Datum +tsa_token_type_current(PG_FUNCTION_ARGS) +{ + INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false); + return ts_token_type_byid(fcinfo); +} + +/* set_curprs(int) */ +Datum +tsa_set_curprs(PG_FUNCTION_ARGS) +{ + Oid parser_oid = PG_GETARG_OID(0); + + if (!SearchSysCacheExists(TSPARSEROID, + ObjectIdGetDatum(parser_oid), + 0, 0, 0)) + elog(ERROR, "cache lookup failed for text search parser %u", + parser_oid); + + current_parser_oid = parser_oid; + + PG_RETURN_VOID(); +} + +/* set_curprs(text) */ +Datum +tsa_set_curprs_byname(PG_FUNCTION_ARGS) +{ + text *name = PG_GETARG_TEXT_P(0); + Oid parser_oid; + + parser_oid = TSParserGetPrsid(stringToQualifiedNameList(TextPGetCString(name)), false); + + current_parser_oid = parser_oid; + + PG_RETURN_VOID(); +} + +/* parse(text) */ +Datum +tsa_parse_current(PG_FUNCTION_ARGS) +{ + INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false); + return ts_parse_byid(fcinfo); +} + +/* set_curcfg(int) */ +Datum +tsa_set_curcfg(PG_FUNCTION_ARGS) +{ + Oid arg0 = PG_GETARG_OID(0); + char *name; + + name = DatumGetCString(DirectFunctionCall1(regconfigout, + ObjectIdGetDatum(arg0))); + + set_config_option("default_text_search_config", name, + PGC_USERSET, + PGC_S_SESSION, + GUC_ACTION_SET, + true); + + PG_RETURN_VOID(); +} + +/* set_curcfg(text) */ +Datum +tsa_set_curcfg_byname(PG_FUNCTION_ARGS) +{ + text *arg0 = PG_GETARG_TEXT_P(0); + char *name; + + name = TextPGetCString(arg0); + + set_config_option("default_text_search_config", name, + PGC_USERSET, + PGC_S_SESSION, + GUC_ACTION_SET, + true); + + PG_RETURN_VOID(); +} + +/* to_tsvector(text, text) */ +Datum +tsa_to_tsvector_name(PG_FUNCTION_ARGS) +{ + text *cfgname = PG_GETARG_TEXT_P(0); + Datum arg1 = PG_GETARG_DATUM(1); + Oid config_oid; + + config_oid = TextGetObjectId(regconfigin, cfgname); + + return DirectFunctionCall2(to_tsvector_byid, + ObjectIdGetDatum(config_oid), arg1); +} + +/* to_tsquery(text, text) */ +Datum +tsa_to_tsquery_name(PG_FUNCTION_ARGS) +{ + text *cfgname = PG_GETARG_TEXT_P(0); + Datum arg1 = PG_GETARG_DATUM(1); + Oid config_oid; + + config_oid = TextGetObjectId(regconfigin, cfgname); + + return DirectFunctionCall2(to_tsquery_byid, + ObjectIdGetDatum(config_oid), arg1); +} + + +/* plainto_tsquery(text, text) */ +Datum +tsa_plainto_tsquery_name(PG_FUNCTION_ARGS) +{ + text *cfgname = PG_GETARG_TEXT_P(0); + Datum arg1 = PG_GETARG_DATUM(1); + Oid config_oid; + + config_oid = TextGetObjectId(regconfigin, cfgname); + + return DirectFunctionCall2(plainto_tsquery_byid, + ObjectIdGetDatum(config_oid), arg1); +} + +/* headline(text, text, tsquery [,text]) */ +Datum +tsa_headline_byname(PG_FUNCTION_ARGS) +{ + Datum arg0 = PG_GETARG_DATUM(0); + Datum arg1 = PG_GETARG_DATUM(1); + Datum arg2 = PG_GETARG_DATUM(2); + Datum result; + Oid config_oid; + + /* first parameter has to be converted to oid */ + config_oid = DatumGetObjectId(DirectFunctionCall1(regconfigin, + DirectFunctionCall1(textout, arg0))); + + if (PG_NARGS() == 3) + result = DirectFunctionCall3(ts_headline_byid, + ObjectIdGetDatum(config_oid), arg1, arg2); + else + { + Datum arg3 = PG_GETARG_DATUM(3); + + result = DirectFunctionCall4(ts_headline_byid_opt, + ObjectIdGetDatum(config_oid), + arg1, arg2, arg3); + } + + return result; +} + +/* + * tsearch2 version of update trigger + * + * We pass this on to the core trigger after inserting the default text + * search configuration name as the second argument. Note that this isn't + * a complete implementation of the original functionality; tsearch2 allowed + * transformation function names to be included in the list. However, that + * is deliberately removed as being a security risk. + */ +Datum +tsa_tsearch2(PG_FUNCTION_ARGS) +{ + TriggerData *trigdata; + Trigger *trigger; + char **tgargs, + **tgargs_old; + int i; + Datum res; + + /* Check call context */ + if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ + elog(ERROR, "tsvector_update_trigger: not fired by trigger manager"); + + trigdata = (TriggerData *) fcinfo->context; + trigger = trigdata->tg_trigger; + + if (trigger->tgnargs < 2) + elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)"); + + /* create space for configuration name */ + tgargs = (char **) palloc((trigger->tgnargs + 1) * sizeof(char *)); + tgargs[0] = trigger->tgargs[0]; + for (i = 1; i < trigger->tgnargs; i++) + tgargs[i + 1] = trigger->tgargs[i]; + + tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config", + NULL)); + tgargs_old = trigger->tgargs; + trigger->tgargs = tgargs; + trigger->tgnargs++; + + res = tsvector_update_trigger_byid(fcinfo); + + /* restore old trigger data */ + trigger->tgargs = tgargs_old; + trigger->tgnargs--; + + pfree(tgargs[1]); + pfree(tgargs); + + return res; +} + + +Datum +tsa_rewrite_accum(PG_FUNCTION_ARGS) +{ + TSQuery acc; + ArrayType *qa; + TSQuery q; + QTNode *qex = NULL, + *subs = NULL, + *acctree = NULL; + bool isfind = false; + Datum *elemsp; + int nelemsp; + MemoryContext aggcontext; + MemoryContext oldcontext; + + aggcontext = ((AggState *) fcinfo->context)->aggcontext; + + if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) + { + acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); + SET_VARSIZE(acc, HDRSIZETQ); + acc->size = 0; + } + else + acc = PG_GETARG_TSQUERY(0); + + if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL) + PG_RETURN_TSQUERY(acc); + else + qa = PG_GETARG_ARRAYTYPE_P_COPY(1); + + if (ARR_NDIM(qa) != 1) + elog(ERROR, "array must be one-dimensional, not %d dimensions", + ARR_NDIM(qa)); + if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) + elog(ERROR, "array must have three elements"); + if (ARR_ELEMTYPE(qa) != TSQUERYOID) + elog(ERROR, "array must contain tsquery elements"); + + deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp); + + q = DatumGetTSQuery(elemsp[0]); + if (q->size == 0) + { + pfree(elemsp); + PG_RETURN_POINTER(acc); + } + + if (!acc->size) + { + if (VARSIZE(acc) > HDRSIZETQ) + { + pfree(elemsp); + PG_RETURN_POINTER(acc); + } + else + acctree = QT2QTN(GETQUERY(q), GETOPERAND(q)); + } + else + acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc)); + + QTNTernary(acctree); + QTNSort(acctree); + + q = DatumGetTSQuery(elemsp[1]); + if (q->size == 0) + { + pfree(elemsp); + PG_RETURN_POINTER(acc); + } + qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); + QTNTernary(qex); + QTNSort(qex); + + q = DatumGetTSQuery(elemsp[2]); + if (q->size) + subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); + + acctree = findsubquery(acctree, qex, subs, &isfind); + + if (isfind || !acc->size) + { + /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ + if (acctree) + { + QTNBinary(acctree); + oldcontext = MemoryContextSwitchTo(aggcontext); + acc = QTN2QT(acctree); + MemoryContextSwitchTo(oldcontext); + } + else + { + acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); + SET_VARSIZE(acc, HDRSIZETQ); + acc->size = 0; + } + } + + pfree(elemsp); + QTNFree(qex); + QTNFree(subs); + QTNFree(acctree); + + PG_RETURN_TSQUERY(acc); +} + +Datum +tsa_rewrite_finish(PG_FUNCTION_ARGS) +{ + TSQuery acc = PG_GETARG_TSQUERY(0); + TSQuery rewrited; + + if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0) + { + rewrited = (TSQuery) palloc(HDRSIZETQ); + SET_VARSIZE(rewrited, HDRSIZETQ); + rewrited->size = 0; + } + else + { + rewrited = (TSQuery) palloc(VARSIZE(acc)); + memcpy(rewrited, acc, VARSIZE(acc)); + pfree(acc); + } + + PG_RETURN_POINTER(rewrited); +} + + +/* + * Get Oid of current dictionary + */ +static Oid +GetCurrentDict(void) +{ + if (current_dictionary_oid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("no current dictionary"), + errhint("Execute SELECT set_curdict(...)."))); + + return current_dictionary_oid; +} + +/* + * Get Oid of current parser + * + * Here, it seems reasonable to select the "default" parser if none has been + * set. + */ +static Oid +GetCurrentParser(void) +{ + if (current_parser_oid == InvalidOid) + current_parser_oid = TSParserGetPrsid(stringToQualifiedNameList("pg_catalog.default"), false); + return current_parser_oid; +} diff --git a/contrib/tsearch2/tsearch2.sql.in b/contrib/tsearch2/tsearch2.sql.in new file mode 100644 index 000000000000..13f22d6adfdf --- /dev/null +++ b/contrib/tsearch2/tsearch2.sql.in @@ -0,0 +1,575 @@ +/* $PostgreSQL: pgsql/contrib/tsearch2/tsearch2.sql.in,v 1.4 2007/11/28 19:33:04 tgl Exp $ */ + +-- Adjust this setting to control where the objects get created. +SET search_path = public; + +-- These domains are just to catch schema-qualified references to the +-- old data types. +CREATE DOMAIN tsvector AS pg_catalog.tsvector; +CREATE DOMAIN tsquery AS pg_catalog.tsquery; +CREATE DOMAIN gtsvector AS pg_catalog.gtsvector; +CREATE DOMAIN gtsq AS pg_catalog.text; + +--dict interface +CREATE FUNCTION lexize(oid, text) + RETURNS _text + as 'ts_lexize' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION lexize(text, text) + RETURNS _text + as 'MODULE_PATHNAME', 'tsa_lexize_byname' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION lexize(text) + RETURNS _text + as 'MODULE_PATHNAME', 'tsa_lexize_bycurrent' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION set_curdict(int) + RETURNS void + as 'MODULE_PATHNAME', 'tsa_set_curdict' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION set_curdict(text) + RETURNS void + as 'MODULE_PATHNAME', 'tsa_set_curdict_byname' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +--built-in dictionaries +CREATE FUNCTION dex_init(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_dex_init' + LANGUAGE C; + +CREATE FUNCTION dex_lexize(internal,internal,int4) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_dex_lexize' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION snb_en_init(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_snb_en_init' + LANGUAGE C; + +CREATE FUNCTION snb_lexize(internal,internal,int4) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_snb_lexize' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION snb_ru_init_koi8(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_snb_ru_init_koi8' + LANGUAGE C; + +CREATE FUNCTION snb_ru_init_utf8(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_snb_ru_init_utf8' + LANGUAGE C; + +CREATE FUNCTION snb_ru_init(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_snb_ru_init' + LANGUAGE C; + +CREATE FUNCTION spell_init(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_spell_init' + LANGUAGE C; + +CREATE FUNCTION spell_lexize(internal,internal,int4) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_spell_lexize' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION syn_init(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_syn_init' + LANGUAGE C; + +CREATE FUNCTION syn_lexize(internal,internal,int4) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_syn_lexize' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION thesaurus_init(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_thesaurus_init' + LANGUAGE C; + +CREATE FUNCTION thesaurus_lexize(internal,internal,int4,internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_thesaurus_lexize' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +--sql-level interface +CREATE TYPE tokentype + as (tokid int4, alias text, descr text); + +CREATE FUNCTION token_type(int4) + RETURNS setof tokentype + as 'ts_token_type_byid' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT + ROWS 16; + +CREATE FUNCTION token_type(text) + RETURNS setof tokentype + as 'ts_token_type_byname' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT + ROWS 16; + +CREATE FUNCTION token_type() + RETURNS setof tokentype + as 'MODULE_PATHNAME', 'tsa_token_type_current' + LANGUAGE C + RETURNS NULL ON NULL INPUT + ROWS 16; + +CREATE FUNCTION set_curprs(int) + RETURNS void + as 'MODULE_PATHNAME', 'tsa_set_curprs' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION set_curprs(text) + RETURNS void + as 'MODULE_PATHNAME', 'tsa_set_curprs_byname' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE TYPE tokenout + as (tokid int4, token text); + +CREATE FUNCTION parse(oid,text) + RETURNS setof tokenout + as 'ts_parse_byid' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION parse(text,text) + RETURNS setof tokenout + as 'ts_parse_byname' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION parse(text) + RETURNS setof tokenout + as 'MODULE_PATHNAME', 'tsa_parse_current' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +--default parser +CREATE FUNCTION prsd_start(internal,int4) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_prsd_start' + LANGUAGE C; + +CREATE FUNCTION prsd_getlexeme(internal,internal,internal) + RETURNS int4 + as 'MODULE_PATHNAME', 'tsa_prsd_getlexeme' + LANGUAGE C; + +CREATE FUNCTION prsd_end(internal) + RETURNS void + as 'MODULE_PATHNAME', 'tsa_prsd_end' + LANGUAGE C; + +CREATE FUNCTION prsd_lextype(internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_prsd_lextype' + LANGUAGE C; + +CREATE FUNCTION prsd_headline(internal,internal,internal) + RETURNS internal + as 'MODULE_PATHNAME', 'tsa_prsd_headline' + LANGUAGE C; + +--tsearch config +CREATE FUNCTION set_curcfg(int) + RETURNS void + as 'MODULE_PATHNAME', 'tsa_set_curcfg' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION set_curcfg(text) + RETURNS void + as 'MODULE_PATHNAME', 'tsa_set_curcfg_byname' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION show_curcfg() + RETURNS oid + AS 'get_current_ts_config' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT STABLE; + +CREATE FUNCTION length(tsvector) + RETURNS int4 + AS 'tsvector_length' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION to_tsvector(oid, text) + RETURNS tsvector + AS 'to_tsvector_byid' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION to_tsvector(text, text) + RETURNS tsvector + AS 'MODULE_PATHNAME', 'tsa_to_tsvector_name' + LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION to_tsvector(text) + RETURNS tsvector + AS 'to_tsvector' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION strip(tsvector) + RETURNS tsvector + AS 'tsvector_strip' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION setweight(tsvector,"char") + RETURNS tsvector + AS 'tsvector_setweight' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION concat(tsvector,tsvector) + RETURNS tsvector + AS 'tsvector_concat' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION querytree(tsquery) + RETURNS text + AS 'tsquerytree' + LANGUAGE INTERNAL RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION to_tsquery(oid, text) + RETURNS tsquery + AS 'to_tsquery_byid' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION to_tsquery(text, text) + RETURNS tsquery + AS 'MODULE_PATHNAME','tsa_to_tsquery_name' + LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION to_tsquery(text) + RETURNS tsquery + AS 'to_tsquery' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION plainto_tsquery(oid, text) + RETURNS tsquery + AS 'plainto_tsquery_byid' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION plainto_tsquery(text, text) + RETURNS tsquery + AS 'MODULE_PATHNAME','tsa_plainto_tsquery_name' + LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION plainto_tsquery(text) + RETURNS tsquery + AS 'plainto_tsquery' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +--Trigger +CREATE FUNCTION tsearch2() + RETURNS trigger + AS 'MODULE_PATHNAME', 'tsa_tsearch2' + LANGUAGE C; + +--Relevation +CREATE FUNCTION rank(float4[], tsvector, tsquery) + RETURNS float4 + AS 'ts_rank_wtt' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION rank(float4[], tsvector, tsquery, int4) + RETURNS float4 + AS 'ts_rank_wttf' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION rank(tsvector, tsquery) + RETURNS float4 + AS 'ts_rank_tt' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION rank(tsvector, tsquery, int4) + RETURNS float4 + AS 'ts_rank_ttf' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION rank_cd(float4[], tsvector, tsquery) + RETURNS float4 + AS 'ts_rankcd_wtt' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION rank_cd(float4[], tsvector, tsquery, int4) + RETURNS float4 + AS 'ts_rankcd_wttf' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION rank_cd(tsvector, tsquery) + RETURNS float4 + AS 'ts_rankcd_tt' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION rank_cd(tsvector, tsquery, int4) + RETURNS float4 + AS 'ts_rankcd_ttf' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION headline(oid, text, tsquery, text) + RETURNS text + AS 'ts_headline_byid_opt' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION headline(oid, text, tsquery) + RETURNS text + AS 'ts_headline_byid' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION headline(text, text, tsquery, text) + RETURNS text + AS 'MODULE_PATHNAME', 'tsa_headline_byname' + LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION headline(text, text, tsquery) + RETURNS text + AS 'MODULE_PATHNAME', 'tsa_headline_byname' + LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION headline(text, tsquery, text) + RETURNS text + AS 'ts_headline_opt' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION headline(text, tsquery) + RETURNS text + AS 'ts_headline' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +-- CREATE the OPERATOR class +CREATE OPERATOR CLASS gist_tsvector_ops +FOR TYPE tsvector USING gist +AS + OPERATOR 1 @@ (tsvector, tsquery) RECHECK , + FUNCTION 1 gtsvector_consistent (gtsvector, internal, int4), + FUNCTION 2 gtsvector_union (internal, internal), + FUNCTION 3 gtsvector_compress (internal), + FUNCTION 4 gtsvector_decompress (internal), + FUNCTION 5 gtsvector_penalty (internal, internal, internal), + FUNCTION 6 gtsvector_picksplit (internal, internal), + FUNCTION 7 gtsvector_same (gtsvector, gtsvector, internal), + STORAGE gtsvector; + +--stat info +CREATE TYPE statinfo + as (word text, ndoc int4, nentry int4); + +CREATE FUNCTION stat(text) + RETURNS setof statinfo + as 'ts_stat1' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION stat(text,text) + RETURNS setof statinfo + as 'ts_stat2' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT; + +--reset - just for debuging +CREATE FUNCTION reset_tsearch() + RETURNS void + as 'MODULE_PATHNAME', 'tsa_reset_tsearch' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +--get cover (debug for rank_cd) +CREATE FUNCTION get_covers(tsvector,tsquery) + RETURNS text + as 'MODULE_PATHNAME', 'tsa_get_covers' + LANGUAGE C + RETURNS NULL ON NULL INPUT; + +--debug function +create type tsdebug as ( + ts_name text, + tok_type text, + description text, + token text, + dict_name text[], + "tsvector" tsvector +); + +CREATE or replace FUNCTION _get_parser_from_curcfg() +RETURNS text as +$$select prsname::text from pg_catalog.pg_ts_parser p join pg_ts_config c on cfgparser = p.oid where c.oid = show_curcfg();$$ +LANGUAGE SQL RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE FUNCTION ts_debug(text) +RETURNS setof tsdebug as $$ +select + (select c.cfgname::text from pg_catalog.pg_ts_config as c + where c.oid = show_curcfg()), + t.alias as tok_type, + t.descr as description, + p.token, + ARRAY ( SELECT m.mapdict::pg_catalog.regdictionary::pg_catalog.text + FROM pg_catalog.pg_ts_config_map AS m + WHERE m.mapcfg = show_curcfg() AND m.maptokentype = p.tokid + ORDER BY m.mapseqno ) + AS dict_name, + strip(to_tsvector(p.token)) as tsvector +from + parse( _get_parser_from_curcfg(), $1 ) as p, + token_type() as t +where + t.tokid = p.tokid +$$ LANGUAGE SQL RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION numnode(tsquery) + RETURNS int4 + as 'tsquery_numnode' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE OR REPLACE FUNCTION tsquery_and(tsquery,tsquery) + RETURNS tsquery + as 'tsquery_and' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE OR REPLACE FUNCTION tsquery_or(tsquery,tsquery) + RETURNS tsquery + as 'tsquery_or' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE OR REPLACE FUNCTION tsquery_not(tsquery) + RETURNS tsquery + as 'tsquery_not' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +--------------rewrite subsystem + +CREATE OR REPLACE FUNCTION rewrite(tsquery, text) + RETURNS tsquery + as 'tsquery_rewrite_query' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE OR REPLACE FUNCTION rewrite(tsquery, tsquery, tsquery) + RETURNS tsquery + as 'tsquery_rewrite' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE OR REPLACE FUNCTION rewrite_accum(tsquery,tsquery[]) + RETURNS tsquery + AS 'MODULE_PATHNAME', 'tsa_rewrite_accum' + LANGUAGE C; + +CREATE OR REPLACE FUNCTION rewrite_finish(tsquery) + RETURNS tsquery + as 'MODULE_PATHNAME', 'tsa_rewrite_finish' + LANGUAGE C; + +CREATE AGGREGATE rewrite ( + BASETYPE = tsquery[], + SFUNC = rewrite_accum, + STYPE = tsquery, + FINALFUNC = rewrite_finish +); + +CREATE OR REPLACE FUNCTION tsq_mcontains(tsquery, tsquery) + RETURNS bool + as 'tsq_mcontains' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE OR REPLACE FUNCTION tsq_mcontained(tsquery, tsquery) + RETURNS bool + as 'tsq_mcontained' + LANGUAGE INTERNAL + RETURNS NULL ON NULL INPUT IMMUTABLE; + +CREATE OPERATOR CLASS gist_tp_tsquery_ops +FOR TYPE tsquery USING gist +AS + OPERATOR 7 @> (tsquery, tsquery) RECHECK, + OPERATOR 8 <@ (tsquery, tsquery) RECHECK, + FUNCTION 1 gtsquery_consistent (bigint, internal, int4), + FUNCTION 2 gtsquery_union (internal, internal), + FUNCTION 3 gtsquery_compress (internal), + FUNCTION 4 gtsquery_decompress (internal), + FUNCTION 5 gtsquery_penalty (internal, internal, internal), + FUNCTION 6 gtsquery_picksplit (internal, internal), + FUNCTION 7 gtsquery_same (bigint, bigint, internal), + STORAGE bigint; + +CREATE OPERATOR CLASS gin_tsvector_ops +FOR TYPE tsvector USING gin +AS + OPERATOR 1 @@ (tsvector, tsquery), + OPERATOR 2 @@@ (tsvector, tsquery) RECHECK, + FUNCTION 1 bttextcmp(text, text), + FUNCTION 2 gin_extract_tsvector(tsvector,internal), + FUNCTION 3 gin_extract_tsquery(tsquery,internal,smallint), + FUNCTION 4 gin_tsquery_consistent(internal,smallint,tsquery), + STORAGE text; + +CREATE OPERATOR CLASS tsvector_ops +FOR TYPE tsvector USING btree AS + OPERATOR 1 < , + OPERATOR 2 <= , + OPERATOR 3 = , + OPERATOR 4 >= , + OPERATOR 5 > , + FUNCTION 1 tsvector_cmp(tsvector, tsvector); + +CREATE OPERATOR CLASS tsquery_ops +FOR TYPE tsquery USING btree AS + OPERATOR 1 < , + OPERATOR 2 <= , + OPERATOR 3 = , + OPERATOR 4 >= , + OPERATOR 5 > , + FUNCTION 1 tsquery_cmp(tsquery, tsquery); diff --git a/contrib/tsearch2/tsvector.c b/contrib/tsearch2/tsvector.c deleted file mode 100644 index d3251a8401ff..000000000000 --- a/contrib/tsearch2/tsvector.c +++ /dev/null @@ -1,1125 +0,0 @@ -/* - * In/Out definitions for tsvector type - * Internal structure: - * string of values, array of position lexeme in string and it's length - * Teodor Sigaev - */ -#include "postgres.h" - - -#include "access/gist.h" -#include "access/itup.h" -#include "catalog/namespace.h" -#include "commands/trigger.h" -#include "executor/spi.h" -#include "nodes/pg_list.h" -#include "storage/bufpage.h" -#include "utils/builtins.h" -#include "utils/pg_locale.h" -#include "mb/pg_wchar.h" - -#include -#include "tsvector.h" -#include "query.h" -#include "ts_cfg.h" -#include "common.h" - -PG_FUNCTION_INFO_V1(tsvector_in); -Datum tsvector_in(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(tsvector_out); -Datum tsvector_out(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(to_tsvector); -Datum to_tsvector(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(to_tsvector_current); -Datum to_tsvector_current(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(to_tsvector_name); -Datum to_tsvector_name(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(tsearch2); -Datum tsearch2(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(tsvector_length); -Datum tsvector_length(PG_FUNCTION_ARGS); - -/* - * in/out text index type - */ -static int -comparePos(const void *a, const void *b) -{ - if (WEP_GETPOS(*(WordEntryPos *) a) == WEP_GETPOS(*(WordEntryPos *) b)) - return 0; - return (WEP_GETPOS(*(WordEntryPos *) a) > WEP_GETPOS(*(WordEntryPos *) b)) ? 1 : -1; -} - -static int -uniquePos(WordEntryPos * a, int4 l) -{ - WordEntryPos *ptr, - *res; - - res = a; - if (l == 1) - return l; - - qsort((void *) a, l, sizeof(WordEntryPos), comparePos); - - ptr = a + 1; - while (ptr - a < l) - { - if (WEP_GETPOS(*ptr) != WEP_GETPOS(*res)) - { - res++; - *res = *ptr; - if (res - a >= MAXNUMPOS - 1 || WEP_GETPOS(*res) == MAXENTRYPOS - 1) - break; - } - else if (WEP_GETWEIGHT(*ptr) > WEP_GETWEIGHT(*res)) - WEP_SETWEIGHT(*res, WEP_GETWEIGHT(*ptr)); - ptr++; - } - return res + 1 - a; -} - -static int -compareentry(const void *a, const void *b, void *arg) -{ - char *BufferStr = (char *) arg; - - if (((WordEntryIN *) a)->entry.len == ((WordEntryIN *) b)->entry.len) - { - return strncmp(&BufferStr[((WordEntryIN *) a)->entry.pos], - &BufferStr[((WordEntryIN *) b)->entry.pos], - ((WordEntryIN *) a)->entry.len); - } - return (((WordEntryIN *) a)->entry.len > ((WordEntryIN *) b)->entry.len) ? 1 : -1; -} - -static int -uniqueentry(WordEntryIN * a, int4 l, char *buf, int4 *outbuflen) -{ - WordEntryIN *ptr, - *res; - - res = a; - if (l == 1) - { - if (a->entry.haspos) - { - *(uint16 *) (a->pos) = uniquePos(&(a->pos[1]), *(uint16 *) (a->pos)); - *outbuflen = SHORTALIGN(res->entry.len) + (*(uint16 *) (a->pos) + 1) * sizeof(WordEntryPos); - } - return l; - } - - ptr = a + 1; - qsort_arg((void *) a, l, sizeof(WordEntryIN), compareentry, (void *) buf); - - while (ptr - a < l) - { - if (!(ptr->entry.len == res->entry.len && - strncmp(&buf[ptr->entry.pos], &buf[res->entry.pos], res->entry.len) == 0)) - { - if (res->entry.haspos) - { - *(uint16 *) (res->pos) = uniquePos(&(res->pos[1]), *(uint16 *) (res->pos)); - *outbuflen += *(uint16 *) (res->pos) * sizeof(WordEntryPos); - } - *outbuflen += SHORTALIGN(res->entry.len); - res++; - memcpy(res, ptr, sizeof(WordEntryIN)); - } - else if (ptr->entry.haspos) - { - if (res->entry.haspos) - { - int4 len = *(uint16 *) (ptr->pos) + 1 + *(uint16 *) (res->pos); - - res->pos = (WordEntryPos *) repalloc(res->pos, len * sizeof(WordEntryPos)); - memcpy(&(res->pos[*(uint16 *) (res->pos) + 1]), - &(ptr->pos[1]), *(uint16 *) (ptr->pos) * sizeof(WordEntryPos)); - *(uint16 *) (res->pos) += *(uint16 *) (ptr->pos); - pfree(ptr->pos); - } - else - { - res->entry.haspos = 1; - res->pos = ptr->pos; - } - } - ptr++; - } - if (res->entry.haspos) - { - *(uint16 *) (res->pos) = uniquePos(&(res->pos[1]), *(uint16 *) (res->pos)); - *outbuflen += *(uint16 *) (res->pos) * sizeof(WordEntryPos); - } - *outbuflen += SHORTALIGN(res->entry.len); - - return res + 1 - a; -} - -#define WAITWORD 1 -#define WAITENDWORD 2 -#define WAITNEXTCHAR 3 -#define WAITENDCMPLX 4 -#define WAITPOSINFO 5 -#define INPOSINFO 6 -#define WAITPOSDELIM 7 -#define WAITCHARCMPLX 8 - -#define RESIZEPRSBUF \ -do { \ - if ( state->curpos - state->word + pg_database_encoding_max_length() >= state->len ) \ - { \ - int4 clen = state->curpos - state->word; \ - state->len *= 2; \ - state->word = (char*)repalloc( (void*)state->word, state->len ); \ - state->curpos = state->word + clen; \ - } \ -} while (0) - - -int4 -gettoken_tsvector(TI_IN_STATE * state) -{ - int4 oldstate = 0; - - state->curpos = state->word; - state->state = WAITWORD; - state->alen = 0; - - while (1) - { - if (state->state == WAITWORD) - { - if (*(state->prsbuf) == '\0') - return 0; - else if (t_iseq(state->prsbuf, '\'')) - state->state = WAITENDCMPLX; - else if (t_iseq(state->prsbuf, '\\')) - { - state->state = WAITNEXTCHAR; - oldstate = WAITENDWORD; - } - else if (state->oprisdelim && ISOPERATOR(state->prsbuf)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - else if (!t_isspace(state->prsbuf)) - { - COPYCHAR(state->curpos, state->prsbuf); - state->curpos += pg_mblen(state->prsbuf); - state->state = WAITENDWORD; - } - } - else if (state->state == WAITNEXTCHAR) - { - if (*(state->prsbuf) == '\0') - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("there is no escaped character"))); - else - { - RESIZEPRSBUF; - COPYCHAR(state->curpos, state->prsbuf); - state->curpos += pg_mblen(state->prsbuf); - state->state = oldstate; - } - } - else if (state->state == WAITENDWORD) - { - if (t_iseq(state->prsbuf, '\\')) - { - state->state = WAITNEXTCHAR; - oldstate = WAITENDWORD; - } - else if (t_isspace(state->prsbuf) || *(state->prsbuf) == '\0' || - (state->oprisdelim && ISOPERATOR(state->prsbuf))) - { - RESIZEPRSBUF; - if (state->curpos == state->word) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - *(state->curpos) = '\0'; - return 1; - } - else if (t_iseq(state->prsbuf, ':')) - { - if (state->curpos == state->word) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - *(state->curpos) = '\0'; - if (state->oprisdelim) - return 1; - else - state->state = INPOSINFO; - } - else - { - RESIZEPRSBUF; - COPYCHAR(state->curpos, state->prsbuf); - state->curpos += pg_mblen(state->prsbuf); - } - } - else if (state->state == WAITENDCMPLX) - { - if (t_iseq(state->prsbuf, '\'')) - { - state->state = WAITCHARCMPLX; - } - else if (t_iseq(state->prsbuf, '\\')) - { - state->state = WAITNEXTCHAR; - oldstate = WAITENDCMPLX; - } - else if (*(state->prsbuf) == '\0') - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - else - { - RESIZEPRSBUF; - COPYCHAR(state->curpos, state->prsbuf); - state->curpos += pg_mblen(state->prsbuf); - } - } - else if (state->state == WAITCHARCMPLX) - { - if (t_iseq(state->prsbuf, '\'')) - { - RESIZEPRSBUF; - COPYCHAR(state->curpos, state->prsbuf); - state->curpos += pg_mblen(state->prsbuf); - state->state = WAITENDCMPLX; - } - else - { - RESIZEPRSBUF; - *(state->curpos) = '\0'; - if (state->curpos == state->word) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - if (state->oprisdelim) - { - /* state->prsbuf+=pg_mblen(state->prsbuf); */ - return 1; - } - else - state->state = WAITPOSINFO; - continue; /* recheck current character */ - } - } - else if (state->state == WAITPOSINFO) - { - if (t_iseq(state->prsbuf, ':')) - state->state = INPOSINFO; - else - return 1; - } - else if (state->state == INPOSINFO) - { - if (t_isdigit(state->prsbuf)) - { - if (state->alen == 0) - { - state->alen = 4; - state->pos = (WordEntryPos *) palloc(sizeof(WordEntryPos) * state->alen); - *(uint16 *) (state->pos) = 0; - } - else if (*(uint16 *) (state->pos) + 1 >= state->alen) - { - state->alen *= 2; - state->pos = (WordEntryPos *) repalloc(state->pos, sizeof(WordEntryPos) * state->alen); - } - (*(uint16 *) (state->pos))++; - WEP_SETPOS(state->pos[*(uint16 *) (state->pos)], LIMITPOS(atoi(state->prsbuf))); - if (WEP_GETPOS(state->pos[*(uint16 *) (state->pos)]) == 0) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("wrong position info"))); - WEP_SETWEIGHT(state->pos[*(uint16 *) (state->pos)], 0); - state->state = WAITPOSDELIM; - } - else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - } - else if (state->state == WAITPOSDELIM) - { - if (t_iseq(state->prsbuf, ',')) - state->state = INPOSINFO; - else if (t_iseq(state->prsbuf, 'a') || t_iseq(state->prsbuf, 'A') || t_iseq(state->prsbuf, '*')) - { - if (WEP_GETWEIGHT(state->pos[*(uint16 *) (state->pos)])) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - WEP_SETWEIGHT(state->pos[*(uint16 *) (state->pos)], 3); - } - else if (t_iseq(state->prsbuf, 'b') || t_iseq(state->prsbuf, 'B')) - { - if (WEP_GETWEIGHT(state->pos[*(uint16 *) (state->pos)])) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - WEP_SETWEIGHT(state->pos[*(uint16 *) (state->pos)], 2); - } - else if (t_iseq(state->prsbuf, 'c') || t_iseq(state->prsbuf, 'C')) - { - if (WEP_GETWEIGHT(state->pos[*(uint16 *) (state->pos)])) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - WEP_SETWEIGHT(state->pos[*(uint16 *) (state->pos)], 1); - } - else if (t_iseq(state->prsbuf, 'd') || t_iseq(state->prsbuf, 'D')) - { - if (WEP_GETWEIGHT(state->pos[*(uint16 *) (state->pos)])) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - WEP_SETWEIGHT(state->pos[*(uint16 *) (state->pos)], 0); - } - else if (t_isspace(state->prsbuf) || - *(state->prsbuf) == '\0') - return 1; - else if (!t_isdigit(state->prsbuf)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"))); - } - else - /* internal error */ - elog(ERROR, "internal error"); - - /* get next char */ - state->prsbuf += pg_mblen(state->prsbuf); - } - - return 0; -} - -Datum -tsvector_in(PG_FUNCTION_ARGS) -{ - char *buf = PG_GETARG_CSTRING(0); - TI_IN_STATE state; - WordEntryIN *arr; - WordEntry *inarr; - int4 len = 0, - totallen = 64; - tsvector *in; - char *tmpbuf, - *cur; - int4 i, - buflen = 256; - - SET_FUNCOID(); - - pg_verifymbstr(buf, strlen(buf), false); - state.prsbuf = buf; - state.len = 32; - state.word = (char *) palloc(state.len); - state.oprisdelim = false; - - arr = (WordEntryIN *) palloc(sizeof(WordEntryIN) * totallen); - cur = tmpbuf = (char *) palloc(buflen); - while (gettoken_tsvector(&state)) - { - if (len >= totallen) - { - totallen *= 2; - arr = (WordEntryIN *) repalloc((void *) arr, sizeof(WordEntryIN) * totallen); - } - while ((cur - tmpbuf) + (state.curpos - state.word) >= buflen) - { - int4 dist = cur - tmpbuf; - - buflen *= 2; - tmpbuf = (char *) repalloc((void *) tmpbuf, buflen); - cur = tmpbuf + dist; - } - if (state.curpos - state.word >= MAXSTRLEN) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("word is too long"))); - arr[len].entry.len = state.curpos - state.word; - if (cur - tmpbuf > MAXSTRPOS) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("too long value"))); - arr[len].entry.pos = cur - tmpbuf; - memcpy((void *) cur, (void *) state.word, arr[len].entry.len); - cur += arr[len].entry.len; - if (state.alen) - { - arr[len].entry.haspos = 1; - arr[len].pos = state.pos; - } - else - arr[len].entry.haspos = 0; - len++; - } - pfree(state.word); - - if (len > 0) - len = uniqueentry(arr, len, tmpbuf, &buflen); - else - buflen = 0; - totallen = CALCDATASIZE(len, buflen); - in = (tsvector *) palloc0(totallen); - SET_VARSIZE(in, totallen); - in->size = len; - cur = STRPTR(in); - inarr = ARRPTR(in); - for (i = 0; i < len; i++) - { - memcpy((void *) cur, (void *) &tmpbuf[arr[i].entry.pos], arr[i].entry.len); - arr[i].entry.pos = cur - STRPTR(in); - cur += SHORTALIGN(arr[i].entry.len); - if (arr[i].entry.haspos) - { - memcpy(cur, arr[i].pos, (*(uint16 *) arr[i].pos + 1) * sizeof(WordEntryPos)); - cur += (*(uint16 *) arr[i].pos + 1) * sizeof(WordEntryPos); - pfree(arr[i].pos); - } - memcpy(&(inarr[i]), &(arr[i].entry), sizeof(WordEntry)); - } - pfree(tmpbuf); - pfree(arr); - PG_RETURN_POINTER(in); -} - -Datum -tsvector_length(PG_FUNCTION_ARGS) -{ - tsvector *in = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - int4 ret = in->size; - - PG_FREE_IF_COPY(in, 0); - PG_RETURN_INT32(ret); -} - -Datum -tsvector_out(PG_FUNCTION_ARGS) -{ - tsvector *out = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - char *outbuf; - int4 i, - lenbuf = 0, - pp; - WordEntry *ptr = ARRPTR(out); - char *curbegin, - *curin, - *curout; - - lenbuf = out->size * 2 /* '' */ + out->size - 1 /* space */ + 2 /* \0 */ ; - for (i = 0; i < out->size; i++) - { - lenbuf += ptr[i].len * 2 * pg_database_encoding_max_length() /* for escape */ ; - if (ptr[i].haspos) - lenbuf += 7 * POSDATALEN(out, &(ptr[i])); - } - - curout = outbuf = (char *) palloc(lenbuf); - for (i = 0; i < out->size; i++) - { - curbegin = curin = STRPTR(out) + ptr->pos; - if (i != 0) - *curout++ = ' '; - *curout++ = '\''; - while (curin - curbegin < ptr->len) - { - int len = pg_mblen(curin); - - if (t_iseq(curin, '\'')) - { - int4 pos = curout - outbuf; - - outbuf = (char *) repalloc((void *) outbuf, ++lenbuf); - curout = outbuf + pos; - *curout++ = '\''; - } - else if (t_iseq(curin, '\\')) - { - int4 pos = curout - outbuf; - - outbuf = (char *) repalloc((void *) outbuf, ++lenbuf); - curout = outbuf + pos; - *curout++ = '\\'; - } - while (len--) - *curout++ = *curin++; - } - *curout++ = '\''; - if ((pp = POSDATALEN(out, ptr)) != 0) - { - WordEntryPos *wptr; - - *curout++ = ':'; - wptr = POSDATAPTR(out, ptr); - while (pp) - { - sprintf(curout, "%d", WEP_GETPOS(*wptr)); - curout = strchr(curout, '\0'); - switch (WEP_GETWEIGHT(*wptr)) - { - case 3: - *curout++ = 'A'; - break; - case 2: - *curout++ = 'B'; - break; - case 1: - *curout++ = 'C'; - break; - case 0: - default: - break; - } - if (pp > 1) - *curout++ = ','; - pp--; - wptr++; - } - } - ptr++; - } - *curout = '\0'; - outbuf[lenbuf - 1] = '\0'; - PG_FREE_IF_COPY(out, 0); - PG_RETURN_POINTER(outbuf); -} - -static int -compareWORD(const void *a, const void *b) -{ - if (((TSWORD *) a)->len == ((TSWORD *) b)->len) - { - int res = strncmp( - ((TSWORD *) a)->word, - ((TSWORD *) b)->word, - ((TSWORD *) b)->len); - - if (res == 0) - { - if ( ((TSWORD *) a)->pos.pos == ((TSWORD *) b)->pos.pos ) - return 0; - - return (((TSWORD *) a)->pos.pos > ((TSWORD *) b)->pos.pos) ? 1 : -1; - } - return res; - } - return (((TSWORD *) a)->len > ((TSWORD *) b)->len) ? 1 : -1; -} - -static int -uniqueWORD(TSWORD * a, int4 l) -{ - TSWORD *ptr, - *res; - int tmppos; - - if (l == 1) - { - tmppos = LIMITPOS(a->pos.pos); - a->alen = 2; - a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen); - a->pos.apos[0] = 1; - a->pos.apos[1] = tmppos; - return l; - } - - res = a; - ptr = a + 1; - - qsort((void *) a, l, sizeof(TSWORD), compareWORD); - tmppos = LIMITPOS(a->pos.pos); - a->alen = 2; - a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen); - a->pos.apos[0] = 1; - a->pos.apos[1] = tmppos; - - while (ptr - a < l) - { - if (!(ptr->len == res->len && - strncmp(ptr->word, res->word, res->len) == 0)) - { - res++; - res->len = ptr->len; - res->word = ptr->word; - tmppos = LIMITPOS(ptr->pos.pos); - res->alen = 2; - res->pos.apos = (uint16 *) palloc(sizeof(uint16) * res->alen); - res->pos.apos[0] = 1; - res->pos.apos[1] = tmppos; - } - else - { - pfree(ptr->word); - if (res->pos.apos[0] < MAXNUMPOS - 1 && res->pos.apos[res->pos.apos[0]] != MAXENTRYPOS - 1 && - res->pos.apos[res->pos.apos[0]] != LIMITPOS(ptr->pos.pos) ) - { - if (res->pos.apos[0] + 1 >= res->alen) - { - res->alen *= 2; - res->pos.apos = (uint16 *) repalloc(res->pos.apos, sizeof(uint16) * res->alen); - } - if (res->pos.apos[0] == 0 || res->pos.apos[res->pos.apos[0]] != LIMITPOS(ptr->pos.pos)) - { - res->pos.apos[res->pos.apos[0] + 1] = LIMITPOS(ptr->pos.pos); - res->pos.apos[0]++; - } - } - } - ptr++; - } - - return res + 1 - a; -} - -/* - * make value of tsvector - */ -static tsvector * -makevalue(PRSTEXT * prs) -{ - int4 i, - j, - lenstr = 0, - totallen; - tsvector *in; - WordEntry *ptr; - char *str, - *cur; - - prs->curwords = uniqueWORD(prs->words, prs->curwords); - for (i = 0; i < prs->curwords; i++) - { - lenstr += SHORTALIGN(prs->words[i].len); - - if (prs->words[i].alen) - lenstr += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos); - } - - totallen = CALCDATASIZE(prs->curwords, lenstr); - in = (tsvector *) palloc0(totallen); - SET_VARSIZE(in, totallen); - in->size = prs->curwords; - - ptr = ARRPTR(in); - cur = str = STRPTR(in); - for (i = 0; i < prs->curwords; i++) - { - ptr->len = prs->words[i].len; - if (cur - str > MAXSTRPOS) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("value is too big"))); - ptr->pos = cur - str; - memcpy((void *) cur, (void *) prs->words[i].word, prs->words[i].len); - pfree(prs->words[i].word); - cur += SHORTALIGN(prs->words[i].len); - if (prs->words[i].alen) - { - WordEntryPos *wptr; - - ptr->haspos = 1; - *(uint16 *) cur = prs->words[i].pos.apos[0]; - wptr = POSDATAPTR(in, ptr); - for (j = 0; j < *(uint16 *) cur; j++) - { - WEP_SETWEIGHT(wptr[j], 0); - WEP_SETPOS(wptr[j], prs->words[i].pos.apos[j + 1]); - } - cur += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos); - pfree(prs->words[i].pos.apos); - } - else - ptr->haspos = 0; - ptr++; - } - pfree(prs->words); - return in; -} - - -Datum -to_tsvector(PG_FUNCTION_ARGS) -{ - text *in = PG_GETARG_TEXT_P(1); - PRSTEXT prs; - tsvector *out; - TSCfgInfo *cfg; - - SET_FUNCOID(); - cfg = findcfg(PG_GETARG_INT32(0)); - - prs.lenwords = 32; - prs.curwords = 0; - prs.pos = 0; - prs.words = (TSWORD *) palloc(sizeof(TSWORD) * prs.lenwords); - - parsetext_v2(cfg, &prs, VARDATA(in), VARSIZE(in) - VARHDRSZ); - PG_FREE_IF_COPY(in, 1); - - if (prs.curwords) - out = makevalue(&prs); - else - { - pfree(prs.words); - out = palloc(CALCDATASIZE(0, 0)); - SET_VARSIZE(out, CALCDATASIZE(0, 0)); - out->size = 0; - } - PG_RETURN_POINTER(out); -} - -Datum -to_tsvector_name(PG_FUNCTION_ARGS) -{ - text *cfg = PG_GETARG_TEXT_P(0); - Datum res; - - SET_FUNCOID(); - res = DirectFunctionCall3( - to_tsvector, - Int32GetDatum(name2id_cfg(cfg)), - PG_GETARG_DATUM(1), - (Datum) 0 - ); - - PG_FREE_IF_COPY(cfg, 0); - PG_RETURN_DATUM(res); -} - -Datum -to_tsvector_current(PG_FUNCTION_ARGS) -{ - Datum res; - - SET_FUNCOID(); - res = DirectFunctionCall3( - to_tsvector, - Int32GetDatum(get_currcfg()), - PG_GETARG_DATUM(0), - (Datum) 0 - ); - - PG_RETURN_DATUM(res); -} - -static Oid -findFunc(char *fname) -{ - FuncCandidateList clist, - ptr; - Oid funcid = InvalidOid; - List *names = list_make1(makeString(fname)); - - ptr = clist = FuncnameGetCandidates( - names, - 1, - false /* whether expand variadic */, - false /* whether expand default */); - - list_free(names); - - if (!ptr) - return funcid; - - while (ptr) - { - if (ptr->args[0] == TEXTOID && funcid == InvalidOid) - funcid = ptr->oid; - clist = ptr->next; - pfree(ptr); - ptr = clist; - } - - return funcid; -} - -/* - * Trigger - */ -Datum -tsearch2(PG_FUNCTION_ARGS) -{ - TriggerData *trigdata; - Trigger *trigger; - Relation rel; - HeapTuple rettuple = NULL; - int numidxattr, - i; - PRSTEXT prs; - Datum datum = (Datum) 0; - Oid funcoid = InvalidOid; - TSCfgInfo *cfg; - - SET_FUNCOID(); - cfg = findcfg(get_currcfg()); - - if (!CALLED_AS_TRIGGER(fcinfo)) - /* internal error */ - elog(ERROR, "TSearch: Not fired by trigger manager"); - - trigdata = (TriggerData *) fcinfo->context; - if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) - /* internal error */ - elog(ERROR, "TSearch: Cannot process STATEMENT events"); - if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) - /* internal error */ - elog(ERROR, "TSearch: Must be fired BEFORE event"); - - if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) - rettuple = trigdata->tg_trigtuple; - else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) - rettuple = trigdata->tg_newtuple; - else - /* internal error */ - elog(ERROR, "TSearch: Unknown event"); - - trigger = trigdata->tg_trigger; - rel = trigdata->tg_relation; - - if (trigger->tgnargs < 2) - /* internal error */ - elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)"); - - numidxattr = SPI_fnumber(rel->rd_att, trigger->tgargs[0]); - if (numidxattr == SPI_ERROR_NOATTRIBUTE) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("tsvector column \"%s\" does not exist", - trigger->tgargs[0]))); - - prs.lenwords = 32; - prs.curwords = 0; - prs.pos = 0; - prs.words = (TSWORD *) palloc(sizeof(TSWORD) * prs.lenwords); - - /* find all words in indexable column */ - for (i = 1; i < trigger->tgnargs; i++) - { - int numattr; - Oid oidtype; - Datum txt_toasted; - bool isnull; - text *txt; - - numattr = SPI_fnumber(rel->rd_att, trigger->tgargs[i]); - if (numattr == SPI_ERROR_NOATTRIBUTE) - { - funcoid = findFunc(trigger->tgargs[i]); - if (funcoid == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("could not find function or field \"%s\"", - trigger->tgargs[i]))); - - continue; - } - oidtype = SPI_gettypeid(rel->rd_att, numattr); - /* We assume char() and varchar() are binary-equivalent to text */ - if (!(oidtype == TEXTOID || - oidtype == VARCHAROID || - oidtype == BPCHAROID)) - { - elog(WARNING, "TSearch: '%s' is not of character type", - trigger->tgargs[i]); - continue; - } - txt_toasted = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull); - if (isnull) - continue; - - if (funcoid != InvalidOid) - { - text *txttmp = (text *) DatumGetPointer(OidFunctionCall1( - funcoid, - txt_toasted - )); - - txt = (text *) PG_DETOAST_DATUM(PointerGetDatum(txttmp)); - if (txt == txttmp) - txt_toasted = PointerGetDatum(txt); - } - else - txt = (text *) PG_DETOAST_DATUM(txt_toasted); - - parsetext_v2(cfg, &prs, VARDATA(txt), VARSIZE(txt) - VARHDRSZ); - if (txt != (text *) DatumGetPointer(txt_toasted)) - pfree(txt); - } - - /* make tsvector value */ - if (prs.curwords) - { - datum = PointerGetDatum(makevalue(&prs)); - rettuple = SPI_modifytuple(rel, rettuple, 1, &numidxattr, - &datum, NULL); - pfree(DatumGetPointer(datum)); - } - else - { - tsvector *out = palloc(CALCDATASIZE(0, 0)); - - SET_VARSIZE(out, CALCDATASIZE(0, 0)); - out->size = 0; - datum = PointerGetDatum(out); - pfree(prs.words); - rettuple = SPI_modifytuple(rel, rettuple, 1, &numidxattr, - &datum, NULL); - } - - if (rettuple == NULL) - /* internal error */ - elog(ERROR, "TSearch: %d returned by SPI_modifytuple", SPI_result); - - return PointerGetDatum(rettuple); -} - -static int -silly_cmp_tsvector(const tsvector * a, const tsvector * b) -{ - if (VARSIZE(a) < VARSIZE(b)) - return -1; - else if (VARSIZE(a) > VARSIZE(b)) - return 1; - else if (a->size < b->size) - return -1; - else if (a->size > b->size) - return 1; - else - { - WordEntry *aptr = ARRPTR(a); - WordEntry *bptr = ARRPTR(b); - int i = 0; - int res; - - - for (i = 0; i < a->size; i++) - { - if (aptr->haspos != bptr->haspos) - { - return (aptr->haspos > bptr->haspos) ? -1 : 1; - } - else if (aptr->len != bptr->len) - { - return (aptr->len > bptr->len) ? -1 : 1; - } - else if ((res = strncmp(STRPTR(a) + aptr->pos, STRPTR(b) + bptr->pos, bptr->len)) != 0) - { - return res; - } - else if (aptr->haspos) - { - WordEntryPos *ap = POSDATAPTR(a, aptr); - WordEntryPos *bp = POSDATAPTR(b, bptr); - int j; - - if (POSDATALEN(a, aptr) != POSDATALEN(b, bptr)) - return (POSDATALEN(a, aptr) > POSDATALEN(b, bptr)) ? -1 : 1; - - for (j = 0; j < POSDATALEN(a, aptr); j++) - { - if (WEP_GETPOS(*ap) != WEP_GETPOS(*bp)) - { - return (WEP_GETPOS(*ap) > WEP_GETPOS(*bp)) ? -1 : 1; - } - else if (WEP_GETWEIGHT(*ap) != WEP_GETWEIGHT(*bp)) - { - return (WEP_GETWEIGHT(*ap) > WEP_GETWEIGHT(*bp)) ? -1 : 1; - } - ap++, bp++; - } - } - - aptr++; - bptr++; - } - } - - return 0; -} - -PG_FUNCTION_INFO_V1(tsvector_cmp); -PG_FUNCTION_INFO_V1(tsvector_lt); -PG_FUNCTION_INFO_V1(tsvector_le); -PG_FUNCTION_INFO_V1(tsvector_eq); -PG_FUNCTION_INFO_V1(tsvector_ne); -PG_FUNCTION_INFO_V1(tsvector_ge); -PG_FUNCTION_INFO_V1(tsvector_gt); -Datum tsvector_cmp(PG_FUNCTION_ARGS); -Datum tsvector_lt(PG_FUNCTION_ARGS); -Datum tsvector_le(PG_FUNCTION_ARGS); -Datum tsvector_eq(PG_FUNCTION_ARGS); -Datum tsvector_ne(PG_FUNCTION_ARGS); -Datum tsvector_ge(PG_FUNCTION_ARGS); -Datum tsvector_gt(PG_FUNCTION_ARGS); - -#define RUNCMP \ -tsvector *a = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));\ -tsvector *b = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));\ -int res = silly_cmp_tsvector(a,b); \ -PG_FREE_IF_COPY(a,0); \ -PG_FREE_IF_COPY(b,1); \ - -Datum -tsvector_cmp(PG_FUNCTION_ARGS) -{ - RUNCMP - PG_RETURN_INT32(res); -} - -Datum -tsvector_lt(PG_FUNCTION_ARGS) -{ - RUNCMP - PG_RETURN_BOOL((res < 0) ? true : false); -} - -Datum -tsvector_le(PG_FUNCTION_ARGS) -{ - RUNCMP - PG_RETURN_BOOL((res <= 0) ? true : false); -} - -Datum -tsvector_eq(PG_FUNCTION_ARGS) -{ - RUNCMP - PG_RETURN_BOOL((res == 0) ? true : false); -} - -Datum -tsvector_ge(PG_FUNCTION_ARGS) -{ - RUNCMP - PG_RETURN_BOOL((res >= 0) ? true : false); -} - -Datum -tsvector_gt(PG_FUNCTION_ARGS) -{ - RUNCMP - PG_RETURN_BOOL((res > 0) ? true : false); -} - -Datum -tsvector_ne(PG_FUNCTION_ARGS) -{ - RUNCMP - PG_RETURN_BOOL((res != 0) ? true : false); -} diff --git a/contrib/tsearch2/tsvector.h b/contrib/tsearch2/tsvector.h deleted file mode 100644 index e006a1dcbdfd..000000000000 --- a/contrib/tsearch2/tsvector.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef __TXTIDX_H__ -#define __TXTIDX_H__ - -/* -#define TXTIDX_DEBUG -*/ - -#include "postgres.h" - -#include "access/gist.h" -#include "access/itup.h" -#include "utils/builtins.h" -#include "storage/bufpage.h" - -typedef struct -{ - uint32 - haspos:1, - len:11, /* MAX 2Kb */ - pos:20; /* MAX 1Mb */ -} WordEntry; - -#define MAXSTRLEN ( 1<<11 ) -#define MAXSTRPOS ( 1<<20 ) - -/* -Equivalent to -typedef struct -{ - uint16 - weight:2, - pos:14; -} WordEntryPos; - -*/ - -typedef uint16 WordEntryPos; - -#define WEP_GETWEIGHT(x) ( (x) >> 14 ) -#define WEP_GETPOS(x) ( (x) & 0x3fff ) - -#define WEP_SETWEIGHT(x,v) (x) = ( (v) << 14 ) | ( (x) & 0x3fff ) -#define WEP_SETPOS(x,v) (x) = ( (x) & 0xc000 ) | ( (v) & 0x3fff ) - - -#define MAXENTRYPOS (1<<14) -#define MAXNUMPOS 256 -#define LIMITPOS(x) ( ( (x) >= MAXENTRYPOS ) ? (MAXENTRYPOS-1) : (x) ) - -/* - * Structure of tsvector datatype: - * 1) standard varlena header - * 2) int4 size - number of lexemes or WordEntry array, which is the same - * 3) Array of WordEntry - sorted array, comparison based on word's length - * and strncmp(). WordEntry->pos points number of - * bytes from end of WordEntry array to start of - * corresponding lexeme. - * 4) Lexeme's storage: - * SHORTALIGNED(lexeme) and position information if it exists - * Position information: first int2 - is a number of positions and it - * follows array of WordEntryPos - */ - -typedef struct -{ - int32 vl_len_; /* varlena header (do not touch directly!) */ - int4 size; - char data[1]; -} tsvector; - -#define DATAHDRSIZE (VARHDRSZ + sizeof(int4)) -#define CALCDATASIZE(x, lenstr) ( (x) * sizeof(WordEntry) + DATAHDRSIZE + (lenstr) ) -#define ARRPTR(x) ( (WordEntry*) ( (char*)(x) + DATAHDRSIZE ) ) -#define STRPTR(x) ( (char*)(x) + DATAHDRSIZE + ( sizeof(WordEntry) * ((tsvector*)(x))->size ) ) -#define STRSIZE(x) ( ((tsvector*)(x))->len - DATAHDRSIZE - ( sizeof(WordEntry) * ((tsvector*)(x))->size ) ) -#define _POSDATAPTR(x,e) (STRPTR(x)+((WordEntry*)(e))->pos+SHORTALIGN(((WordEntry*)(e))->len)) -#define POSDATALEN(x,e) ( ( ((WordEntry*)(e))->haspos ) ? (*(uint16*)_POSDATAPTR(x,e)) : 0 ) -#define POSDATAPTR(x,e) ( (WordEntryPos*)( _POSDATAPTR(x,e)+sizeof(uint16) ) ) - - -typedef struct -{ - WordEntry entry; - WordEntryPos *pos; -} WordEntryIN; - -typedef struct -{ - char *prsbuf; - char *word; - char *curpos; - int4 len; - int4 state; - int4 alen; - WordEntryPos *pos; - bool oprisdelim; -} TI_IN_STATE; - -int4 gettoken_tsvector(TI_IN_STATE * state); - -#endif diff --git a/contrib/tsearch2/tsvector_op.c b/contrib/tsearch2/tsvector_op.c deleted file mode 100644 index e8d8e16db416..000000000000 --- a/contrib/tsearch2/tsvector_op.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Operations for tsvector type - * Teodor Sigaev - */ -#include "postgres.h" - - -#include "access/gist.h" -#include "access/itup.h" -#include "catalog/namespace.h" -#include "commands/trigger.h" -#include "executor/spi.h" -#include "nodes/pg_list.h" -#include "storage/bufpage.h" -#include "utils/builtins.h" -#include "utils/pg_locale.h" - -#include "tsvector.h" -#include "query.h" -#include "ts_cfg.h" -#include "common.h" - -PG_FUNCTION_INFO_V1(strip); -Datum strip(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(setweight); -Datum setweight(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(concat); -Datum concat(PG_FUNCTION_ARGS); - -Datum -strip(PG_FUNCTION_ARGS) -{ - tsvector *in = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - tsvector *out; - int i, - len = 0; - WordEntry *arrin = ARRPTR(in), - *arrout; - char *cur; - - for (i = 0; i < in->size; i++) - len += SHORTALIGN(arrin[i].len); - - len = CALCDATASIZE(in->size, len); - out = (tsvector *) palloc0(len); - SET_VARSIZE(out, len); - out->size = in->size; - arrout = ARRPTR(out); - cur = STRPTR(out); - for (i = 0; i < in->size; i++) - { - memcpy(cur, STRPTR(in) + arrin[i].pos, arrin[i].len); - arrout[i].haspos = 0; - arrout[i].len = arrin[i].len; - arrout[i].pos = cur - STRPTR(out); - cur += SHORTALIGN(arrout[i].len); - } - - PG_FREE_IF_COPY(in, 0); - PG_RETURN_POINTER(out); -} - -Datum -setweight(PG_FUNCTION_ARGS) -{ - tsvector *in = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - char cw = PG_GETARG_CHAR(1); - tsvector *out; - int i, - j; - WordEntry *entry; - WordEntryPos *p; - int w = 0; - - switch (cw) - { - case 'A': - case 'a': - w = 3; - break; - case 'B': - case 'b': - w = 2; - break; - case 'C': - case 'c': - w = 1; - break; - case 'D': - case 'd': - w = 0; - break; - /* internal error */ - default: - elog(ERROR, "unrecognized weight"); - } - - out = (tsvector *) palloc(VARSIZE(in)); - memcpy(out, in, VARSIZE(in)); - entry = ARRPTR(out); - i = out->size; - while (i--) - { - if ((j = POSDATALEN(out, entry)) != 0) - { - p = POSDATAPTR(out, entry); - while (j--) - { - WEP_SETWEIGHT(*p, w); - p++; - } - } - entry++; - } - - PG_FREE_IF_COPY(in, 0); - PG_RETURN_POINTER(out); -} - -static int -compareEntry(char *ptra, WordEntry * a, char *ptrb, WordEntry * b) -{ - if (a->len == b->len) - { - return strncmp( - ptra + a->pos, - ptrb + b->pos, - a->len); - } - return (a->len > b->len) ? 1 : -1; -} - -static int4 -add_pos(tsvector * src, WordEntry * srcptr, tsvector * dest, WordEntry * destptr, int4 maxpos) -{ - uint16 *clen = (uint16 *) _POSDATAPTR(dest, destptr); - int i; - uint16 slen = POSDATALEN(src, srcptr), - startlen; - WordEntryPos *spos = POSDATAPTR(src, srcptr), - *dpos = POSDATAPTR(dest, destptr); - - if (!destptr->haspos) - *clen = 0; - - startlen = *clen; - for (i = 0; i < slen && *clen < MAXNUMPOS && (*clen == 0 || WEP_GETPOS(dpos[*clen - 1]) != MAXENTRYPOS - 1); i++) - { - WEP_SETWEIGHT(dpos[*clen], WEP_GETWEIGHT(spos[i])); - WEP_SETPOS(dpos[*clen], LIMITPOS(WEP_GETPOS(spos[i]) + maxpos)); - (*clen)++; - } - - if (*clen != startlen) - destptr->haspos = 1; - return *clen - startlen; -} - - -Datum -concat(PG_FUNCTION_ARGS) -{ - tsvector *in1 = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - tsvector *in2 = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); - tsvector *out; - WordEntry *ptr; - WordEntry *ptr1, - *ptr2; - WordEntryPos *p; - int maxpos = 0, - i, - j, - i1, - i2; - char *cur; - char *data, - *data1, - *data2; - - ptr = ARRPTR(in1); - i = in1->size; - while (i--) - { - if ((j = POSDATALEN(in1, ptr)) != 0) - { - p = POSDATAPTR(in1, ptr); - while (j--) - { - if (WEP_GETPOS(*p) > maxpos) - maxpos = WEP_GETPOS(*p); - p++; - } - } - ptr++; - } - - ptr1 = ARRPTR(in1); - ptr2 = ARRPTR(in2); - data1 = STRPTR(in1); - data2 = STRPTR(in2); - i1 = in1->size; - i2 = in2->size; - out = (tsvector *) palloc0(VARSIZE(in1) + VARSIZE(in2)); - SET_VARSIZE(out, VARSIZE(in1) + VARSIZE(in2)); - out->size = in1->size + in2->size; - data = cur = STRPTR(out); - ptr = ARRPTR(out); - while (i1 && i2) - { - int cmp = compareEntry(data1, ptr1, data2, ptr2); - - if (cmp < 0) - { /* in1 first */ - ptr->haspos = ptr1->haspos; - ptr->len = ptr1->len; - memcpy(cur, data1 + ptr1->pos, ptr1->len); - ptr->pos = cur - data; - cur += SHORTALIGN(ptr1->len); - if (ptr->haspos) - { - memcpy(cur, _POSDATAPTR(in1, ptr1), POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16)); - cur += POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16); - } - ptr++; - ptr1++; - i1--; - } - else if (cmp > 0) - { /* in2 first */ - ptr->haspos = ptr2->haspos; - ptr->len = ptr2->len; - memcpy(cur, data2 + ptr2->pos, ptr2->len); - ptr->pos = cur - data; - cur += SHORTALIGN(ptr2->len); - if (ptr->haspos) - { - int addlen = add_pos(in2, ptr2, out, ptr, maxpos); - - if (addlen == 0) - ptr->haspos = 0; - else - cur += addlen * sizeof(WordEntryPos) + sizeof(uint16); - } - ptr++; - ptr2++; - i2--; - } - else - { - ptr->haspos = ptr1->haspos | ptr2->haspos; - ptr->len = ptr1->len; - memcpy(cur, data1 + ptr1->pos, ptr1->len); - ptr->pos = cur - data; - cur += SHORTALIGN(ptr1->len); - if (ptr->haspos) - { - if (ptr1->haspos) - { - memcpy(cur, _POSDATAPTR(in1, ptr1), POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16)); - cur += POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16); - if (ptr2->haspos) - cur += add_pos(in2, ptr2, out, ptr, maxpos) * sizeof(WordEntryPos); - } - else if (ptr2->haspos) - { - int addlen = add_pos(in2, ptr2, out, ptr, maxpos); - - if (addlen == 0) - ptr->haspos = 0; - else - cur += addlen * sizeof(WordEntryPos) + sizeof(uint16); - } - } - ptr++; - ptr1++; - ptr2++; - i1--; - i2--; - } - } - - while (i1) - { - ptr->haspos = ptr1->haspos; - ptr->len = ptr1->len; - memcpy(cur, data1 + ptr1->pos, ptr1->len); - ptr->pos = cur - data; - cur += SHORTALIGN(ptr1->len); - if (ptr->haspos) - { - memcpy(cur, _POSDATAPTR(in1, ptr1), POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16)); - cur += POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16); - } - ptr++; - ptr1++; - i1--; - } - - while (i2) - { - ptr->haspos = ptr2->haspos; - ptr->len = ptr2->len; - memcpy(cur, data2 + ptr2->pos, ptr2->len); - ptr->pos = cur - data; - cur += SHORTALIGN(ptr2->len); - if (ptr->haspos) - { - int addlen = add_pos(in2, ptr2, out, ptr, maxpos); - - if (addlen == 0) - ptr->haspos = 0; - else - cur += addlen * sizeof(WordEntryPos) + sizeof(uint16); - } - ptr++; - ptr2++; - i2--; - } - - out->size = ptr - ARRPTR(out); - SET_VARSIZE(out, CALCDATASIZE(out->size, cur - data)); - if (data != STRPTR(out)) - memmove(STRPTR(out), data, cur - data); - - PG_FREE_IF_COPY(in1, 0); - PG_FREE_IF_COPY(in2, 1); - PG_RETURN_POINTER(out); -} diff --git a/contrib/tsearch2/uninstall_tsearch2.sql b/contrib/tsearch2/uninstall_tsearch2.sql new file mode 100644 index 000000000000..180b021d2c31 --- /dev/null +++ b/contrib/tsearch2/uninstall_tsearch2.sql @@ -0,0 +1,96 @@ +/* $PostgreSQL: pgsql/contrib/tsearch2/uninstall_tsearch2.sql,v 1.2 2007/11/16 00:34:54 tgl Exp $ */ + +-- Adjust this setting to control where the objects get dropped. +SET search_path = public, pg_catalog; + +DROP DOMAIN tsvector CASCADE; +DROP DOMAIN tsquery CASCADE; +DROP DOMAIN gtsvector CASCADE; +DROP DOMAIN gtsq CASCADE; + +DROP TYPE tokentype CASCADE; +DROP TYPE tokenout CASCADE; +DROP TYPE statinfo CASCADE; +DROP TYPE tsdebug CASCADE; + +DROP OPERATOR CLASS tsquery_ops USING btree CASCADE; + +DROP OPERATOR CLASS tsvector_ops USING btree CASCADE; + +DROP OPERATOR CLASS gin_tsvector_ops USING gin CASCADE; + +DROP OPERATOR CLASS gist_tp_tsquery_ops USING gist CASCADE; + +DROP OPERATOR CLASS gist_tsvector_ops USING gist CASCADE; + +DROP FUNCTION lexize(oid, text) ; +DROP FUNCTION lexize(text, text); +DROP FUNCTION lexize(text); +DROP FUNCTION set_curdict(int); +DROP FUNCTION set_curdict(text); +DROP FUNCTION dex_init(internal); +DROP FUNCTION dex_lexize(internal,internal,int4); +DROP FUNCTION snb_en_init(internal); +DROP FUNCTION snb_lexize(internal,internal,int4); +DROP FUNCTION snb_ru_init_koi8(internal); +DROP FUNCTION snb_ru_init_utf8(internal); +DROP FUNCTION snb_ru_init(internal); +DROP FUNCTION spell_init(internal); +DROP FUNCTION spell_lexize(internal,internal,int4); +DROP FUNCTION syn_init(internal); +DROP FUNCTION syn_lexize(internal,internal,int4); +DROP FUNCTION thesaurus_init(internal); +DROP FUNCTION thesaurus_lexize(internal,internal,int4,internal); +DROP FUNCTION set_curprs(int); +DROP FUNCTION set_curprs(text); +DROP FUNCTION prsd_start(internal,int4); +DROP FUNCTION prsd_getlexeme(internal,internal,internal); +DROP FUNCTION prsd_end(internal); +DROP FUNCTION prsd_lextype(internal); +DROP FUNCTION prsd_headline(internal,internal,internal); +DROP FUNCTION set_curcfg(int); +DROP FUNCTION set_curcfg(text); +DROP FUNCTION show_curcfg(); +DROP FUNCTION length(tsvector); +DROP FUNCTION to_tsvector(oid, text); +DROP FUNCTION to_tsvector(text, text); +DROP FUNCTION to_tsvector(text); +DROP FUNCTION strip(tsvector); +DROP FUNCTION setweight(tsvector,"char"); +DROP FUNCTION concat(tsvector,tsvector); +DROP FUNCTION querytree(tsquery); +DROP FUNCTION to_tsquery(oid, text); +DROP FUNCTION to_tsquery(text, text); +DROP FUNCTION to_tsquery(text); +DROP FUNCTION plainto_tsquery(oid, text); +DROP FUNCTION plainto_tsquery(text, text); +DROP FUNCTION plainto_tsquery(text); +DROP FUNCTION tsearch2() CASCADE; +DROP FUNCTION rank(float4[], tsvector, tsquery); +DROP FUNCTION rank(float4[], tsvector, tsquery, int4); +DROP FUNCTION rank(tsvector, tsquery); +DROP FUNCTION rank(tsvector, tsquery, int4); +DROP FUNCTION rank_cd(float4[], tsvector, tsquery); +DROP FUNCTION rank_cd(float4[], tsvector, tsquery, int4); +DROP FUNCTION rank_cd(tsvector, tsquery); +DROP FUNCTION rank_cd(tsvector, tsquery, int4); +DROP FUNCTION headline(oid, text, tsquery, text); +DROP FUNCTION headline(oid, text, tsquery); +DROP FUNCTION headline(text, text, tsquery, text); +DROP FUNCTION headline(text, text, tsquery); +DROP FUNCTION headline(text, tsquery, text); +DROP FUNCTION headline(text, tsquery); +DROP FUNCTION get_covers(tsvector,tsquery); +DROP FUNCTION _get_parser_from_curcfg(); +DROP FUNCTION numnode(tsquery); +DROP FUNCTION tsquery_and(tsquery,tsquery); +DROP FUNCTION tsquery_or(tsquery,tsquery); +DROP FUNCTION tsquery_not(tsquery); +DROP FUNCTION rewrite(tsquery, text); +DROP FUNCTION rewrite(tsquery, tsquery, tsquery); +DROP AGGREGATE rewrite (tsquery[]); +DROP FUNCTION rewrite_accum(tsquery,tsquery[]); +DROP FUNCTION rewrite_finish(tsquery); +DROP FUNCTION tsq_mcontains(tsquery, tsquery); +DROP FUNCTION tsq_mcontained(tsquery, tsquery); +DROP FUNCTION reset_tsearch(); diff --git a/contrib/tsearch2/untsearch.sql.in b/contrib/tsearch2/untsearch.sql.in deleted file mode 100644 index 26683c478e83..000000000000 --- a/contrib/tsearch2/untsearch.sql.in +++ /dev/null @@ -1,75 +0,0 @@ -BEGIN; - ---Be careful !!! ---script drops all indices, triggers and columns with types defined ---in tsearch2.sql - - -DROP OPERATOR CLASS gin_tsvector_ops USING gin CASCADE; - -DROP OPERATOR CLASS gist_tsvector_ops USING gist CASCADE; - - -DROP OPERATOR || (tsvector, tsvector); -DROP OPERATOR @@ (tsvector, tsquery); -DROP OPERATOR @@ (tsquery, tsvector); - ---DROP AGGREGATE stat(tsvector); - -DROP TABLE pg_ts_dict; -DROP TABLE pg_ts_parser; -DROP TABLE pg_ts_cfg; -DROP TABLE pg_ts_cfgmap; - -DROP TYPE tokentype CASCADE; -DROP TYPE tokenout CASCADE; -DROP TYPE tsvector CASCADE; -DROP TYPE tsquery CASCADE; -DROP TYPE gtsvector CASCADE; ---DROP TYPE tsstat CASCADE; -DROP TYPE statinfo CASCADE; -DROP TYPE tsdebug CASCADE; -DROP TYPE gtsq CASCADE; - -DROP FUNCTION lexize(oid, text) ; -DROP FUNCTION lexize(text, text); -DROP FUNCTION lexize(text); -DROP FUNCTION set_curdict(int); -DROP FUNCTION set_curdict(text); -DROP FUNCTION dex_init(internal); -DROP FUNCTION dex_lexize(internal,internal,int4); -DROP FUNCTION snb_en_init(internal); -DROP FUNCTION snb_lexize(internal,internal,int4); -DROP FUNCTION snb_ru_init_koi8(internal); -DROP FUNCTION snb_ru_init_utf8(internal); -DROP FUNCTION spell_init(internal); -DROP FUNCTION spell_lexize(internal,internal,int4); -DROP FUNCTION thesaurus_init(internal); -DROP FUNCTION thesaurus_lexize(internal,internal,int4,internal); -DROP FUNCTION syn_init(internal); -DROP FUNCTION syn_lexize(internal,internal,int4); -DROP FUNCTION set_curprs(int); -DROP FUNCTION set_curprs(text); -DROP FUNCTION prsd_start(internal,int4); -DROP FUNCTION prsd_getlexeme(internal,internal,internal); -DROP FUNCTION prsd_end(internal); -DROP FUNCTION prsd_lextype(internal); -DROP FUNCTION prsd_headline(internal,internal,internal); -DROP FUNCTION set_curcfg(int); -DROP FUNCTION set_curcfg(text); -DROP FUNCTION show_curcfg(); -DROP FUNCTION gtsvector_compress(internal); -DROP FUNCTION gtsvector_decompress(internal); -DROP FUNCTION gtsvector_penalty(internal,internal,internal); -DROP FUNCTION gtsvector_picksplit(internal, internal); -DROP FUNCTION gtsvector_union(internal, internal); -DROP FUNCTION gtsq_compress(internal); -DROP FUNCTION gtsq_decompress(internal); -DROP FUNCTION gtsq_penalty(internal,internal,internal); -DROP FUNCTION gtsq_picksplit(internal, internal); -DROP FUNCTION gtsq_union(bytea, internal); -DROP FUNCTION reset_tsearch(); -DROP FUNCTION tsearch2() CASCADE; -DROP FUNCTION _get_parser_from_curcfg(); - -END; diff --git a/contrib/tsearch2/wordparser/Makefile b/contrib/tsearch2/wordparser/Makefile deleted file mode 100644 index c4eceba60bb2..000000000000 --- a/contrib/tsearch2/wordparser/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# $PostgreSQL: pgsql/contrib/tsearch2/wordparser/Makefile,v 1.9 2005/11/21 12:27:57 teodor Exp $ - -SUBOBJS = parser.o deflex.o - -EXTRA_CLEAN = SUBSYS.o $(SUBOBJS) - -PG_CPPFLAGS = -I$(srcdir)/.. - -ifdef USE_PGXS -PGXS := $(shell pg_config --pgxs) -include $(PGXS) -else -subdir = contrib/tsearch2/wordparser -top_builddir = ../../.. -include $(top_builddir)/src/Makefile.global -include $(top_srcdir)/contrib/contrib-global.mk -endif - -override CFLAGS += $(CFLAGS_SL) - -all: SUBSYS.o - -SUBSYS.o: $(SUBOBJS) - $(LD) $(LDREL) $(LDOUT) $@ $^ - - diff --git a/contrib/tsearch2/wordparser/deflex.c b/contrib/tsearch2/wordparser/deflex.c deleted file mode 100644 index 004069f48e87..000000000000 --- a/contrib/tsearch2/wordparser/deflex.c +++ /dev/null @@ -1,57 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/wordparser/deflex.c,v 1.4 2006/03/11 04:38:30 momjian Exp $ */ - -#include "deflex.h" - -const char *lex_descr[] = { - "", - "Latin word", - "Non-latin word", - "Word", - "Email", - "URL", - "Host", - "Scientific notation", - "VERSION", - "Part of hyphenated word", - "Non-latin part of hyphenated word", - "Latin part of hyphenated word", - "Space symbols", - "HTML Tag", - "Protocol head", - "Hyphenated word", - "Latin hyphenated word", - "Non-latin hyphenated word", - "URI", - "File or path name", - "Decimal notation", - "Signed integer", - "Unsigned integer", - "HTML Entity" -}; - -const char *tok_alias[] = { - "", - "lword", - "nlword", - "word", - "email", - "url", - "host", - "sfloat", - "version", - "part_hword", - "nlpart_hword", - "lpart_hword", - "blank", - "tag", - "protocol", - "hword", - "lhword", - "nlhword", - "uri", - "file", - "float", - "int", - "uint", - "entity" -}; diff --git a/contrib/tsearch2/wordparser/deflex.h b/contrib/tsearch2/wordparser/deflex.h deleted file mode 100644 index 72852e448566..000000000000 --- a/contrib/tsearch2/wordparser/deflex.h +++ /dev/null @@ -1,36 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/wordparser/deflex.h,v 1.3 2006/03/11 04:38:30 momjian Exp $ */ - -#ifndef __DEFLEX_H__ -#define __DEFLEX_H__ - -/* rememder !!!! */ -#define LASTNUM 23 - -#define LATWORD 1 -#define CYRWORD 2 -#define UWORD 3 -#define EMAIL 4 -#define FURL 5 -#define HOST 6 -#define SCIENTIFIC 7 -#define VERSIONNUMBER 8 -#define PARTHYPHENWORD 9 -#define CYRPARTHYPHENWORD 10 -#define LATPARTHYPHENWORD 11 -#define SPACE 12 -#define TAG 13 -#define PROTOCOL 14 -#define HYPHENWORD 15 -#define LATHYPHENWORD 16 -#define CYRHYPHENWORD 17 -#define URI 18 -#define FILEPATH 19 -#define DECIMAL 20 -#define SIGNEDINT 21 -#define UNSIGNEDINT 22 -#define HTMLENTITY 23 - -extern const char *lex_descr[]; -extern const char *tok_alias[]; - -#endif diff --git a/contrib/tsearch2/wordparser/parser.c b/contrib/tsearch2/wordparser/parser.c deleted file mode 100644 index 6352c112663c..000000000000 --- a/contrib/tsearch2/wordparser/parser.c +++ /dev/null @@ -1,1266 +0,0 @@ -/* $PostgreSQL: pgsql/contrib/tsearch2/wordparser/parser.c,v 1.11.2.2 2007/03/22 15:59:09 teodor Exp $ */ - -#include "postgres.h" - -#include "utils/builtins.h" -#include "utils/pg_locale.h" -#include "mb/pg_wchar.h" - -#include "deflex.h" -#include "parser.h" -#include "ts_locale.h" - - -static TParserPosition * -newTParserPosition(TParserPosition * prev) -{ - TParserPosition *res = (TParserPosition *) palloc(sizeof(TParserPosition)); - - if (prev) - memcpy(res, prev, sizeof(TParserPosition)); - else - memset(res, 0, sizeof(TParserPosition)); - - res->prev = prev; - - res->pushedAtAction = NULL; - - return res; -} - -TParser * -TParserInit(char *str, int len) -{ - TParser *prs = (TParser *) palloc0(sizeof(TParser)); - - prs->charmaxlen = pg_database_encoding_max_length(); - prs->str = str; - prs->lenstr = len; - -#ifdef TS_USE_WIDE - - /* - * Use wide char code only when max encoding length > 1. - */ - - if (prs->charmaxlen > 1) - { - prs->usewide = true; - prs->wstr = (wchar_t *) palloc(sizeof(wchar_t) * (prs->lenstr+1)); - prs->lenwstr = char2wchar(prs->wstr, prs->lenstr, prs->str, prs->lenstr); - } - else -#endif - prs->usewide = false; - - prs->state = newTParserPosition(NULL); - prs->state->state = TPS_Base; - - return prs; -} - -void -TParserClose(TParser * prs) -{ - while (prs->state) - { - TParserPosition *ptr = prs->state->prev; - - pfree(prs->state); - prs->state = ptr; - } - -#ifdef TS_USE_WIDE - if (prs->wstr) - pfree(prs->wstr); -#endif - - pfree(prs); -} - -/* - * defining support function, equvalent is* macroses, but - * working with any possible encodings and locales. Note, - * that with multibyte encoding and C-locale isw* function may fail - * or give wrong result. Note 2: multibyte encoding and C-locale - * often are used for Asian languages. - */ - -#ifdef TS_USE_WIDE - -#define p_iswhat(type) \ -static int \ -p_is##type(TParser *prs) { \ - Assert( prs->state ); \ - if ( prs->usewide ) \ - { \ - if ( lc_ctype_is_c() ) \ - return is##type( 0xff & *( prs->wstr + prs->state->poschar) ); \ - \ - return isw##type( *(wint_t*)( prs->wstr + prs->state->poschar ) ); \ - } \ - \ - return is##type( *(unsigned char*)( prs->str + prs->state->posbyte ) ); \ -} \ - \ -static int \ -p_isnot##type(TParser *prs) { \ - return !p_is##type(prs); \ -} - -static int -p_isalnum(TParser *prs) -{ - Assert( prs->state ); - - if (prs->usewide) - { - if (lc_ctype_is_c()) - { - unsigned int c = *(prs->wstr + prs->state->poschar); - - /* - * any non-ascii symbol with multibyte encoding - * with C-locale is an alpha character - */ - if ( c > 0x7f ) - return 1; - - return isalnum(0xff & c); - } - - return iswalnum( (wint_t)*( prs->wstr + prs->state->poschar)); - } - - return isalnum( *(unsigned char*)( prs->str + prs->state->posbyte )); -} - -static int -p_isnotalnum(TParser *prs) -{ - return !p_isalnum(prs); -} - -static int -p_isalpha(TParser *prs) -{ - Assert( prs->state ); - - if (prs->usewide) - { - if (lc_ctype_is_c()) - { - unsigned int c = *(prs->wstr + prs->state->poschar); - - /* - * any non-ascii symbol with multibyte encoding - * with C-locale is an alpha character - */ - if ( c > 0x7f ) - return 1; - - return isalpha(0xff & c); - } - - return iswalpha( (wint_t)*( prs->wstr + prs->state->poschar)); - } - - return isalpha( *(unsigned char*)( prs->str + prs->state->posbyte )); -} - -static int -p_isnotalpha(TParser *prs) -{ - return !p_isalpha(prs); -} - -/* p_iseq should be used only for ascii symbols */ - -static int -p_iseq(TParser * prs, char c) -{ - Assert(prs->state); - return ((prs->state->charlen == 1 && *(prs->str + prs->state->posbyte) == c)) ? 1 : 0; -} - -#else /* TS_USE_WIDE */ - -#define p_iswhat(type) \ -static int \ -p_is##type(TParser *prs) { \ - Assert( prs->state ); \ - return is##type( (unsigned char)*( prs->str + prs->state->posbyte ) ); \ -} \ - \ -static int \ -p_isnot##type(TParser *prs) { \ - return !p_is##type(prs); \ -} - - -static int -p_iseq(TParser * prs, char c) -{ - Assert(prs->state); - return (*(prs->str + prs->state->posbyte) == c) ? 1 : 0; -} - -p_iswhat(alnum) -p_iswhat(alpha) - -#endif /* TS_USE_WIDE */ - -p_iswhat(digit) -p_iswhat(lower) -p_iswhat(print) -p_iswhat(punct) -p_iswhat(space) -p_iswhat(upper) -p_iswhat(xdigit) - -static int -p_isEOF(TParser * prs) -{ - Assert(prs->state); - return (prs->state->posbyte == prs->lenstr || prs->state->charlen == 0) ? 1 : 0; -} - -static int -p_iseqC(TParser * prs) -{ - return p_iseq(prs, prs->c); -} - -static int -p_isneC(TParser * prs) -{ - return !p_iseq(prs, prs->c); -} - -static int -p_isascii(TParser * prs) -{ - return (prs->state->charlen == 1 && isascii((unsigned char) *(prs->str + prs->state->posbyte))) ? 1 : 0; -} - -static int -p_islatin(TParser * prs) -{ - return (p_isalpha(prs) && p_isascii(prs)) ? 1 : 0; -} - -static int -p_isnonlatin(TParser * prs) -{ - return (p_isalpha(prs) && !p_isascii(prs)) ? 1 : 0; -} - -void _make_compiler_happy(void); -void -_make_compiler_happy(void) -{ - p_isalnum(NULL); - p_isnotalnum(NULL); - p_isalpha(NULL); - p_isnotalpha(NULL); - p_isdigit(NULL); - p_isnotdigit(NULL); - p_islower(NULL); - p_isnotlower(NULL); - p_isprint(NULL); - p_isnotprint(NULL); - p_ispunct(NULL); - p_isnotpunct(NULL); - p_isspace(NULL); - p_isnotspace(NULL); - p_isupper(NULL); - p_isnotupper(NULL); - p_isxdigit(NULL); - p_isnotxdigit(NULL); - p_isEOF(NULL); - p_iseqC(NULL); - p_isneC(NULL); -} - - -static void -SpecialTags(TParser * prs) -{ - switch (prs->state->lencharlexeme) - { - case 8: /* lexeme, "ignore = false; - break; - case 7: /* token, "ignore = false; + break; + case 7: /* + +', +to_tsquery('english', 'sea&foo'), 'HighlightAll=true'); + ts_headline +----------------------------------------------------------------------------- + + + + + Sea view wow foo bar qq + YES   + ff-bg + + + +(1 row) + +--Rewrite sub system +CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT); +\set ECHO none +ALTER TABLE test_tsquery ADD COLUMN keyword tsquery; +UPDATE test_tsquery SET keyword = to_tsquery('english', txtkeyword); +ALTER TABLE test_tsquery ADD COLUMN sample tsquery; +UPDATE test_tsquery SET sample = to_tsquery('english', txtsample::text); +SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; + count +------- + 1 +(1 row) + +SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york'; + count +------- + 2 +(1 row) + +SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york'; + count +------- + 1 +(1 row) + +SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york'; + count +------- + 3 +(1 row) + +SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; + count +------- + 2 +(1 row) + +CREATE INDEX bt_tsq ON test_tsquery (keyword); +SET enable_seqscan=OFF; +SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; + count +------- + 1 +(1 row) + +SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york'; + count +------- + 2 +(1 row) + +SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york'; + count +------- + 1 +(1 row) + +SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york'; + count +------- + 3 +(1 row) + +SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; + count +------- + 2 +(1 row) + +RESET enable_seqscan; +SELECT ts_rewrite('foo & bar & qq & new & york', 'new & york'::tsquery, 'big & apple | nyc | new & york & city'); + ts_rewrite +---------------------------------------------------------------------------------- + 'foo' & 'bar' & 'qq' & ( 'city' & 'new' & 'york' | ( 'nyc' | 'big' & 'apple' ) ) +(1 row) + +SELECT ts_rewrite('moscow', 'SELECT keyword, sample FROM test_tsquery'::text ); + ts_rewrite +--------------------- + 'moskva' | 'moscow' +(1 row) + +SELECT ts_rewrite('moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'::text ); + ts_rewrite +----------------------------------- + 'hotel' & ( 'moskva' | 'moscow' ) +(1 row) + +SELECT ts_rewrite('bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'::text ); + ts_rewrite +------------------------------------------------------------------------------------- + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) +(1 row) + +SELECT ts_rewrite( 'moscow', 'SELECT keyword, sample FROM test_tsquery'); + ts_rewrite +--------------------- + 'moskva' | 'moscow' +(1 row) + +SELECT ts_rewrite( 'moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'); + ts_rewrite +----------------------------------- + 'hotel' & ( 'moskva' | 'moscow' ) +(1 row) + +SELECT ts_rewrite( 'bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'); + ts_rewrite +------------------------------------------------------------------------------------- + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) +(1 row) + +SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; + keyword +---------------- + 'new' & 'york' +(1 row) + +SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow'; + keyword +---------- + 'moscow' +(1 row) + +SELECT keyword FROM test_tsquery WHERE keyword <@ 'new'; + keyword +--------- +(0 rows) + +SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow'; + keyword +---------- + 'moscow' +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; + ts_rewrite +--------------------- + 'moskva' | 'moscow' +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; + ts_rewrite +----------------------------------- + 'hotel' & ( 'moskva' | 'moscow' ) +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; + ts_rewrite +------------------------------------------------------------------------------------- + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; + ts_rewrite +--------------------- + 'moskva' | 'moscow' +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; + ts_rewrite +----------------------------------- + 'hotel' & ( 'moskva' | 'moscow' ) +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; + ts_rewrite +------------------------------------------------------------------------------------- + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) +(1 row) + +CREATE INDEX qq ON test_tsquery USING gist (keyword tsquery_ops); +SET enable_seqscan=OFF; +SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; + keyword +---------------- + 'new' & 'york' +(1 row) + +SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow'; + keyword +---------- + 'moscow' +(1 row) + +SELECT keyword FROM test_tsquery WHERE keyword <@ 'new'; + keyword +--------- +(0 rows) + +SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow'; + keyword +---------- + 'moscow' +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; + ts_rewrite +--------------------- + 'moskva' | 'moscow' +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; + ts_rewrite +----------------------------------- + 'hotel' & ( 'moskva' | 'moscow' ) +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; + ts_rewrite +------------------------------------------------------------------------------------- + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; + ts_rewrite +--------------------- + 'moskva' | 'moscow' +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; + ts_rewrite +----------------------------------- + 'hotel' & ( 'moskva' | 'moscow' ) +(1 row) + +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; + ts_rewrite +------------------------------------------------------------------------------------- + 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) ) +(1 row) + +RESET enable_seqscan; +--test GUC +SET default_text_search_config=simple; +SELECT to_tsvector('SKIES My booKs'); + to_tsvector +---------------------------- + 'my':2 'books':3 'skies':1 +(1 row) + +SELECT plainto_tsquery('SKIES My booKs'); + plainto_tsquery +-------------------------- + 'skies' & 'my' & 'books' +(1 row) + +SELECT to_tsquery('SKIES & My | booKs'); + to_tsquery +-------------------------- + 'skies' & 'my' | 'books' +(1 row) + +SET default_text_search_config=english; +SELECT to_tsvector('SKIES My booKs'); + to_tsvector +------------------ + 'sky':1 'book':3 +(1 row) + +SELECT plainto_tsquery('SKIES My booKs'); + plainto_tsquery +----------------- + 'sky' & 'book' +(1 row) + +SELECT to_tsquery('SKIES & My | booKs'); + to_tsquery +---------------- + 'sky' | 'book' +(1 row) + +--trigger +-- GPDB doesn't allow updating the distribution key, so create a synthetic +-- distribution key column. +alter table test_tsvector add column distkey int4; +alter table test_tsvector set distributed by (distkey); +CREATE TRIGGER tsvectorupdate +BEFORE UPDATE OR INSERT ON test_tsvector +FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(a, 'pg_catalog.english', t); +SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); + count +------- + 0 +(1 row) + +INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); +SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); + count +------- + 1 +(1 row) + +UPDATE test_tsvector SET t = null WHERE t = '345 qwerty'; +SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); + count +------- + 0 +(1 row) + +INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); +SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); + count +------- + 1 +(1 row) + diff --git a/src/test/regress/expected/tstypes.out b/src/test/regress/expected/tstypes.out new file mode 100644 index 000000000000..4672f099e0ac --- /dev/null +++ b/src/test/regress/expected/tstypes.out @@ -0,0 +1,513 @@ +--Base tsvector test +SELECT '1'::tsvector; + tsvector +---------- + '1' +(1 row) + +SELECT '1 '::tsvector; + tsvector +---------- + '1' +(1 row) + +SELECT ' 1'::tsvector; + tsvector +---------- + '1' +(1 row) + +SELECT ' 1 '::tsvector; + tsvector +---------- + '1' +(1 row) + +SELECT '1 2'::tsvector; + tsvector +---------- + '1' '2' +(1 row) + +SELECT '''1 2'''::tsvector; + tsvector +---------- + '1 2' +(1 row) + +SELECT E'''1 \\''2'''::tsvector; + tsvector +---------- + '1 ''2' +(1 row) + +SELECT E'''1 \\''2''3'::tsvector; + tsvector +------------- + '3' '1 ''2' +(1 row) + +SELECT E'''1 \\''2'' 3'::tsvector; + tsvector +------------- + '3' '1 ''2' +(1 row) + +SELECT E'''1 \\''2'' '' 3'' 4 '::tsvector; + tsvector +------------------ + '4' ' 3' '1 ''2' +(1 row) + +SELECT $$'\\as' ab\c ab\\c AB\\\c ab\\\\c$$::tsvector; + tsvector +---------------------------------------- + '\\as' 'abc' 'AB\\c' 'ab\\c' 'ab\\\\c' +(1 row) + +SELECT tsvectorin(tsvectorout($$'\\as' ab\c ab\\c AB\\\c ab\\\\c$$::tsvector)); + tsvectorin +---------------------------------------- + '\\as' 'abc' 'AB\\c' 'ab\\c' 'ab\\\\c' +(1 row) + +SELECT '''w'':4A,3B,2C,1D,5 a:8'; + ?column? +----------------------- + 'w':4A,3B,2C,1D,5 a:8 +(1 row) + +SELECT 'a:3A b:2a'::tsvector || 'ba:1234 a:1B'; + ?column? +---------------------------- + 'a':3A,4B 'b':2A 'ba':1237 +(1 row) + +SELECT setweight('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd zxc:81,567,222A'::tsvector, 'c'); + setweight +---------------------------------------------------------- + 'a':1C,3C 'w':5C,6C,12C,13C 'asd':1C 'zxc':81C,222C,567C +(1 row) + +SELECT strip('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd'::tsvector); + strip +--------------- + 'a' 'w' 'asd' +(1 row) + +--Base tsquery test +SELECT '1'::tsquery; + tsquery +--------- + '1' +(1 row) + +SELECT '1 '::tsquery; + tsquery +--------- + '1' +(1 row) + +SELECT ' 1'::tsquery; + tsquery +--------- + '1' +(1 row) + +SELECT ' 1 '::tsquery; + tsquery +--------- + '1' +(1 row) + +SELECT '''1 2'''::tsquery; + tsquery +--------- + '1 2' +(1 row) + +SELECT E'''1 \\''2'''::tsquery; + tsquery +--------- + '1 ''2' +(1 row) + +SELECT '!1'::tsquery; + tsquery +--------- + !'1' +(1 row) + +SELECT '1|2'::tsquery; + tsquery +----------- + '1' | '2' +(1 row) + +SELECT '1|!2'::tsquery; + tsquery +------------ + '1' | !'2' +(1 row) + +SELECT '!1|2'::tsquery; + tsquery +------------ + !'1' | '2' +(1 row) + +SELECT '!1|!2'::tsquery; + tsquery +------------- + !'1' | !'2' +(1 row) + +SELECT '!(!1|!2)'::tsquery; + tsquery +------------------ + !( !'1' | !'2' ) +(1 row) + +SELECT '!(!1|2)'::tsquery; + tsquery +----------------- + !( !'1' | '2' ) +(1 row) + +SELECT '!(1|!2)'::tsquery; + tsquery +----------------- + !( '1' | !'2' ) +(1 row) + +SELECT '!(1|2)'::tsquery; + tsquery +---------------- + !( '1' | '2' ) +(1 row) + +SELECT '1&2'::tsquery; + tsquery +----------- + '1' & '2' +(1 row) + +SELECT '!1&2'::tsquery; + tsquery +------------ + !'1' & '2' +(1 row) + +SELECT '1&!2'::tsquery; + tsquery +------------ + '1' & !'2' +(1 row) + +SELECT '!1&!2'::tsquery; + tsquery +------------- + !'1' & !'2' +(1 row) + +SELECT '(1&2)'::tsquery; + tsquery +----------- + '1' & '2' +(1 row) + +SELECT '1&(2)'::tsquery; + tsquery +----------- + '1' & '2' +(1 row) + +SELECT '!(1)&2'::tsquery; + tsquery +------------ + !'1' & '2' +(1 row) + +SELECT '!(1&2)'::tsquery; + tsquery +---------------- + !( '1' & '2' ) +(1 row) + +SELECT '1|2&3'::tsquery; + tsquery +----------------- + '1' | '2' & '3' +(1 row) + +SELECT '1|(2&3)'::tsquery; + tsquery +----------------- + '1' | '2' & '3' +(1 row) + +SELECT '(1|2)&3'::tsquery; + tsquery +--------------------- + ( '1' | '2' ) & '3' +(1 row) + +SELECT '1|2&!3'::tsquery; + tsquery +------------------ + '1' | '2' & !'3' +(1 row) + +SELECT '1|!2&3'::tsquery; + tsquery +------------------ + '1' | !'2' & '3' +(1 row) + +SELECT '!1|2&3'::tsquery; + tsquery +------------------ + !'1' | '2' & '3' +(1 row) + +SELECT '!1|(2&3)'::tsquery; + tsquery +------------------ + !'1' | '2' & '3' +(1 row) + +SELECT '!(1|2)&3'::tsquery; + tsquery +---------------------- + !( '1' | '2' ) & '3' +(1 row) + +SELECT '(!1|2)&3'::tsquery; + tsquery +---------------------- + ( !'1' | '2' ) & '3' +(1 row) + +SELECT '1|(2|(4|(5|6)))'::tsquery; + tsquery +----------------------------------------- + '1' | ( '2' | ( '4' | ( '5' | '6' ) ) ) +(1 row) + +SELECT '1|2|4|5|6'::tsquery; + tsquery +----------------------------------------- + ( ( ( '1' | '2' ) | '4' ) | '5' ) | '6' +(1 row) + +SELECT '1&(2&(4&(5&6)))'::tsquery; + tsquery +----------------------------- + '1' & '2' & '4' & '5' & '6' +(1 row) + +SELECT '1&2&4&5&6'::tsquery; + tsquery +----------------------------- + '1' & '2' & '4' & '5' & '6' +(1 row) + +SELECT '1&(2&(4&(5|6)))'::tsquery; + tsquery +--------------------------------- + '1' & '2' & '4' & ( '5' | '6' ) +(1 row) + +SELECT '1&(2&(4&(5|!6)))'::tsquery; + tsquery +---------------------------------- + '1' & '2' & '4' & ( '5' | !'6' ) +(1 row) + +SELECT E'1&(''2''&('' 4''&(\\|5 | ''6 \\'' !|&'')))'::tsquery; + tsquery +------------------------------------------ + '1' & '2' & ' 4' & ( '|5' | '6 '' !|&' ) +(1 row) + +SELECT $$'\\as'$$::tsquery; + tsquery +--------- + '\\as' +(1 row) + +SELECT 'a' < 'b & c'::tsquery as "true"; + true +------ + t +(1 row) + +SELECT 'a' > 'b & c'::tsquery as "false"; + false +------- + f +(1 row) + +SELECT 'a | f' < 'b & c'::tsquery as "true"; + true +------ + t +(1 row) + +SELECT 'a | ff' < 'b & c'::tsquery as "false"; + false +------- + f +(1 row) + +SELECT 'a | f | g' < 'b & c'::tsquery as "false"; + false +------- + f +(1 row) + +SELECT numnode( 'new'::tsquery ); + numnode +--------- + 1 +(1 row) + +SELECT numnode( 'new & york'::tsquery ); + numnode +--------- + 3 +(1 row) + +SELECT numnode( 'new & york | qwery'::tsquery ); + numnode +--------- + 5 +(1 row) + +SELECT 'foo & bar'::tsquery && 'asd'; + ?column? +----------------------- + 'foo' & 'bar' & 'asd' +(1 row) + +SELECT 'foo & bar'::tsquery || 'asd & fg'; + ?column? +------------------------------ + 'foo' & 'bar' | 'asd' & 'fg' +(1 row) + +SELECT 'foo & bar'::tsquery || !!'asd & fg'::tsquery; + ?column? +----------------------------------- + 'foo' & 'bar' | !( 'asd' & 'fg' ) +(1 row) + +SELECT 'foo & bar'::tsquery && 'asd | fg'; + ?column? +---------------------------------- + 'foo' & 'bar' & ( 'asd' | 'fg' ) +(1 row) + +-- tsvector-tsquery operations +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca' as "true"; + true +------ + t +(1 row) + +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:B' as "true"; + true +------ + t +(1 row) + +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:A' as "true"; + true +------ + t +(1 row) + +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:C' as "false"; + false +------- + f +(1 row) + +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:CB' as "true"; + true +------ + t +(1 row) + +SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a | s'); + ts_rank +----------- + 0.0911891 +(1 row) + +SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a | s'); + ts_rank +---------- + 0.151982 +(1 row) + +SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a | s'); + ts_rank +----------- + 0.0607927 +(1 row) + +SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a & s'); + ts_rank +---------- + 0.140153 +(1 row) + +SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a & s'); + ts_rank +---------- + 0.198206 +(1 row) + +SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a & s'); + ts_rank +----------- + 0.0991032 +(1 row) + +SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a | s'); + ts_rank_cd +------------ + 0.3 +(1 row) + +SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a | s'); + ts_rank_cd +------------ + 0.5 +(1 row) + +SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a | s'); + ts_rank_cd +------------ + 0.2 +(1 row) + +SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a & s'); + ts_rank_cd +------------ + 0.133333 +(1 row) + +SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a & s'); + ts_rank_cd +------------ + 0.16 +(1 row) + +SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a & s'); + ts_rank_cd +------------ + 0.1 +(1 row) + diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out new file mode 100644 index 000000000000..c94502d32279 --- /dev/null +++ b/src/test/regress/expected/txid.out @@ -0,0 +1,238 @@ +-- txid_snapshot data type and related functions +-- i/o +select '12:13:'::txid_snapshot; + txid_snapshot +--------------- + 12:13: +(1 row) + +select '12:18:14,16'::txid_snapshot; + txid_snapshot +--------------- + 12:18:14,16 +(1 row) + +-- errors +select '31:12:'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "31:12:" +LINE 1: select '31:12:'::txid_snapshot; + ^ +select '0:1:'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "0:1:" +LINE 1: select '0:1:'::txid_snapshot; + ^ +select '12:13:0'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "12:13:0" +LINE 1: select '12:13:0'::txid_snapshot; + ^ +select '12:16:14,13'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "12:16:14,13" +LINE 1: select '12:16:14,13'::txid_snapshot; + ^ +select '12:16:14,14'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "12:16:14,14" +LINE 1: select '12:16:14,14'::txid_snapshot; + ^ +create temp table snapshot_test ( + nr integer, + snap txid_snapshot +); +insert into snapshot_test values (1, '12:13:'); +insert into snapshot_test values (2, '12:20:13,15,18'); +insert into snapshot_test values (3, '100001:100009:100005,100007,100008'); +insert into snapshot_test values (4, '100:150: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'); +select snap from snapshot_test order by nr; + snap +------------------------------------------------------------------------------------------------------------------------------------- + 12:13: + 12:20:13,15,18 + 100001:100009:100005,100007,100008 + 100:150: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 +(4 rows) + +select txid_snapshot_xmin(snap), + txid_snapshot_xmax(snap), + txid_snapshot_xip(snap) +from snapshot_test order by nr, 1, 2, 3; + txid_snapshot_xmin | txid_snapshot_xmax | txid_snapshot_xip +--------------------+--------------------+------------------- + 12 | 20 | 13 + 12 | 20 | 15 + 12 | 20 | 18 + 100001 | 100009 | 100005 + 100001 | 100009 | 100007 + 100001 | 100009 | 100008 + 100 | 150 | 101 + 100 | 150 | 102 + 100 | 150 | 103 + 100 | 150 | 104 + 100 | 150 | 105 + 100 | 150 | 106 + 100 | 150 | 107 + 100 | 150 | 108 + 100 | 150 | 109 + 100 | 150 | 110 + 100 | 150 | 111 + 100 | 150 | 112 + 100 | 150 | 113 + 100 | 150 | 114 + 100 | 150 | 115 + 100 | 150 | 116 + 100 | 150 | 117 + 100 | 150 | 118 + 100 | 150 | 119 + 100 | 150 | 120 + 100 | 150 | 121 + 100 | 150 | 122 + 100 | 150 | 123 + 100 | 150 | 124 + 100 | 150 | 125 + 100 | 150 | 126 + 100 | 150 | 127 + 100 | 150 | 128 + 100 | 150 | 129 + 100 | 150 | 130 + 100 | 150 | 131 +(37 rows) + +select id, txid_visible_in_snapshot(id, snap) +from snapshot_test, generate_series(11, 21) id +where nr = 2; + id | txid_visible_in_snapshot +----+-------------------------- + 11 | t + 12 | t + 13 | f + 14 | t + 15 | f + 16 | t + 17 | t + 18 | f + 19 | t + 20 | f + 21 | f +(11 rows) + +-- test bsearch +select id, txid_visible_in_snapshot(id, snap) +from snapshot_test, generate_series(90, 160) id +where nr = 4; + id | txid_visible_in_snapshot +-----+-------------------------- + 90 | t + 91 | t + 92 | t + 93 | t + 94 | t + 95 | t + 96 | t + 97 | t + 98 | t + 99 | t + 100 | t + 101 | f + 102 | f + 103 | f + 104 | f + 105 | f + 106 | f + 107 | f + 108 | f + 109 | f + 110 | f + 111 | f + 112 | f + 113 | f + 114 | f + 115 | f + 116 | f + 117 | f + 118 | f + 119 | f + 120 | f + 121 | f + 122 | f + 123 | f + 124 | f + 125 | f + 126 | f + 127 | f + 128 | f + 129 | f + 130 | f + 131 | f + 132 | t + 133 | t + 134 | t + 135 | t + 136 | t + 137 | t + 138 | t + 139 | t + 140 | t + 141 | t + 142 | t + 143 | t + 144 | t + 145 | t + 146 | t + 147 | t + 148 | t + 149 | t + 150 | f + 151 | f + 152 | f + 153 | f + 154 | f + 155 | f + 156 | f + 157 | f + 158 | f + 159 | f + 160 | f +(71 rows) + +-- test current values also +select txid_current() >= txid_snapshot_xmin(txid_current_snapshot()); + ?column? +---------- + t +(1 row) + +-- we can't assume current is always less than xmax, however +select txid_visible_in_snapshot(txid_current(), txid_current_snapshot()); + txid_visible_in_snapshot +-------------------------- + f +(1 row) + +-- test 64bitness +select txid_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013'; + txid_snapshot +--------------------------------------------------------------------- + 1000100010001000:1000100010001100:1000100010001012,1000100010001013 +(1 row) + +select txid_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + txid_visible_in_snapshot +-------------------------- + f +(1 row) + +select txid_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + txid_visible_in_snapshot +-------------------------- + t +(1 row) + +-- test 64bit overflow +SELECT txid_snapshot '1:9223372036854775807:3'; + txid_snapshot +------------------------- + 1:9223372036854775807:3 +(1 row) + +SELECT txid_snapshot '1:9223372036854775808:3'; +ERROR: invalid input for txid_snapshot: "1:9223372036854775808:3" +LINE 1: SELECT txid_snapshot '1:9223372036854775808:3'; + ^ diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index ba6440fc5f99..df4a18234c9c 100755 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typnamespace = 0 OR (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR - (p1.typtype not in ('b', 'c', 'd', 'p')) OR + (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm')); @@ -56,20 +56,30 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR -----+--------- (0 rows) --- Look for basic types that don't have an array type. +-- Look for basic or enum types that don't have an array type. -- NOTE: as of 8.0, this check finds smgr and unknown. SELECT p1.oid, p1.typname FROM pg_type as p1 -WHERE p1.typtype in ('b') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS +WHERE p1.typtype in ('b','e') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND - p2.typelem = p1.oid); + p2.typelem = p1.oid and p1.typarray = p2.oid); oid | typname -----+--------- 210 | smgr 705 | unknown (2 rows) +-- Make sure typarray points to a varlena array type of our own base +SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, + p2.typelem, p2.typlen +FROM pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid) +WHERE p1.typarray <> 0 AND + (p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> -1); + oid | basetype | arraytype | typelem | typlen +-----+----------+-----------+---------+-------- +(0 rows) + -- Text conversion routines must be provided. SELECT p1.oid, p1.typname FROM pg_type as p1 diff --git a/src/test/regress/expected/union_gp.out b/src/test/regress/expected/union_gp.out index 597cb288e0b1..6d178d0aa4bd 100644 --- a/src/test/regress/expected/union_gp.out +++ b/src/test/regress/expected/union_gp.out @@ -45,14 +45,13 @@ select 1 a, row_number() over (partition by 'a') union all (select 1 a , 2 b); 1 | 2 (2 rows) --- This can preserve domain types, but we keep compatibility for now --- See MPP-7509 +-- This should preserve domain types select pg_typeof(a) from (select 'a'::information_schema.sql_identifier a union all select 'b'::information_schema.sql_identifier)a; - pg_typeof -------------------- - character varying - character varying + pg_typeof +----------------------------------- + information_schema.sql_identifier + information_schema.sql_identifier (2 rows) (select * from ( diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 7a4ed3f8c6b1..2d10369f5d1b 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -462,7 +462,7 @@ SELECT table_name, view_definition FROM information_schema.views xmlview5 | SELECT XMLPARSE(CONTENT 'x'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; xmlview7 | SELECT XMLROOT(''::xml, VERSION NO VALUE, STANDALONE YES) AS "xmlroot"; - xmlview8 | SELECT (XMLSERIALIZE(CONTENT 'good'::xml AS bpchar))::bpchar AS "xmlserialize"; + xmlview8 | SELECT (XMLSERIALIZE(CONTENT 'good'::xml AS character(10)))::character(10) AS "xmlserialize"; xmlview9 | SELECT XMLSERIALIZE(CONTENT 'good'::xml AS text) AS "xmlserialize"; (9 rows) diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source index e1a651dd9b2e..cbe46478ce81 100755 --- a/src/test/regress/input/copy.source +++ b/src/test/regress/input/copy.source @@ -39,6 +39,8 @@ COPY hash_txt_heap FROM '@abs_srcdir@/data/hash.data'; COPY hash_f8_heap FROM '@abs_srcdir@/data/hash.data'; +COPY test_tsvector FROM '@abs_srcdir@/data/tsearch.data'; + -- the data in this file has a lot of duplicates in the index key -- fields, leading to long bucket chains and lots of table expansion. -- this is therefore a stress test of the bucket overflow code (unlike diff --git a/src/test/regress/input/largeobject.source b/src/test/regress/input/largeobject.source index 961148d6e4a6..17f86ccbc551 100644 --- a/src/test/regress/input/largeobject.source +++ b/src/test/regress/input/largeobject.source @@ -14,11 +14,11 @@ BEGIN; -- lo_open(lobjId oid, mode integer) returns integer -- The mode parameter to lo_open uses two constants: --- INV_READ = 0x20000 = 2 * 16^4 --- INV_WRITE = 0x40000 = 4 * 16^4 +-- INV_READ = 0x20000 +-- INV_WRITE = 0x40000 -- The return value is a file descriptor-like value which remains valid for the -- transaction. -UPDATE lotest_stash_values SET fd = lo_open(loid, CAST((2 | 4) * 16^4 AS integer)); +UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer)); -- loread/lowrite names are wonky, different from other functions which are lo_* -- lowrite(fd integer, data bytea) returns integer @@ -55,7 +55,7 @@ END; -- Read out a portion BEGIN; -UPDATE lotest_stash_values SET fd=lo_open(loid, CAST((2 | 4) * 16^4 AS integer)); +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); -- lo_lseek(fd integer, offset integer, whence integer) returns integer -- offset is in bytes, whence is one of three values: @@ -83,6 +83,25 @@ SELECT lo_close(fd) FROM lotest_stash_values; END; +-- Test truncation. +BEGIN; +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); + +SELECT lo_truncate(fd, 10) FROM lotest_stash_values; +SELECT loread(fd, 15) FROM lotest_stash_values; + +SELECT lo_truncate(fd, 10000) FROM lotest_stash_values; +SELECT loread(fd, 10) FROM lotest_stash_values; +SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values; +SELECT lo_tell(fd) FROM lotest_stash_values; + +SELECT lo_truncate(fd, 5000) FROM lotest_stash_values; +SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values; +SELECT lo_tell(fd) FROM lotest_stash_values; + +SELECT lo_close(fd) FROM lotest_stash_values; +END; + -- lo_unlink(lobjId oid) returns integer -- return value appears to always be 1 SELECT lo_unlink(loid) from lotest_stash_values; @@ -92,7 +111,7 @@ TRUNCATE lotest_stash_values; INSERT INTO lotest_stash_values (loid) SELECT lo_import('@abs_srcdir@/data/tenk.data'); BEGIN; -UPDATE lotest_stash_values SET fd=lo_open(loid, CAST((2 | 4) * 16^4 AS integer)); +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); -- with the default BLKSZ, LOBLKSZ = 2048, so this positions us for a block -- edge case diff --git a/src/test/regress/input/table_functions.source b/src/test/regress/input/table_functions.source index dc2bec9a565a..45d3d3b6f3de 100644 --- a/src/test/regress/input/table_functions.source +++ b/src/test/regress/input/table_functions.source @@ -991,6 +991,8 @@ DEALLOCATE p1; -- FAIL: can't pass anytable as prepare argument PREPARE p2 AS SELECT * FROM multiset_2( $1 ) order by a, b; +-- This actually passes currently. You can't do anything useful with the prepared +-- statement though, as anytable's input function just throws an error. PREPARE p3(anytable) AS SELECT * FROM multiset_5( $1 ); -- FAIL: $1 is not a constant @@ -1003,8 +1005,8 @@ PREPARE p5(integer) AS SELECT * FROM project( TABLE( SELECT * FROM pg_am ), $1); PREPARE p6 AS SELECT * FROM project( TABLE( SELECT * FROM example_r ), 2) ORDER BY 1; EXECUTE p6; --- FAIL: MPP-16640: when underlying table has changed prepared statements --- will fail. If this issue is ever fixed then this should pass as well. +-- PASS: Check that the prepared statement is re-planned when the underlying +-- table changes. ALTER TABLE example_r ALTER COLUMN a TYPE numeric; EXECUTE p6; DEALLOCATE p6; diff --git a/src/test/regress/mapred/agebracket.source b/src/test/regress/mapred/agebracket.source index c0d752207744..2c33dd25b15b 100644 --- a/src/test/regress/mapred/agebracket.source +++ b/src/test/regress/mapred/agebracket.source @@ -27,6 +27,8 @@ DEFINE: - TRANSITION: NAME: myAdd LANGUAGE: perlu + PARAMETERS: [state integer, value integer] + RETURNS: value integer FUNCTION: | my ($state, $value) = @_; return $state + $value; diff --git a/src/test/regress/output/aocs.source b/src/test/regress/output/aocs.source index cf8ecfc98fd9..33fce29c3684 100644 --- a/src/test/regress/output/aocs.source +++ b/src/test/regress/output/aocs.source @@ -1046,4 +1046,4 @@ a.c1 1 | 2000000 (1 row) -drop table bms_ao_bug; \ No newline at end of file +drop table bms_ao_bug; diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source index 9705a4f58de8..843d157ce66a 100755 --- a/src/test/regress/output/copy.source +++ b/src/test/regress/output/copy.source @@ -21,6 +21,7 @@ COPY hash_i4_heap FROM '@abs_srcdir@/data/hash.data'; COPY hash_name_heap FROM '@abs_srcdir@/data/hash.data'; COPY hash_txt_heap FROM '@abs_srcdir@/data/hash.data'; COPY hash_f8_heap FROM '@abs_srcdir@/data/hash.data'; +COPY test_tsvector FROM '@abs_srcdir@/data/tsearch.data'; -- the data in this file has a lot of duplicates in the index key -- fields, leading to long bucket chains and lots of table expansion. -- this is therefore a stress test of the bucket overflow code (unlike diff --git a/src/test/regress/output/external_table.source b/src/test/regress/output/external_table.source index 14b630d69410..4043b4ef3b1a 100644 --- a/src/test/regress/output/external_table.source +++ b/src/test/regress/output/external_table.source @@ -553,15 +553,15 @@ SELECT relname, linenum, errmsg FROM gp_read_error_log('tableless_ext'); -- Earlier these functions were hidden from the catalog and would not show up -- in pg_proc SELECT * from pg_proc where proname = 'gp_read_error_log'; - proname | pronamespace | proowner | prolang | procost | prorows | provariadic | proisagg | prosecdef | proisstrict | proretset | provolatile | pronargs | pronargdefaults | prorettype | proiswin | proargtypes | proallargtypes | proargmodes | proargnames | proargdefaults | prosrc | probin | proacl | prodataaccess --------------------+--------------+----------+---------+---------+---------+-------------+----------+-----------+-------------+-----------+-------------+----------+-----------------+------------+----------+-------------+--------------------------------+---------------------+-----------------------------------------------------------------------------+----------------+-------------------+--------+--------+--------------- - gp_read_error_log | 11 | 10 | 12 | 1 | 1000 | 0 | f | f | t | t | v | 1 | 0 | 2249 | f | 25 | {25,1184,25,25,23,23,25,25,17} | {i,o,o,o,o,o,o,o,o} | {exttable,cmdtime,relname,filename,linenum,bytenum,errmsg,rawdata,rawbytes} | | gp_read_error_log | | | s + proname | pronamespace | proowner | prolang | procost | prorows | provariadic | proisagg | prosecdef | proisstrict | proretset | provolatile | pronargs | pronargdefaults | prorettype | proiswin | proargtypes | proallargtypes | proargmodes | proargnames | proargdefaults | prosrc | probin | proconfig | proacl | prodataaccess +-------------------+--------------+----------+---------+---------+---------+-------------+----------+-----------+-------------+-----------+-------------+----------+-----------------+------------+----------+-------------+--------------------------------+---------------------+-----------------------------------------------------------------------------+----------------+-------------------+--------+-----------+--------+--------------- + gp_read_error_log | 11 | 10 | 12 | 1 | 1000 | 0 | f | f | t | t | v | 1 | 0 | 2249 | f | 25 | {25,1184,25,25,23,23,25,25,17} | {i,o,o,o,o,o,o,o,o} | {exttable,cmdtime,relname,filename,linenum,bytenum,errmsg,rawdata,rawbytes} | | gp_read_error_log | | | | s (1 row) SELECT * from pg_proc where proname = 'gp_truncate_error_log'; - proname | pronamespace | proowner | prolang | procost | prorows | provariadic | proisagg | prosecdef | proisstrict | proretset | provolatile | pronargs | pronargdefaults | prorettype | proiswin | proargtypes | proallargtypes | proargmodes | proargnames | proargdefaults | prosrc | probin | proacl | prodataaccess ------------------------+--------------+----------+---------+---------+---------+-------------+----------+-----------+-------------+-----------+-------------+----------+-----------------+------------+----------+-------------+----------------+-------------+-------------+----------------+-----------------------+--------+--------+--------------- - gp_truncate_error_log | 11 | 10 | 12 | 1 | 0 | 0 | f | f | t | f | v | 1 | 0 | 16 | f | 25 | | | | | gp_truncate_error_log | | | m + proname | pronamespace | proowner | prolang | procost | prorows | provariadic | proisagg | prosecdef | proisstrict | proretset | provolatile | pronargs | pronargdefaults | prorettype | proiswin | proargtypes | proallargtypes | proargmodes | proargnames | proargdefaults | prosrc | probin | proconfig | proacl | prodataaccess +-----------------------+--------------+----------+---------+---------+---------+-------------+----------+-----------+-------------+-----------+-------------+----------+-----------------+------------+----------+-------------+----------------+-------------+-------------+----------------+-----------------------+--------+-----------+--------+--------------- + gp_truncate_error_log | 11 | 10 | 12 | 1 | 0 | 0 | f | f | t | f | v | 1 | 0 | 16 | f | 25 | | | | | gp_truncate_error_log | | | | m (1 row) -- Test for error log functionality @@ -1836,7 +1836,7 @@ CONTEXT: SQL statement "SELECT sum(distinct e1.i) as sum_i, sum(distinct e2.i) (SELECT i, j FROM exttab_udfs_1 WHERE i < 5 ) e1, (SELECT i, j FROM exttab_udfs_2 WHERE i < 10) e2 group by e1.j" -PL/pgSQL function "exttab_udfs_func1" line 2 at execute statement +PL/pgSQL function "exttab_udfs_func1" line 2 at EXECUTE statement -- Should be populated SELECT COUNT(*) > 0 FROM ( @@ -1875,7 +1875,7 @@ CONTEXT: SQL statement "SELECT sum(distinct e1.i) as sum_i, sum(distinct e2.i) (SELECT i, j FROM exttab_udfs_1 WHERE i < 5 ) e1, (SELECT i, j FROM exttab_udfs_2 WHERE i < 10) e2 group by e1.j" -PL/pgSQL function "exttab_udfs_func1" line 2 at execute statement +PL/pgSQL function "exttab_udfs_func1" line 2 at EXECUTE statement SELECT * FROM exttab_udfs_insert_1; a --- diff --git a/src/test/regress/output/largeobject.source b/src/test/regress/output/largeobject.source index 6d6e5cd24cf8..36b51fdccddf 100644 --- a/src/test/regress/output/largeobject.source +++ b/src/test/regress/output/largeobject.source @@ -11,11 +11,11 @@ INSERT INTO lotest_stash_values (loid) SELECT lo_creat(42); BEGIN; -- lo_open(lobjId oid, mode integer) returns integer -- The mode parameter to lo_open uses two constants: --- INV_READ = 0x20000 = 2 * 16^4 --- INV_WRITE = 0x40000 = 4 * 16^4 +-- INV_READ = 0x20000 +-- INV_WRITE = 0x40000 -- The return value is a file descriptor-like value which remains valid for the -- transaction. -UPDATE lotest_stash_values SET fd = lo_open(loid, CAST((2 | 4) * 16^4 AS integer)); +UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer)); -- loread/lowrite names are wonky, different from other functions which are lo_* -- lowrite(fd integer, data bytea) returns integer -- the integer is the number of bytes written @@ -58,7 +58,7 @@ SELECT lo_close(fd) FROM lotest_stash_values; END; -- Read out a portion BEGIN; -UPDATE lotest_stash_values SET fd=lo_open(loid, CAST((2 | 4) * 16^4 AS integer)); +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); -- lo_lseek(fd integer, offset integer, whence integer) returns integer -- offset is in bytes, whence is one of three values: -- SEEK_SET (= 0) meaning relative to beginning @@ -115,6 +115,70 @@ SELECT lo_close(fd) FROM lotest_stash_values; 0 (1 row) +END; +-- Test truncation. +BEGIN; +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); +SELECT lo_truncate(fd, 10) FROM lotest_stash_values; + lo_truncate +------------- + 0 +(1 row) + +SELECT loread(fd, 15) FROM lotest_stash_values; + loread +--------------- + \012Whose woo +(1 row) + +SELECT lo_truncate(fd, 10000) FROM lotest_stash_values; + lo_truncate +------------- + 0 +(1 row) + +SELECT loread(fd, 10) FROM lotest_stash_values; + loread +------------------------------------------ + \000\000\000\000\000\000\000\000\000\000 +(1 row) + +SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values; + lo_lseek +---------- + 10000 +(1 row) + +SELECT lo_tell(fd) FROM lotest_stash_values; + lo_tell +--------- + 10000 +(1 row) + +SELECT lo_truncate(fd, 5000) FROM lotest_stash_values; + lo_truncate +------------- + 0 +(1 row) + +SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values; + lo_lseek +---------- + 5000 +(1 row) + +SELECT lo_tell(fd) FROM lotest_stash_values; + lo_tell +--------- + 5000 +(1 row) + +SELECT lo_close(fd) FROM lotest_stash_values; + lo_close +---------- + 0 +(1 row) + END; -- lo_unlink(lobjId oid) returns integer -- return value appears to always be 1 @@ -127,7 +191,7 @@ SELECT lo_unlink(loid) from lotest_stash_values; TRUNCATE lotest_stash_values; INSERT INTO lotest_stash_values (loid) SELECT lo_import('@abs_srcdir@/data/tenk.data'); BEGIN; -UPDATE lotest_stash_values SET fd=lo_open(loid, CAST((2 | 4) * 16^4 AS integer)); +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); -- with the default BLKSZ, LOBLKSZ = 2048, so this positions us for a block -- edge case SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values; diff --git a/src/test/regress/output/largeobject_1.source b/src/test/regress/output/largeobject_1.source new file mode 100644 index 000000000000..f1157e457188 --- /dev/null +++ b/src/test/regress/output/largeobject_1.source @@ -0,0 +1,275 @@ +-- +-- Test large object support +-- +-- Load a file +CREATE TABLE lotest_stash_values (loid oid, fd integer); +-- lo_creat(mode integer) returns oid +-- The mode arg to lo_creat is unused, some vestigal holdover from ancient times +-- returns the large object id +INSERT INTO lotest_stash_values (loid) SELECT lo_creat(42); +-- NOTE: large objects require transactions +BEGIN; +-- lo_open(lobjId oid, mode integer) returns integer +-- The mode parameter to lo_open uses two constants: +-- INV_READ = 0x20000 +-- INV_WRITE = 0x40000 +-- The return value is a file descriptor-like value which remains valid for the +-- transaction. +UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer)); +-- loread/lowrite names are wonky, different from other functions which are lo_* +-- lowrite(fd integer, data bytea) returns integer +-- the integer is the number of bytes written +SELECT lowrite(fd, ' +Whose woods these are I think I know, +His house is in the village though. +He will not see me stopping here, +To watch his woods fill up with snow. + +My little horse must think it queer, +To stop without a farmhouse near, +Between the woods and frozen lake, +The darkest evening of the year. + +He gives his harness bells a shake, +To ask if there is some mistake. +The only other sound''s the sweep, +Of easy wind and downy flake. + +The woods are lovely, dark and deep, +But I have promises to keep, +And miles to go before I sleep, +And miles to go before I sleep. + + -- Robert Frost +') FROM lotest_stash_values; + lowrite +--------- + 578 +(1 row) + +-- lo_close(fd integer) returns integer +-- return value is 0 for success, or <0 for error (actually only -1, but...) +SELECT lo_close(fd) FROM lotest_stash_values; + lo_close +---------- + 0 +(1 row) + +END; +-- Read out a portion +BEGIN; +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); +-- lo_lseek(fd integer, offset integer, whence integer) returns integer +-- offset is in bytes, whence is one of three values: +-- SEEK_SET (= 0) meaning relative to beginning +-- SEEK_CUR (= 1) meaning relative to current position +-- SEEK_END (= 2) meaning relative to end (offset better be negative) +-- returns current position in file +SELECT lo_lseek(fd, 422, 0) FROM lotest_stash_values; + lo_lseek +---------- + 422 +(1 row) + +-- loread/lowrite names are wonky, different from other functions which are lo_* +-- loread(fd integer, len integer) returns bytea +SELECT loread(fd, 35) FROM lotest_stash_values; + loread +------------------------------------- + The woods are lovely, dark and deep +(1 row) + +SELECT lo_lseek(fd, -19, 1) FROM lotest_stash_values; + lo_lseek +---------- + 438 +(1 row) + +SELECT lowrite(fd, 'n') FROM lotest_stash_values; + lowrite +--------- + 1 +(1 row) + +SELECT lo_tell(fd) FROM lotest_stash_values; + lo_tell +--------- + 439 +(1 row) + +SELECT lo_lseek(fd, -156, 2) FROM lotest_stash_values; + lo_lseek +---------- + 422 +(1 row) + +SELECT loread(fd, 35) FROM lotest_stash_values; + loread +------------------------------------- + The woods are lonely, dark and deep +(1 row) + +SELECT lo_close(fd) FROM lotest_stash_values; + lo_close +---------- + 0 +(1 row) + +END; +-- Test truncation. +BEGIN; +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); +SELECT lo_truncate(fd, 10) FROM lotest_stash_values; + lo_truncate +------------- + 0 +(1 row) + +SELECT loread(fd, 15) FROM lotest_stash_values; + loread +--------------- + \012Whose woo +(1 row) + +SELECT lo_truncate(fd, 10000) FROM lotest_stash_values; + lo_truncate +------------- + 0 +(1 row) + +SELECT loread(fd, 10) FROM lotest_stash_values; + loread +------------------------------------------ + \000\000\000\000\000\000\000\000\000\000 +(1 row) + +SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values; + lo_lseek +---------- + 10000 +(1 row) + +SELECT lo_tell(fd) FROM lotest_stash_values; + lo_tell +--------- + 10000 +(1 row) + +SELECT lo_truncate(fd, 5000) FROM lotest_stash_values; + lo_truncate +------------- + 0 +(1 row) + +SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values; + lo_lseek +---------- + 5000 +(1 row) + +SELECT lo_tell(fd) FROM lotest_stash_values; + lo_tell +--------- + 5000 +(1 row) + +SELECT lo_close(fd) FROM lotest_stash_values; + lo_close +---------- + 0 +(1 row) + +END; +-- lo_unlink(lobjId oid) returns integer +-- return value appears to always be 1 +SELECT lo_unlink(loid) from lotest_stash_values; + lo_unlink +----------- + 1 +(1 row) + +TRUNCATE lotest_stash_values; +INSERT INTO lotest_stash_values (loid) SELECT lo_import('@abs_srcdir@/data/tenk.data'); +BEGIN; +UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); +-- with the default BLKSZ, LOBLKSZ = 2048, so this positions us for a block +-- edge case +SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values; + lo_lseek +---------- + 2030 +(1 row) + +-- this should get half of the value from page 0 and half from page 1 of the +-- large object +SELECT loread(fd, 36) FROM lotest_stash_values; + loread +-------------------------------------------------------------- + 44\011144\0111144\0114144\0119144\01188\01189\011SNAAAA\011F +(1 row) + +SELECT lo_tell(fd) FROM lotest_stash_values; + lo_tell +--------- + 2066 +(1 row) + +SELECT lo_lseek(fd, -26, 1) FROM lotest_stash_values; + lo_lseek +---------- + 2040 +(1 row) + +SELECT lowrite(fd, 'abcdefghijklmnop') FROM lotest_stash_values; + lowrite +--------- + 16 +(1 row) + +SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values; + lo_lseek +---------- + 2030 +(1 row) + +SELECT loread(fd, 36) FROM lotest_stash_values; + loread +-------------------------------------------------- + 44\011144\011114abcdefghijklmnop9\011SNAAAA\011F +(1 row) + +SELECT lo_close(fd) FROM lotest_stash_values; + lo_close +---------- + 0 +(1 row) + +END; +SELECT lo_export(loid, '@abs_builddir@/results/lotest.txt') FROM lotest_stash_values; + lo_export +----------- + 1 +(1 row) + +\lo_import 'results/lotest.txt' +\set newloid :LASTOID +-- just make sure \lo_export does not barf +\lo_export :newloid 'results/lotest2.txt' +-- This is a hack to test that export/import are reversible +-- This uses knowledge about the inner workings of large object mechanism +-- which should not be used outside it. This makes it a HACK +SELECT pageno, data FROM pg_largeobject WHERE loid = (SELECT loid from lotest_stash_values) +EXCEPT +SELECT pageno, data FROM pg_largeobject WHERE loid = :newloid; + pageno | data +--------+------ +(0 rows) + +SELECT lo_unlink(loid) FROM lotest_stash_values; + lo_unlink +----------- + 1 +(1 row) + +\lo_unlink :newloid +TRUNCATE lotest_stash_values; diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source index 95c542ba1888..e34fc21fec50 100755 --- a/src/test/regress/output/misc.source +++ b/src/test/regress/output/misc.source @@ -606,6 +606,7 @@ SELECT user_relns() AS user_relns interval_tbl iportaltest lseg_tbl + money_data num_data num_exp_add num_exp_div @@ -640,6 +641,7 @@ SELECT user_relns() AS user_relns tab2 tenk1 tenk2 + test_tsvector text_tbl time_tbl timestamp_tbl @@ -649,7 +651,7 @@ SELECT user_relns() AS user_relns toyemp usr_define_type varchar_tbl -(145 rows) +(147 rows) SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))); name diff --git a/src/test/regress/output/partindex_test.source b/src/test/regress/output/partindex_test.source index 7546b9d605ce..4ee5dbe7433a 100644 --- a/src/test/regress/output/partindex_test.source +++ b/src/test/regress/output/partindex_test.source @@ -53,9 +53,9 @@ NOTICE: building index for child partition "part_table1_1_prt_part2" select indKey, indpred, partCons from gp_build_logical_index('part_table1'::regclass) as index(logicalIndexOid Oid, nColumns smallint, indKey text, indUnique bool, indPred text, indExprs text, partCons text, defaultLevels text, indType int2) where indpred is NOT NULL; - indkey | indpred | partcons ---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------- - 1 | {BOOLEXPR :boolop and :args ({OPEXPR :opno 525 :opfuncid 150 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 1 0 0 0 0 0 0 0 ]})} {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 10 0 0 0 0 0 0 0 ]})})} | + indkey | indpred | partcons +--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------- + 1 | {BOOLEXPR :boolop and :args ({OPEXPR :opno 525 :opfuncid 150 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :consttypmod -1 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 1 0 0 0 0 0 0 0 ]})} {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :consttypmod -1 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 10 0 0 0 0 0 0 0 ]})})} | (1 row) -- add a new part - index automatically created @@ -72,9 +72,9 @@ as index(logicalIndexOid Oid, nColumns smallint, indKey text, indUnique bool, in select indKey, indpred, partCons from gp_build_logical_index('part_table1'::regclass) as index(logicalIndexOid Oid, nColumns smallint, indKey text, indUnique bool, indPred text, indExprs text, partCons text, defaultLevels text, indType int2) where indpred is NOT NULL; - indkey | indpred | partcons ---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------- - 1 | {BOOLEXPR :boolop and :args ({OPEXPR :opno 525 :opfuncid 150 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 1 0 0 0 0 0 0 0 ]})} {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 10 0 0 0 0 0 0 0 ]})})} | + indkey | indpred | partcons +--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------- + 1 | {BOOLEXPR :boolop and :args ({OPEXPR :opno 525 :opfuncid 150 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :consttypmod -1 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 1 0 0 0 0 0 0 0 ]})} {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :consttypmod -1 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 10 0 0 0 0 0 0 0 ]})})} | (1 row) -- create index on just 1 part @@ -373,9 +373,9 @@ NOTICE: building index for child partition "part_table2_1_prt_13_2_prt_other_re select indKey, indPred, defaultLevels, partCons from gp_build_logical_index('part_table2'::regclass) as index(logicalIndexOid Oid, nColumns smallint, indKey text, indUnique bool, indPred text, indExprs text, partCons text, defaultLevels text, indType int2) where indPred is NOT NULL; - indkey | indpred | defaultlevels | partcons ---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+---------- - 1 | {BOOLEXPR :boolop and :args ({OPEXPR :opno 525 :opfuncid 150 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ -24 3 0 0 0 0 0 0 ]})} {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ -48 7 0 0 0 0 0 0 ]})})} | | + indkey | indpred | defaultlevels | partcons +--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+---------- + 1 | {BOOLEXPR :boolop and :args ({OPEXPR :opno 525 :opfuncid 150 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :consttypmod -1 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ -24 3 0 0 0 0 0 0 ]})} {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :consttypmod -1 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ -48 7 0 0 0 0 0 0 ]})})} | | (1 row) -- create index on expression on all parts @@ -888,18 +888,18 @@ create index part_table5_i3 on part_table5_1_prt_def(abs(c1)); select indPred, indExprs, partCons from gp_build_logical_index('part_table5'::regclass) as index(logicalIndexOid Oid, nColumns smallint, indKey text, indUnique bool, indPred text, indExprs text, partCons text, defaultLevels text, indType int2) where defaultLevels is NULL and partcons is NULL; - indpred | indexprs | partcons ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------+---------- - {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 100 0 0 0 0 0 0 0 ]})} | | + indpred | indexprs | partcons +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------+---------- + {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :consttypmod -1 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 100 0 0 0 0 0 0 0 ]})} | | (1 row) -- returns 1 row with index on whole table select indPred from gp_build_logical_index('part_table5'::regclass) as index(logicalIndexOid Oid, nColumns smallint, indKey text, indUnique bool, indPred text, indExprs text, partCons text, defaultLevels text, indType int2) where partCons is NULL; - indpred --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 100 0 0 0 0 0 0 0 ]})} + indpred +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + {OPEXPR :opno 97 :opfuncid 66 :opresulttype 16 :opretset false :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varlevelsup 0 :varnoold 1 :varoattno 1} {CONST :consttype 23 :consttypmod -1 :constlen 4 :constbyval true :constisnull false :constvalue 4 [ 100 0 0 0 0 0 0 0 ]})} (1 row) -- returns 1 row with index on default part diff --git a/src/test/regress/output/table_functions.source b/src/test/regress/output/table_functions.source index 72329694786c..3c6165293964 100644 --- a/src/test/regress/output/table_functions.source +++ b/src/test/regress/output/table_functions.source @@ -2170,12 +2170,12 @@ View definition: SELECT tf.a, tf.b FROM multiset_2(TABLE( SELECT example.a, example.b FROM example - SCATTER BY example.a::text || example.b, 5)) tf(a, b); + SCATTER BY example.a || example.b, 5)) tf(a, b); SELECT pg_get_viewdef('v2'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------------------------- - SELECT tf.a, tf.b FROM multiset_2(TABLE(SELECT example.a, example.b FROM example SCATTER BY ((example.a)::text || example.b), 5)) tf(a, b); + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------------------------- + SELECT tf.a, tf.b FROM multiset_2(TABLE(SELECT example.a, example.b FROM example SCATTER BY (example.a || example.b), 5)) tf(a, b); (1 row) CREATE VIEW v3 AS @@ -3067,8 +3067,9 @@ ERROR: function multiset_2(unknown) does not exist LINE 1: PREPARE p2 AS SELECT * FROM multiset_2( $1 ) order by a, b; ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- This actually passes currently. You can't do anything useful with the prepared +-- statement though, as anytable's input function just throws an error. PREPARE p3(anytable) AS SELECT * FROM multiset_5( $1 ); -ERROR: type "anytable" is not a valid parameter for PREPARE -- FAIL: $1 is not a constant PREPARE p4 AS SELECT * FROM project( TABLE( SELECT * FROM pg_am ), $1); ERROR: unable to resolve function argument (execQual.c:5348) @@ -3097,12 +3098,24 @@ EXECUTE p6; value4.1/1 (10 rows) --- FAIL: MPP-16640: when underlying table has changed prepared statements --- will fail. If this issue is ever fixed then this should pass as well. +-- PASS: Check that the prepared statement is re-planned when the underlying +-- table changes. ALTER TABLE example_r ALTER COLUMN a TYPE numeric; EXECUTE p6; -ERROR: attribute 1 has wrong type (execQual.c:642) (seg1 slice1 maple.local:50001 pid=64248) (cdbdisp.c:1462) -DETAIL: Table has type numeric, but query expects integer. + b +------------- + value1.1/4 + value1.2/4 + value1.3/4 + value1.4/4 + value2.1/3 + value2.2/3 + value2.3/3 + value3.1/2 + value3.2/2 + value4.1/1 +(10 rows) + DEALLOCATE p6; -- ======= -- Cleanup diff --git a/src/test/regress/output/table_functions_optimizer.source b/src/test/regress/output/table_functions_optimizer.source index 9486396fdfa6..5797a4206696 100644 --- a/src/test/regress/output/table_functions_optimizer.source +++ b/src/test/regress/output/table_functions_optimizer.source @@ -2171,12 +2171,12 @@ View definition: SELECT tf.a, tf.b FROM multiset_2(TABLE( SELECT example.a, example.b FROM example - SCATTER BY example.a::text || example.b, 5)) tf(a, b); + SCATTER BY example.a || example.b, 5)) tf(a, b); SELECT pg_get_viewdef('v2'::regclass); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------------------------- - SELECT tf.a, tf.b FROM multiset_2(TABLE(SELECT example.a, example.b FROM example SCATTER BY ((example.a)::text || example.b), 5)) tf(a, b); + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------------------------- + SELECT tf.a, tf.b FROM multiset_2(TABLE(SELECT example.a, example.b FROM example SCATTER BY (example.a || example.b), 5)) tf(a, b); (1 row) CREATE VIEW v3 AS @@ -3068,8 +3068,9 @@ ERROR: function multiset_2(unknown) does not exist LINE 1: PREPARE p2 AS SELECT * FROM multiset_2( $1 ) order by a, b; ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- This actually passes currently. You can't do anything useful with the prepared +-- statement though, as anytable's input function just throws an error. PREPARE p3(anytable) AS SELECT * FROM multiset_5( $1 ); -ERROR: type "anytable" is not a valid parameter for PREPARE -- FAIL: $1 is not a constant PREPARE p4 AS SELECT * FROM project( TABLE( SELECT * FROM pg_am ), $1); ERROR: unable to resolve function argument (execQual.c:5348) @@ -3098,12 +3099,24 @@ EXECUTE p6; value4.1/1 (10 rows) --- FAIL: MPP-16640: when underlying table has changed prepared statements --- will fail. If this issue is ever fixed then this should pass as well. +-- PASS: Check that the prepared statement is re-planned when the underlying +-- table changes. ALTER TABLE example_r ALTER COLUMN a TYPE numeric; EXECUTE p6; -ERROR: attribute 1 has wrong type (execQual.c:642) (seg1 slice1 maple.local:50001 pid=64248) (cdbdisp.c:1462) -DETAIL: Table has type numeric, but query expects integer. + b +------------- + value1.1/4 + value1.2/4 + value1.3/4 + value1.4/4 + value2.1/3 + value2.2/3 + value2.3/3 + value3.1/2 + value3.2/2 + value4.1/1 +(10 rows) + DEALLOCATE p6; -- ======= -- Cleanup diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 74243967f82c..fe1dac504ba7 100755 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -1,8 +1,14 @@ # ---------- -# The first group of parallel test -# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.39 2007/02/09 03:35:35 tgl Exp $ +# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.46 2007/11/24 19:49:23 darcy Exp $ +# +# By convention, we put no more than twenty tests in any one parallel group; +# this limits the number of connections needed to run the tests. +# ---------- + # ---------- -test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid +# The first group of parallel tests +# ---------- +test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money # Depends on things setup during char, varchar and text test: strings @@ -10,14 +16,16 @@ test: strings test: numerology # ---------- -# The second group of parallel test +# The second group of parallel tests # ---------- -test: point lseg box path polygon circle date time timetz timestamp timestamptz interval abstime reltime tinterval inet comments oidjoins type_sanity opr_sanity +test: point lseg box path polygon circle date time timetz timestamp timestamptz interval abstime reltime tinterval inet tstypes comments -# Depends on point, lseg, box, path, polygon and circle -test: geometry -# Depends on interval, timetz, timestamp, timestamptz, reltime and abstime -test: horology +# ---------- +# Another group of parallel tests +# geometry depends on point, lseg, box, path, polygon and circle +# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime +# ---------- +test: geometry horology oidjoins type_sanity opr_sanity # ---------- # These four each depend on the previous one @@ -37,7 +45,7 @@ test: create_function_2 test: copy copyselect # ---------- -# The third group of parallel test +# Another group of parallel tests # ---------- test: constraints create_misc create_aggregate create_operator vacuum drop_if_exists @@ -70,7 +78,7 @@ test: select ignore: random # ---------- -# The fourth group of parallel test +# Another group of parallel tests # ---------- test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates random portals arrays btree_index hash_index update delete lock @@ -91,9 +99,9 @@ test: privileges test: misc # ---------- -# The fifth group of parallel test +# Another group of parallel tests # ---------- -test: select_views portals_p2 cluster dependency guc +test: select_views portals_p2 cluster dependency guc tsearch tsdicts # 'rules' test is disabled in GPDB. Maintaining the list of views in it is # too painful, and there are also errors because of cross-segment UPDATEs @@ -109,10 +117,10 @@ test: select_views portals_p2 cluster dependency guc #test: foreign_key # ---------- -# The sixth group of parallel test +# Another group of parallel tests # ---------- # "plpgsql" cannot run concurrently with "rules" -test: limit copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes xml +test: plancache limit copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes xml # 83MERGE_FIXME: the largeobject test is temporarily disabled due to test errors # test: largeobject diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 76c6efe9554f..5ae3ba7d9da4 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/test/regress/pg_regress.c,v 1.36 2007/07/18 21:19:17 alvherre Exp $ + * $PostgreSQL: pgsql/src/test/regress/pg_regress.c,v 1.41.2.4 2009/11/14 15:39:41 mha Exp $ * *------------------------------------------------------------------------- */ @@ -86,6 +86,7 @@ static char *encoding = NULL; static _stringlist *schedulelist = NULL; static _stringlist *extra_tests = NULL; static char *temp_install = NULL; +static char *temp_config = NULL; static char *top_builddir = NULL; static int temp_port = 65432; static bool nolocale = false; @@ -414,16 +415,18 @@ replace_string(char *string, char *replace, char *replacement) * the given suffix. */ static void -convert_sourcefiles_in(char *source, char *dest, char *suffix) +convert_sourcefiles_in(char *source, char * dest_dir, char *dest, char *suffix) { char abs_srcdir[MAXPGPATH]; char abs_builddir[MAXPGPATH]; char testtablespace[MAXPGPATH]; char indir[MAXPGPATH]; - char outdir[MAXPGPATH]; + struct stat st; + int ret; char **name; char **names; int count = 0; + #ifdef WIN32 char *c; #endif @@ -436,8 +439,8 @@ convert_sourcefiles_in(char *source, char *dest, char *suffix) } /* - * in a VPATH build, use the provided source directory; otherwise, use - * the current directory. + * in a VPATH build, use the provided source directory; otherwise, use the + * current directory. */ if (srcdir) strlcpy(abs_srcdir, srcdir, MAXPGPATH); @@ -445,15 +448,32 @@ convert_sourcefiles_in(char *source, char *dest, char *suffix) strlcpy(abs_srcdir, abs_builddir, MAXPGPATH); snprintf(indir, MAXPGPATH, "%s/%s", abs_srcdir, source); + + /* Check that indir actually exists and is a directory */ + ret = stat(indir, &st); + if (ret != 0 || !S_ISDIR(st.st_mode)) + { + /* + * No warning, to avoid noise in tests that do not have + * these directories; for example, ecpg, contrib and src/pl. + */ + return; + } + names = pgfnames(indir); if (!names) /* Error logged in pgfnames */ exit_nicely(2); /* also create the output directory if not present */ - snprintf(outdir, sizeof(outdir), "%s/%s", abs_srcdir, dest); - if (!directory_exists(outdir)) - make_directory(outdir); + { + char outdir[MAXPGPATH]; + + snprintf(outdir, MAXPGPATH, "%s/%s", dest_dir, dest); + + if (!directory_exists(outdir)) + make_directory(outdir); + } #ifdef WIN32 /* in Win32, replace backslashes with forward slashes */ @@ -465,7 +485,6 @@ convert_sourcefiles_in(char *source, char *dest, char *suffix) *c = '/'; #endif - /* try to create the test tablespace dir if it doesn't exist */ snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", abs_builddir); #ifdef WIN32 @@ -506,7 +525,8 @@ convert_sourcefiles_in(char *source, char *dest, char *suffix) /* build the full actual paths to open */ snprintf(prefix, strlen(*name) - 6, "%s", *name); snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name); - snprintf(destfile, MAXPGPATH, "%s/%s.%s", outdir, prefix, suffix); + snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest, + prefix, suffix); infile = fopen(srcfile, "r"); if (!infile) @@ -573,20 +593,10 @@ convert_sourcefiles_in(char *source, char *dest, char *suffix) static void convert_sourcefiles(void) { - struct stat st; - int ret; - - ret = stat("input", &st); - if (ret == 0 && S_ISDIR(st.st_mode)) - convert_sourcefiles_in("input", "sql", "sql"); - - ret = stat("output", &st); - if (ret == 0 && S_ISDIR(st.st_mode)) - convert_sourcefiles_in("output", "expected", "out"); + convert_sourcefiles_in("input", inputdir, "sql", "sql"); + convert_sourcefiles_in("output", outputdir, "expected", "out"); - ret = stat("mapred", &st); - if (ret == 0 && S_ISDIR(st.st_mode)) - convert_sourcefiles_in("mapred", "yml", "yml"); + convert_sourcefiles_in("mapred", inputdir, "yml", "yml"); } /* @@ -842,6 +852,19 @@ initialize_environment(void) doputenv("PGPORT", s); } + /* + * GNU make stores some flags in the MAKEFLAGS environment variable to + * pass arguments to its own children. If we are invoked by make, + * that causes the make invoked by us to think its part of the make + * task invoking us, and so it tries to communicate with the toplevel + * make. Which fails. + * + * Unset the variable to protect against such problems. We also reset + * MAKELEVEL to be certain the child doesn't notice the make above us. + */ + unsetenv("MAKEFLAGS"); + unsetenv("MAKELEVEL"); + /* * Adjust path variables to point into the temp-install tree */ @@ -872,8 +895,10 @@ initialize_environment(void) add_to_path("LD_LIBRARY_PATH", ':', libdir); add_to_path("DYLD_LIBRARY_PATH", ':', libdir); add_to_path("LIBPATH", ':', libdir); -#if defined(WIN32) || defined(__CYGWIN__) +#if defined(WIN32) add_to_path("PATH", ';', libdir); +#elif defined(__CYGWIN__) + add_to_path("PATH", ':', libdir); #endif } else @@ -1030,57 +1055,6 @@ spawn_process(const char *cmdline) ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); - - Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); - if (Advapi32Handle != NULL) - { - _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken"); - } - - if (_CreateRestrictedToken == NULL) - { - if (Advapi32Handle != NULL) - FreeLibrary(Advapi32Handle); - fprintf(stderr, "ERROR: Unable to create restricted tokens on this platform\n"); - exit_nicely(2); - } - - /* Open the current token to use as base for the restricted one */ - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) - { - fprintf(stderr, "Failed to open process token: %lu\n", GetLastError()); - exit_nicely(2); - } - - /* Allocate list of SIDs to remove */ - ZeroMemory(&dropSids, sizeof(dropSids)); - if (!AllocateAndInitializeSid(&NtAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &dropSids[0].Sid) || - !AllocateAndInitializeSid(&NtAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &dropSids[1].Sid)) - { - fprintf(stderr, "Failed to allocate SIDs: %lu\n", GetLastError()); - exit_nicely(2); - } - - b = _CreateRestrictedToken(origToken, - DISABLE_MAX_PRIVILEGE, - sizeof(dropSids)/sizeof(dropSids[0]), - dropSids, - 0, NULL, - 0, NULL, - &restrictedToken); - - FreeSid(dropSids[1].Sid); - FreeSid(dropSids[0].Sid); - CloseHandle(origToken); - FreeLibrary(Advapi32Handle); - - if (!b) - { - fprintf(stderr, "Failed to create restricted token: %lu\n", GetLastError()); - exit_nicely(2); - } Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); if (Advapi32Handle != NULL) @@ -1162,7 +1136,7 @@ spawn_process(const char *cmdline) free(cmdline2); - ResumeThread(pi.hThread); + ResumeThread(pi.hThread); CloseHandle(pi.hThread); return pi.hProcess; #endif @@ -1757,7 +1731,7 @@ run_schedule(const char *schedule, test_function tfunc) bool newdiff; if (tl) - tl = tl->next; /* tl has the same length as rl and el + tl = tl->next; /* tl has the same lengt has rl and el * if it exists */ newdiff = results_differ(tests[i], rl->str, el->str); @@ -1849,7 +1823,7 @@ run_single_test(const char *test, test_function tfunc) bool newdiff; if (tl) - tl = tl->next; /* tl has the same length as rl and el if it + tl = tl->next; /* tl has the same lengt has rl and el if it * exists */ newdiff = results_differ(test, rl->str, el->str); @@ -2143,6 +2117,7 @@ help(void) printf(_(" --no-locale use C locale\n")); printf(_(" --top-builddir=DIR (relative) path to top level build directory\n")); printf(_(" --temp-port=PORT port number to start temp postmaster on\n")); + printf(_(" --temp-config=PATH append contents of PATH to temporary config\n")); printf(_("\n")); printf(_("Options for using an existing installation:\n")); printf(_(" --host=HOST use postmaster running on HOST\n")); @@ -2186,7 +2161,8 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc {"psqldir", required_argument, NULL, 16}, {"srcdir", required_argument, NULL, 17}, {"create-role", required_argument, NULL, 18}, - {"init-file", required_argument, NULL, 19}, + {"temp-config", required_argument, NULL, 19}, + {"init-file", required_argument, NULL, 20}, {NULL, 0, NULL, 0} }; @@ -2282,10 +2258,13 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc case 18: split_to_stringlist(strdup(optarg), ", ", &extraroles); break; - case 19: + case 19: + temp_config = strdup(optarg); + break; + case 20: initfile = strdup(optarg); break; - default: + default: /* getopt_long already emitted a complaint */ fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), progname); @@ -2374,6 +2353,32 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc exit_nicely(2); } + /* add any extra config specified to the postgresql.conf */ + if (temp_config != NULL) + { + FILE *extra_conf; + FILE *pg_conf; + char line_buf[1024]; + + snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install); + pg_conf = fopen(buf, "a"); + if (pg_conf == NULL) + { + fprintf(stderr, _("\n%s: could not open %s for adding extra config:\nError was %s\n"), progname, buf, strerror(errno)); + exit_nicely(2); + } + extra_conf = fopen(temp_config, "r"); + if (extra_conf == NULL) + { + fprintf(stderr, _("\n%s: could not open %s to read extra config:\nError was %s\n"), progname, temp_config, strerror(errno)); + exit_nicely(2); + } + while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL) + fputs(line_buf, pg_conf); + fclose(extra_conf); + fclose(pg_conf); + } + /* * Start the temp postmaster */ diff --git a/src/test/regress/pg_regress.h b/src/test/regress/pg_regress.h index 94db0b4d4559..40b6e7727656 100644 --- a/src/test/regress/pg_regress.h +++ b/src/test/regress/pg_regress.h @@ -42,6 +42,7 @@ extern bool debug; extern char *inputdir; extern char *outputdir; extern bool optimizer_enabled; + /* * This should not be global but every module should be able to read command * line parameters. diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 69f78326b316..80c129cb47f8 100755 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.37 2007/02/09 03:35:35 tgl Exp $ +# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.43 2007/11/24 20:41:35 tgl Exp $ # This should probably be in an order similar to parallel_schedule. test: boolean test: char @@ -13,7 +13,10 @@ test: float4 test: float8 test: bit test: numeric +test: txid test: uuid +test: enum +test: money test: strings test: numerology test: point @@ -32,12 +35,13 @@ test: abstime test: reltime test: tinterval test: inet +test: tstypes test: comments +test: geometry +test: horology test: oidjoins test: type_sanity test: opr_sanity -test: geometry -test: horology test: insert test: create_function_1 test: create_type @@ -90,6 +94,8 @@ test: cluster test: dependency test: guc test: combocid +test: tsearch +test: plancache test: limit ignore: plpgsql test: copy2 @@ -99,6 +105,7 @@ test: rangefuncs test: prepare test: without_oid test: conversion +test: tsdicts test: truncate test: alter_table test: sequence diff --git a/src/test/regress/sql/.gitignore b/src/test/regress/sql/.gitignore index e8668668f325..480f514f95a7 100644 --- a/src/test/regress/sql/.gitignore +++ b/src/test/regress/sql/.gitignore @@ -1,18 +1,20 @@ +/constraints.sql +/copy.sql +/create_function_1.sql +/create_function_2.sql +/largeobject.sql +/misc.sql +/tablespace.sql + restore_restoredb.sql sreh.sql -tablespace.sql upg2.sql gptokencheck.sql loadcat33.sql loadcat34.sql -misc.sql bkuprestore_advance.sql bkuprestore_cleanup.sql caql.sql -constraints.sql -copy.sql -create_function_1.sql -create_function_2.sql aocs.sql appendonly.sql bkup_bkupdb.sql diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index 9aac5940ddd3..ae7fd80b215e 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -80,6 +80,12 @@ group by ten having exists (select 1 from onek b where sum(distinct a.four + b.four) = b.four); +-- Test handling of sublinks within outer-level aggregates. +-- Per bug report from Daniel Grace. +select + (select max((select i.unique2 from tenk1 i where i.unique1 = o.unique1))) +from tenk1 o; + -- -- test for bitwise integer aggregates -- diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index ba3c3d7307c3..1fc235e1ab35 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -177,8 +177,8 @@ DROP TABLE oldcor; -- typname is out of sync CREATE TABLE cor (a int, b float, c text); UPDATE pg_type SET typname='newcor' WHERE typrelid='cor'::regclass; -ALTER TABLE cor RENAME TO newcor; -ALTER TABLE newcor RENAME TO cor; +ALTER TABLE cor RENAME TO newcor2; +ALTER TABLE newcor2 RENAME TO cor; DROP TABLE cor; -- relname is out of sync @@ -547,7 +547,7 @@ drop table atacc1; -- adding a new column as primary key to a non-empty table. -- should fail unless the column has a non-null default value. -create table atacc1 ( test int ) distributed by (test); +create table atacc1 ( test int ); insert into atacc1 (test) values (0); -- add a primary key column without a default (fails). alter table atacc1 add column test2 int primary key; @@ -1103,6 +1103,15 @@ select * from another order by 1,2; drop table another; +-- disallow recursive containment of row types +create temp table recur1 (f1 int); +alter table recur1 add column f2 recur1; -- fails +alter table recur1 add column f2 recur1[]; -- fails +create temp table recur2 (f1 int, f2 recur1); +alter table recur1 add column f2 recur2; -- fails +alter table recur1 add column f2 int; +alter table recur1 alter column f2 type recur2; -- fails + -- -- alter function -- @@ -1229,7 +1238,8 @@ select relname, relnatts from pg_class where relname = 'ao1'; -- check col details in pg_attribute select pg_class.relname, attname, typname from pg_attribute, pg_class, pg_type where attrelid = pg_class.oid and pg_class.relname = 'ao1' and atttypid = pg_type.oid and attname = 'col3'; --- no explicit entry in pg_attrdef for NULL default +-- There's an explicit entry in pg_attrdef for the NULL default (although it has +-- the same effect as no entry). select relname, attname, adsrc from pg_class, pg_attribute, pg_attrdef where attrelid = pg_class.oid and adrelid = pg_class.oid and adnum = pg_attribute.attnum and pg_class.relname = 'ao1'; @@ -1313,7 +1323,8 @@ select relname, relnatts from pg_class where relname = 'aoco1'; -- check col details in pg_attribute select pg_class.relname, attname, typname from pg_attribute, pg_class, pg_type where attrelid = pg_class.oid and pg_class.relname = 'aoco1' and atttypid = pg_type.oid and attname = 'col3'; --- no explicit entry in pg_attrdef for NULL default +-- There's an explicit entry in pg_attrdef for the NULL default (although it has +-- the same effect as no entry). select relname, attname, adsrc from pg_class, pg_attribute, pg_attrdef where attrelid = pg_class.oid and adrelid = pg_class.oid and adnum = pg_attribute.attnum and pg_class.relname = 'aoco1'; --- diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index 893a53d4e6f8..d7b6ccbda413 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -328,6 +328,34 @@ INSERT INTO arraggtest (f1, f2, f3) VALUES ('{}','{{pink,white,blue,red,grey,orange}}','{2.1,1.87,1.4,2.2}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; +-- A few simple tests for arrays of composite types + +create type comptype as (f1 int, f2 text); + +create table comptable (c1 comptype, c2 comptype[], distkey int4) distributed by (distkey); + +-- XXX would like to not have to specify row() construct types here ... +insert into comptable + values (row(1,'foo'), array[row(2,'bar')::comptype, row(3,'baz')::comptype]); + +-- check that implicitly named array type _comptype isn't a problem +create type _comptype as enum('fooey'); + +select c1, c2 from comptable; +select c2[2].f2 from comptable; + +drop type _comptype; +drop table comptable; +drop type comptype; + +-- Insert/update on a column that is array of composite + +create temp table t1 (f1 int8_tbl[], distkey int4) distributed by (distkey); +insert into t1 (f1[5].q1) values(42); +select f1 from t1; +update t1 set f1[5].q2 = 43; +select f1 from t1; + create or replace function unnest1(anyarray) returns setof anyelement as $$ select $1[s] from generate_subscripts($1,1) g(s); diff --git a/src/test/regress/sql/bitmapscan.sql b/src/test/regress/sql/bitmapscan.sql index 8799a486aab7..01dafc408d6a 100644 --- a/src/test/regress/sql/bitmapscan.sql +++ b/src/test/regress/sql/bitmapscan.sql @@ -279,3 +279,5 @@ INSERT INTO card_heap_table_w_bitmap (id, v, highCardinalityHighDomain) SELECT i, CAST(i AS VARCHAR), i % 50000 FROM generate_series(1000001, 1050000) i; SELECT COUNT(DISTINCT(highCardinalityHighDomain)) AS distinct_hchd FROM card_heap_table_w_bitmap ORDER BY distinct_hchd; + +drop table if exists bm_test; diff --git a/src/test/regress/sql/boolean.sql b/src/test/regress/sql/boolean.sql index 4282c15d5d13..f875deeddf2a 100644 --- a/src/test/regress/sql/boolean.sql +++ b/src/test/regress/sql/boolean.sql @@ -14,7 +14,7 @@ SELECT 1 AS one; SELECT bool 't' AS true; -SELECT bool 'f' AS false; +SELECT bool ' f ' AS false; SELECT bool 't' or bool 'f' AS true; @@ -26,6 +26,14 @@ SELECT bool 't' = bool 'f' AS false; SELECT bool 't' <> bool 'f' AS true; +-- explicit casts to/from text +SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; +SELECT ' true '::text::boolean AS true, + ' FALSE'::text::boolean AS false; +SELECT true::boolean::text AS true, false::boolean::text AS false; + +SELECT ' tru e '::text::boolean AS invalid; -- error +SELECT ''::text::boolean AS invalid; -- error CREATE TABLE BOOLTBL1 (f1 bool); diff --git a/src/test/regress/sql/case_gp.sql b/src/test/regress/sql/case_gp.sql index 0bf996c7a750..025a8c1198a1 100644 --- a/src/test/regress/sql/case_gp.sql +++ b/src/test/regress/sql/case_gp.sql @@ -89,7 +89,7 @@ SELECT a,b,c,CASE c WHEN IS NOT DISTINCT FROM b THEN a -- DECODE(): Oracle compatibility -- SELECT decode(null,null,true,false); -SELECT decode(NULL, 1, 100, NULL, 200, 300); +SELECT decode(NULL::integer, 1, 100, NULL, 200, 300); SELECT decode('1'::text, '1', 100, '2', 200); SELECT decode(2, 1, 'ABC', 2, 'DEF'); SELECT decode('2009-02-05'::date, '2009-02-05', 'ok'); diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index 9ca31458f9ca..711bc29ba43f 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -74,7 +74,8 @@ SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY 1; -- Verify that foreign key link still works INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail'); -SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass; +SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass +ORDER BY 1; SELECT relname, relkind, @@ -123,7 +124,7 @@ INSERT INTO clstr_3 VALUES (1); CLUSTER clstr_2; CLUSTER clstr_1_pkey ON clstr_1; -CLUSTER clstr_2_pkey ON clstr_2; +CLUSTER clstr_2 USING clstr_2_pkey; SELECT * FROM clstr_1 UNION ALL SELECT * FROM clstr_2 UNION ALL SELECT * FROM clstr_3 ORDER BY 1; @@ -154,8 +155,43 @@ INSERT INTO clstr_1 VALUES (1); CLUSTER clstr_1; SELECT * FROM clstr_1 ORDER BY 1; +-- Test MVCC-safety of cluster. There isn't much we can do to verify the +-- results with a single backend... + +CREATE TABLE clustertest (key int, distkey int) DISTRIBUTED BY (distkey); +CREATE INDEX clustertest_pkey ON clustertest (key); + +INSERT INTO clustertest VALUES (10, 1); +INSERT INTO clustertest VALUES (20, 2); +INSERT INTO clustertest VALUES (30, 1); +INSERT INTO clustertest VALUES (40, 2); +INSERT INTO clustertest VALUES (50, 3); + +-- Use a transaction so that updates are not committed when CLUSTER sees 'em +BEGIN; + +-- Test update where the old row version is found first in the scan +UPDATE clustertest SET key = 100 WHERE key = 10; + +-- Test update where the new row version is found first in the scan +UPDATE clustertest SET key = 35 WHERE key = 40; + +-- Test longer update chain +UPDATE clustertest SET key = 60 WHERE key = 50; +UPDATE clustertest SET key = 70 WHERE key = 60; +UPDATE clustertest SET key = 80 WHERE key = 70; + +SELECT key FROM clustertest; +CLUSTER clustertest_pkey ON clustertest; +SELECT key FROM clustertest; + +COMMIT; + +SELECT key FROM clustertest; + -- clean up \c - +DROP TABLE clustertest; DROP TABLE clstr_1; DROP TABLE clstr_2; DROP TABLE clstr_3; diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql index b193388ab8cd..7c23ba253c4e 100644 --- a/src/test/regress/sql/copy2.sql +++ b/src/test/regress/sql/copy2.sql @@ -1,4 +1,4 @@ -CREATE TABLE x ( +CREATE TEMP TABLE x ( a serial, b int, c text not null default 'stuff', @@ -116,7 +116,7 @@ COPY x TO stdout; COPY x (c, e) TO stdout; COPY x (b, e) TO stdout WITH NULL 'I''m null'; -CREATE TABLE y ( +CREATE TEMP TABLE y ( col1 text, col2 text ); diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index 7785b01d4019..172a36c89b09 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -96,6 +96,8 @@ SELECT * FROM fast_emp4000 SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; +SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; + SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; @@ -119,6 +121,8 @@ SELECT * FROM fast_emp4000 SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; +SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; + SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; @@ -193,7 +197,7 @@ CREATE INDEX func_index_index on func_index_heap (textcat(f1,f2)); INSERT INTO func_index_heap VALUES('ABC','DEF'); INSERT INTO func_index_heap VALUES('AB','CDEFG'); INSERT INTO func_index_heap VALUES('QWE','RTY'); --- this should fail because of unique index: +-- this should fail because of unique index: (In GPDB, the index isn't unique, so no error) INSERT INTO func_index_heap VALUES('ABCD', 'EF'); -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); @@ -209,7 +213,7 @@ CREATE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops); INSERT INTO func_index_heap VALUES('ABC','DEF'); INSERT INTO func_index_heap VALUES('AB','CDEFG'); INSERT INTO func_index_heap VALUES('QWE','RTY'); --- this should fail because of unique index: +-- this should fail because of unique index: (In GPDB, the index isn't unique, so no error) INSERT INTO func_index_heap VALUES('ABCD', 'EF'); -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); @@ -228,20 +232,22 @@ create index hash_f8_index_3 on hash_f8_heap(random) where seqno > 1000; -- Unfortunately this only tests about half the code paths because there are -- no concurrent updates happening to the table at the same time. -CREATE TABLE concur_heap (f1 text, f2 text) distributed by (f1); +CREATE TABLE concur_heap (f1 text, f2 text, dk text) distributed by (dk); -- empty table CREATE INDEX CONCURRENTLY concur_index1 ON concur_heap(f2,f1); -- MPP-9772, MPP-9773: re-enable CREATE INDEX CONCURRENTLY (off by default) set gp_create_index_concurrently=true; CREATE INDEX CONCURRENTLY concur_index1 ON concur_heap(f2,f1); -INSERT INTO concur_heap VALUES ('a','b'); -INSERT INTO concur_heap VALUES ('b','b'); +INSERT INTO concur_heap VALUES ('a','b', '1'); +INSERT INTO concur_heap VALUES ('b','b', '1'); +INSERT INTO concur_heap VALUES ('c','c', '2'); +INSERT INTO concur_heap VALUES ('d','d', '3'); -- unique index -CREATE UNIQUE INDEX CONCURRENTLY concur_index2 ON concur_heap(f1); +CREATE UNIQUE INDEX CONCURRENTLY concur_index2 ON concur_heap(dk, f1); -- check if constraint is set up properly to be enforced -INSERT INTO concur_heap VALUES ('b','x'); +INSERT INTO concur_heap VALUES ('b','x', '1'); -- check if constraint is enforced properly at build time ---CREATE UNIQUE INDEX CONCURRENTLY concur_index3 ON concur_heap(f2); +CREATE UNIQUE INDEX CONCURRENTLY concur_index3 ON concur_heap(dk, f2); -- test that expression indexes and partial indexes work concurrently CREATE INDEX CONCURRENTLY concur_index4 on concur_heap(f2) WHERE f1='a'; CREATE INDEX CONCURRENTLY concur_index5 on concur_heap(f2) WHERE f1='x'; @@ -264,3 +270,45 @@ COMMIT; \d concur_heap DROP TABLE concur_heap; + +-- +-- Tests for IS NULL with b-tree indexes +-- + +SELECT unique1, unique2 INTO onek_with_null FROM onek; +INSERT INTO onek_with_null (unique1,unique2) VALUES (NULL, -1), (NULL, NULL); +CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2,unique1); + +SET enable_seqscan = OFF; +SET enable_indexscan = ON; +SET enable_bitmapscan = ON; + +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; + +DROP INDEX onek_nulltest; + +CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc,unique1); + +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; + +DROP INDEX onek_nulltest; + +CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc nulls last,unique1); + +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; + +DROP INDEX onek_nulltest; + +CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 nulls first,unique1); + +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; + +RESET enable_seqscan; +RESET enable_indexscan; +RESET enable_bitmapscan; + +DROP TABLE onek_with_null; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index a777cb425e24..a8f6fddd6bbb 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -231,3 +231,8 @@ CREATE TABLE array_index_op_test ( i int4[], t text[] ); + +CREATE TABLE test_tsvector( + t text, + a tsvector +); diff --git a/src/test/regress/sql/decode_expr.sql b/src/test/regress/sql/decode_expr.sql index 96e6bf2e9861..1761000fb367 100644 --- a/src/test/regress/sql/decode_expr.sql +++ b/src/test/regress/sql/decode_expr.sql @@ -613,6 +613,8 @@ select * from vtable order by a; select decode(null, 1, 'null = 1', 'null != 1'); +select decode(null::integer, 1, 'null = 1', 'null != 1'); + select decode(1, null, '1 = null', '1 != null'); select decode(null, null, 'null = null', 'null != null'); diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 353ec05bbb58..19a7bf24113e 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -58,6 +58,11 @@ select * from basictest; select testtext || testvarchar as concat, testnumeric + 42 as sum from basictest; +-- check that union/case/coalesce type resolution handles domains properly +select coalesce(4::domainint4, 7) is of (int4) as t; +select coalesce(4::domainint4, 7) is of (domainint4) as f; +select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t; + drop table basictest; drop domain domainvarchar restrict; drop domain domainnumeric restrict; @@ -176,7 +181,13 @@ create table defaulttest , col7 ddef4 DEFAULT 8000 , col8 ddef5 ); -insert into defaulttest default values; +insert into defaulttest(col4) values(0); -- fails, col5 defaults to null +alter table defaulttest alter column col5 drop default; +insert into defaulttest default values; -- succeeds, inserts domain default +-- We used to treat SET DEFAULT NULL as equivalent to DROP DEFAULT; wrong +alter table defaulttest alter column col5 set default null; +insert into defaulttest(col4) values(0); -- fails +alter table defaulttest alter column col5 drop default; insert into defaulttest default values; insert into defaulttest default values; @@ -372,6 +383,11 @@ insert into ddtest2 values(row(-1)); alter domain posint add constraint c1 check(value >= 0); drop table ddtest2; +create table ddtest2(f1 ddtest1[], distkey int) distributed by (distkey); +insert into ddtest2 values('{(-1)}'); +alter domain posint add constraint c1 check(value >= 0); +drop table ddtest2; + alter domain posint add constraint c1 check(value >= 0); create domain posint2 as posint check (value % 2 = 0); diff --git a/src/test/regress/sql/enum.sql b/src/test/regress/sql/enum.sql new file mode 100644 index 000000000000..cb13113da9ce --- /dev/null +++ b/src/test/regress/sql/enum.sql @@ -0,0 +1,146 @@ +-- +-- Enum tests +-- + +CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); + +-- +-- Did it create the right number of rows? +-- +SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype; + +-- +-- I/O functions +-- +SELECT 'red'::rainbow; +SELECT 'mauve'::rainbow; + +-- +-- Basic table creation, row selection +-- +CREATE TABLE enumtest (col rainbow); +INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green'); +COPY enumtest FROM stdin; +blue +purple +\. +SELECT * FROM enumtest; + +-- +-- Operators, no index +-- +SELECT * FROM enumtest WHERE col = 'orange'; +SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; +SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; +SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; +SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; +SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; + +-- +-- Cast to/from text +-- +SELECT 'red'::rainbow::text || 'hithere'; +SELECT 'red'::text::rainbow = 'red'::rainbow; + +-- +-- Aggregates +-- +SELECT min(col) FROM enumtest; +SELECT max(col) FROM enumtest; +SELECT max(col) FROM enumtest WHERE col < 'green'; + +-- +-- Index tests, force use of index +-- +SET enable_seqscan = off; +SET enable_bitmapscan = off; + +-- +-- Btree index / opclass with the various operators +-- +CREATE INDEX enumtest_btree ON enumtest USING btree (col); +SELECT * FROM enumtest WHERE col = 'orange'; +SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; +SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; +SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; +SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; +SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; +SELECT min(col) FROM enumtest; +SELECT max(col) FROM enumtest; +SELECT max(col) FROM enumtest WHERE col < 'green'; +DROP INDEX enumtest_btree; + +-- +-- End index tests +-- +RESET enable_seqscan; +RESET enable_bitmapscan; + +-- +-- Domains over enums +-- +CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue')); +SELECT 'red'::rgb; +SELECT 'purple'::rgb; +SELECT 'purple'::rainbow::rgb; +DROP DOMAIN rgb; + +-- +-- Arrays +-- +SELECT '{red,green,blue}'::rainbow[]; +SELECT ('{red,green,blue}'::rainbow[])[2]; +SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]); +SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]); +SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]); +SELECT 'red' = ALL ('{red,red}'::rainbow[]); + +-- +-- Support functions +-- +SELECT enum_first(NULL::rainbow); +SELECT enum_last('green'::rainbow); +SELECT enum_range(NULL::rainbow); +SELECT enum_range('orange'::rainbow, 'green'::rainbow); +SELECT enum_range(NULL, 'green'::rainbow); +SELECT enum_range('orange'::rainbow, NULL); +SELECT enum_range(NULL::rainbow, NULL); + +-- +-- User functions, can't test perl/python etc here since may not be compiled. +-- +CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$ +BEGIN +RETURN $1::text || 'omg'; +END +$$ LANGUAGE plpgsql; +SELECT echo_me('red'::rainbow); +-- +-- Concrete function should override generic one +-- +CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$ +BEGIN +RETURN $1::text || 'wtf'; +END +$$ LANGUAGE plpgsql; +SELECT echo_me('red'::rainbow); +-- +-- If we drop the original generic one, we don't have to qualify the type +-- anymore, since there's only one match +-- +DROP FUNCTION echo_me(anyenum); +SELECT echo_me('red'); +DROP FUNCTION echo_me(rainbow); + +-- +-- Cleanup +-- +DROP TABLE enumtest; +DROP TYPE rainbow; + +-- +-- Verify properly cleaned up +-- +SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow'; +SELECT * FROM pg_enum WHERE NOT EXISTS + (SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid); diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 0e5f78b6c792..bf08a3760428 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -760,12 +760,12 @@ FOREIGN KEY (x2) REFERENCES pktable(id1); ALTER TABLE fktable ADD CONSTRAINT fk_3_1 FOREIGN KEY (x3) REFERENCES pktable(id1); --- should succeed - --- int4 promotes to text, so this is allowed (though pretty durn debatable) +-- int4 does not promote to text ALTER TABLE fktable ADD CONSTRAINT fk_1_2 FOREIGN KEY (x1) REFERENCES pktable(id2); +-- should succeed + -- int4 promotes to real ALTER TABLE fktable ADD CONSTRAINT fk_1_3 FOREIGN KEY (x1) REFERENCES pktable(id3); @@ -921,3 +921,25 @@ SELECT * FROM tasks; DELETE FROM users WHERE id = 2; SELECT * FROM tasks; COMMIT; + +-- +-- Test self-referential FK with CASCADE (bug #6268) +-- +create temp table selfref ( + a int primary key, + b int, + foreign key (b) references selfref (a) + on update cascade on delete cascade +); + +insert into selfref (a, b) +values + (0, 0), + (1, 1); + +begin; + update selfref set a = 123 where a = 0; + select a, b from selfref; + update selfref set a = 456 where a = 123; + select a, b from selfref; +commit; diff --git a/src/test/regress/sql/gp_create_table.sql b/src/test/regress/sql/gp_create_table.sql index 5bddc2cab788..09363ba95cff 100644 --- a/src/test/regress/sql/gp_create_table.sql +++ b/src/test/regress/sql/gp_create_table.sql @@ -120,7 +120,7 @@ drop table if exists foo; drop table if exists bar; create table foo ( -c1 int,c2 int,c3 int,c4 int,c5 int,c6 int,c7 int,c8 int,c9 int,c10 int,c11 int,c12 int,c13 int,c14 int,c15 int,c16 int,c17 int,c18 int,c19 int,c20 int,c21 int,c22 int,c23 int,c24 int,c25 int,c26 int,c27 int,c28 int,c29 int,c30 int,c31 int,c32 int,c33 int,c34 int,c35 int,c36 int,c37 int,c38 int,c39 int,c40 int,c41 int,c42 int,c43 int,c44 int,c45 int,c46 int,c47 int,c48 int,c49 int,c50 int,c51 int,c52 int,c53 int,c54 int,c55 int,c56 int,c57 int,c58 int,c59 int,c60 int,c61 int,c62 int,c63 int,c64 int,c65 int,c66 int,c67 int,c68 int,c69 int,c70 int,c71 int,c72 int,c73 int,c74 int,c75 int,c76 int,c77 int,c78 int,c79 int,c80 int,c81 int,c82 int,c83 int,c84 int,c85 int,c86 int,c87 int,c88 int,c89 int,c90 int,c91 int,c92 int,c93 int,c94 int,c95 int,c96 int,c97 int,c98 int,c99 int,c100 int,c101 int,c102 int,c103 int,c104 int,c105 int,c106 int,c107 int,c108 int,c109 int,c110 int,c111 int,c112 int,c113 int,c114 int,c115 int,c116 int,c117 int,c118 int,c119 int,c120 int,c121 int,c122 int,c123 int,c124 int,c125 int,c126 int,c127 int,c128 int,c129 int,c130 int,c131 int,c132 int,c133 int,c134 int,c135 int,c136 int,c137 int,c138 int,c139 int,c140 int,c141 int,c142 int,c143 int,c144 int,c145 int,c146 int,c147 int,c148 int,c149 int,c150 int,c151 int,c152 int,c153 int,c154 int,c155 int,c156 int,c157 int,c158 int,c159 int,c160 int,c161 int,c162 int,c163 int,c164 int,c165 int,c166 int,c167 int,c168 int,c169 int,c170 int,c171 int,c172 int,c173 int,c174 int,c175 int,c176 int,c177 int,c178 int,c179 int,c180 int,c181 int,c182 int,c183 int,c184 int,c185 int,c186 int,c187 int,c188 int,c189 int,c190 int,c191 int,c192 int,c193 int,c194 int,c195 int,c196 int,c197 int,c198 int,c199 int,c200 int,c201 int,c202 int,c203 int,c204 int,c205 int,c206 int,c207 int,c208 int,c209 int,c210 int,c211 int,c212 int,c213 int,c214 int,c215 int,c216 int,c217 int,c218 int,c219 int,c220 int,c221 int,c222 int,c223 int,c224 int,c225 int,c226 int,c227 int,c228 int,c229 int,c230 int,c231 int,c232 int,c233 int,c234 int,c235 int,c236 int,c237 int,c238 int,c239 int,c240 int,c241 int,c242 int,c243 int,c244 int,c245 int,c246 int,c247 int,c248 int,c249 int,c250 int,c251 int,c252 int,c253 int,c254 int,c255 int,c256 int,c257 int,c258 int,c259 int,c260 int,c261 int,c262 int,c263 int,c264 int,c265 int,c266 int,c267 int,c268 int,c269 int,c270 int,c271 int,c272 int,c273 int,c274 int,c275 int,c276 int,c277 int,c278 int,c279 int,c280 int,c281 int,c282 int,c283 int,c284 int,c285 int,c286 int,c287 int,c288 int,c289 int,c290 int,c291 int,c292 int,c293 int,c294 int,c295 int,c296 int,c297 int,c298 int,c299 int,c300 int,c301 int,c302 int,c303 int,c304 int,c305 int,c306 int,c307 int,c308 int,c309 int,c310 int,c311 int,c312 int,c313 int,c314 int,c315 int,c316 int,c317 int,c318 int,c319 int,c320 int,c321 int,c322 int,c323 int,c324 int,c325 int,c326 int,c327 int,c328 int,c329 int,c330 int,c331 int,c332 int,c333 int,c334 int,c335 int,c336 int,c337 int,c338 int,c339 int,c340 int,c341 int,c342 int,c343 int,c344 int,c345 int,c346 int,c347 int,c348 int,c349 int,c350 int,c351 int,c352 int,c353 int,c354 int,c355 int,c356 int,c357 int,c358 int,c359 int,c360 int,c361 int,c362 int,c363 int,c364 int,c365 int,c366 int,c367 int,c368 int,c369 int,c370 int,c371 int,c372 int,c373 int,c374 int,c375 int,c376 int,c377 int,c378 int,c379 int,c380 int,c381 int,c382 int,c383 int,c384 int,c385 int,c386 int,c387 int,c388 int,c389 int,c390 int,c391 int,c392 int,c393 int,c394 int,c395 int,c396 int,c397 int,c398 int,c399 int,c400 int,c401 int,c402 int,c403 int,c404 int,c405 int,c406 int,c407 int,c408 int,c409 int,c410 int,c411 int,c412 int,c413 int,c414 int,c415 int,c416 int,c417 int,c418 int,c419 int,c420 int,c421 int,c422 int,c423 int,c424 int,c425 int,c426 int,c427 int,c428 int,c429 int,c430 int,c431 int,c432 int,c433 int,c434 int,c435 int,c436 int,c437 int,c438 int,c439 int,c440 int,c441 int,c442 int,c443 int,c444 int,c445 int,c446 int,c447 int,c448 int,c449 int,c450 int,c451 int,c452 int,c453 int,c454 int,c455 int,c456 int,c457 int,c458 int,c459 int,c460 int,c461 int,c462 int,c463 int,c464 int,c465 int,c466 int,c467 int,c468 int,c469 int,c470 int,c471 int,c472 int,c473 int,c474 int,c475 int,c476 int,c477 int,c478 int,c479 int,c480 int,c481 int,c482 int,c483 int,c484 int,c485 int,c486 int,c487 int,c488 int,c489 int,c490 int,c491 int,c492 int,c493 int,c494 int,c495 int,c496 int,c497 int,c498 int,c499 int,c500 int,c501 int,c502 int,c503 int,c504 int,c505 int,c506 int,c507 int,c508 int,c509 int,c510 int,c511 int,c512 int,c513 int,c514 int,c515 int,c516 int,c517 int,c518 int,c519 int,c520 int,c521 int,c522 int,c523 int,c524 int,c525 int,c526 int,c527 int,c528 int,c529 int,c530 int,c531 int,c532 int,c533 int,c534 int,c535 int,c536 int,c537 int,c538 int,c539 int,c540 int,c541 int,c542 int,c543 int,c544 int,c545 int,c546 int,c547 int,c548 int,c549 int,c550 int,c551 int,c552 int,c553 int,c554 int,c555 int,c556 int,c557 int,c558 int,c559 int,c560 int,c561 int,c562 int,c563 int,c564 int,c565 int,c566 int,c567 int,c568 int,c569 int,c570 int,c571 int,c572 int,c573 int,c574 int,c575 int,c576 int,c577 int,c578 int,c579 int,c580 int,c581 int,c582 int,c583 int,c584 int,c585 int,c586 int,c587 int,c588 int,c589 int,c590 int,c591 int,c592 int,c593 int,c594 int,c595 int,c596 int,c597 int,c598 int,c599 int,c600 int,c601 int,c602 int,c603 int,c604 int,c605 int,c606 int,c607 int,c608 int,c609 int,c610 int,c611 int,c612 int,c613 int,c614 int,c615 int,c616 int,c617 int,c618 int,c619 int,c620 int,c621 int,c622 int,c623 int,c624 int,c625 int,c626 int,c627 int,c628 int,c629 int,c630 int,c631 int,c632 int,c633 int,c634 int,c635 int,c636 int,c637 int,c638 int,c639 int,c640 int,c641 int,c642 int,c643 int,c644 int,c645 int,c646 int,c647 int,c648 int,c649 int,c650 int,c651 int,c652 int,c653 int,c654 int,c655 int,c656 int,c657 int,c658 int,c659 int,c660 int,c661 int,c662 int,c663 int,c664 int,c665 int,c666 int,c667 int,c668 int,c669 int,c670 int,c671 int,c672 int,c673 int,c674 int,c675 int,c676 int,c677 int,c678 int,c679 int,c680 int,c681 int,c682 int,c683 int,c684 int,c685 int,c686 int,c687 int,c688 int,c689 int,c690 int,c691 int,c692 int,c693 int,c694 int,c695 int,c696 int,c697 int,c698 int,c699 int,c700 int,c701 int,c702 int,c703 int,c704 int,c705 int,c706 int,c707 int,c708 int,c709 int,c710 int,c711 int,c712 int,c713 int,c714 int,c715 int,c716 int,c717 int,c718 int,c719 int,c720 int,c721 int,c722 int,c723 int,c724 int,c725 int,c726 int,c727 int,c728 int,c729 int,c730 int,c731 int,c732 int,c733 int,c734 int,c735 int,c736 int,c737 int,c738 int,c739 int,c740 int,c741 int,c742 int,c743 int,c744 int,c745 int,c746 int,c747 int,c748 int,c749 int,c750 int,c751 int,c752 int,c753 int,c754 int,c755 int,c756 int,c757 int,c758 int,c759 int,c760 int,c761 int,c762 int,c763 int,c764 int,c765 int,c766 int,c767 int,c768 int,c769 int,c770 int,c771 int,c772 int,c773 int,c774 int,c775 int,c776 int,c777 int,c778 int,c779 int,c780 int,c781 int,c782 int,c783 int,c784 int,c785 int,c786 int,c787 int,c788 int,c789 int,c790 int,c791 int,c792 int,c793 int,c794 int,c795 int,c796 int,c797 int,c798 int,c799 int,c800 int,c801 int,c802 int,c803 int,c804 int,c805 int,c806 int,c807 int,c808 int,c809 int,c810 int,c811 int,c812 int,c813 int,c814 int,c815 int,c816 int,c817 int,c818 int,c819 int,c820 int,c821 int,c822 int,c823 int,c824 int,c825 int,c826 int,c827 int,c828 int,c829 int,c830 int,c831 int,c832 int,c833 int,c834 int,c835 int,c836 int,c837 int,c838 int,c839 int,c840 int,c841 int,c842 int,c843 int,c844 int,c845 int,c846 int,c847 int,c848 int,c849 int,c850 int,c851 int,c852 int,c853 int,c854 int,c855 int,c856 int,c857 int,c858 int,c859 int,c860 int,c861 int,c862 int,c863 int,c864 int,c865 int,c866 int,c867 int,c868 int,c869 int,c870 int,c871 int,c872 int,c873 int,c874 int,c875 int,c876 int,c877 int,c878 int,c879 int,c880 int,c881 int,c882 int,c883 int,c884 int,c885 int,c886 int,c887 int,c888 int,c889 int,c890 int,c891 int,c892 int,c893 int,c894 int,c895 int,c896 int,c897 int,c898 int,c899 int,c900 int,c901 int,c902 int,c903 int,c904 int,c905 int,c906 int,c907 int,c908 int,c909 int,c910 int,c911 int,c912 int,c913 int,c914 int,c915 int,c916 int,c917 int,c918 int,c919 int,c920 int,c921 int,c922 int,c923 int,c924 int,c925 int,c926 int,c927 int,c928 int,c929 int,c930 int,c931 int,c932 int,c933 int,c934 int,c935 int,c936 int,c937 int,c938 int,c939 int,c940 int,c941 int,c942 int,c943 int,c944 int,c945 int,c946 int,c947 int,c948 int,c949 int,c950 int,c951 int,c952 int,c953 int,c954 int,c955 int,c956 int,c957 int,c958 int,c959 int,c960 int,c961 int,c962 int,c963 int,c964 int,c965 int,c966 int,c967 int,c968 int,c969 int,c970 int,c971 int,c972 int,c973 int,c974 int,c975 int,c976 int,c977 int,c978 int,c979 int,c980 int,c981 int,c982 int,c983 int,c984 int,c985 int,c986 int,c987 int,c988 int,c989 int,c990 int,c991 int,c992 int,c993 int,c994 int,c995 int,c996 int,c997 int,c998 int,c999 int,c1000 int,c1001 int,c1002 int,c1003 int,c1004 int,c1005 int,c1006 int,c1007 int,c1008 int,c1009 int,c1010 int,c1011 int,c1012 int,c1013 int,c1014 int,c1015 int,c1016 int,c1017 int,c1018 int,c1019 int,c1020 int,c1021 int,c1022 int,c1023 int,c1024 int,c1025 int,c1026 int,c1027 int,c1028 int,c1029 int,c1030 int,c1031 int,c1032 int,c1033 int,c1034 int,c1035 int,c1036 int,c1037 int,c1038 int,c1039 int,c1040 int,c1041 int,c1042 int,c1043 int,c1044 int,c1045 int,c1046 int,c1047 int,c1048 int,c1049 int,c1050 int,c1051 int,c1052 int,c1053 int,c1054 int,c1055 int,c1056 int,c1057 int,c1058 int,c1059 int,c1060 int,c1061 int,c1062 int,c1063 int,c1064 int,c1065 int,c1066 int,c1067 int,c1068 int,c1069 int,c1070 int,c1071 int,c1072 int,c1073 int,c1074 int,c1075 int,c1076 int,c1077 int,c1078 int,c1079 int,c1080 int,c1081 int,c1082 int,c1083 int,c1084 int,c1085 int,c1086 int,c1087 int,c1088 int,c1089 int,c1090 int,c1091 int,c1092 int,c1093 int,c1094 int,c1095 int,c1096 int,c1097 int,c1098 int,c1099 int,c1100 int,c1101 int,c1102 int,c1103 int,c1104 int,c1105 int,c1106 int,c1107 int,c1108 int,c1109 int,c1110 int,c1111 int,c1112 int,c1113 int,c1114 int,c1115 int,c1116 int,c1117 int,c1118 int,c1119 int,c1120 int,c1121 int,c1122 int,c1123 int,c1124 int,c1125 int,c1126 int,c1127 int,c1128 int,c1129 int,c1130 int,c1131 int,c1132 int,c1133 int,c1134 int,c1135 int,c1136 int,c1137 int,c1138 int,c1139 int,c1140 int,c1141 int,c1142 int,c1143 int,c1144 int,c1145 int,c1146 int,c1147 int,c1148 int,c1149 int,c1150 int,c1151 int,c1152 int,c1153 int,c1154 int,c1155 int,c1156 int,c1157 int,c1158 int,c1159 int,c1160 int,c1161 int,c1162 int,c1163 int,c1164 int,c1165 int,c1166 int,c1167 int,c1168 int,c1169 int,c1170 int,c1171 int,c1172 int,c1173 int,c1174 int,c1175 int,c1176 int,c1177 int,c1178 int,c1179 int,c1180 int,c1181 int,c1182 int,c1183 int,c1184 int,c1185 int,c1186 int,c1187 int,c1188 int,c1189 int,c1190 int,c1191 int,c1192 int,c1193 int,c1194 int,c1195 int,c1196 int,c1197 int,c1198 int,c1199 int,c1200 int,c1201 int,c1202 int,c1203 int,c1204 int,c1205 int,c1206 int,c1207 int,c1208 int,c1209 int,c1210 int,c1211 int,c1212 int,c1213 int,c1214 int,c1215 int,c1216 int,c1217 int,c1218 int,c1219 int,c1220 int,c1221 int,c1222 int,c1223 int,c1224 int,c1225 int,c1226 int,c1227 int,c1228 int,c1229 int,c1230 int,c1231 int,c1232 int,c1233 int,c1234 int,c1235 int,c1236 int,c1237 int,c1238 int,c1239 int,c1240 int,c1241 int,c1242 int,c1243 int,c1244 int,c1245 int,c1246 int,c1247 int,c1248 int,c1249 int,c1250 int,c1251 int,c1252 int,c1253 int,c1254 int,c1255 int,c1256 int,c1257 int,c1258 int,c1259 int,c1260 int,c1261 int,c1262 int,c1263 int,c1264 int,c1265 int,c1266 int,c1267 int,c1268 int,c1269 int,c1270 int,c1271 int,c1272 int,c1273 int,c1274 int,c1275 int,c1276 int,c1277 int,c1278 int,c1279 int,c1280 int,c1281 int,c1282 int,c1283 int,c1284 int,c1285 int,c1286 int,c1287 int,c1288 int,c1289 int,c1290 int,c1291 int,c1292 int,c1293 int,c1294 int,c1295 int,c1296 int,c1297 int,c1298 int,c1299 int,c1300 int,c1301 int,c1302 int,c1303 int,c1304 int,c1305 int,c1306 int,c1307 int,c1308 int,c1309 int,c1310 int,c1311 int,c1312 int,c1313 int,c1314 int,c1315 int,c1316 int,c1317 int,c1318 int,c1319 int,c1320 int,c1321 int,c1322 int,c1323 int,c1324 int,c1325 int,c1326 int,c1327 int,c1328 int,c1329 int,c1330 int,c1331 int,c1332 int,c1333 int,c1334 int,c1335 int,c1336 int,c1337 int,c1338 int,c1339 int,c1340 int,c1341 int,c1342 int,c1343 int,c1344 int,c1345 int,c1346 int,c1347 int,c1348 int,c1349 int,c1350 int,c1351 int,c1352 int,c1353 int,c1354 int,c1355 int,c1356 int,c1357 int,c1358 int,c1359 int,c1360 int,c1361 int,c1362 int,c1363 int,c1364 int,c1365 int,c1366 int,c1367 int,c1368 int,c1369 int,c1370 int,c1371 int,c1372 int,c1373 int,c1374 int,c1375 int,c1376 int,c1377 int,c1378 int,c1379 int,c1380 int,c1381 int,c1382 int,c1383 int,c1384 int,c1385 int,c1386 int,c1387 int,c1388 int,c1389 int,c1390 int,c1391 int,c1392 int,c1393 int,c1394 int,c1395 int,c1396 int,c1397 int,c1398 int,c1399 int,c1400 int,c1401 int,c1402 int,c1403 int,c1404 int,c1405 int,c1406 int,c1407 int,c1408 int,c1409 int,c1410 int,c1411 int,c1412 int,c1413 int,c1414 int,c1415 int,c1416 int,c1417 int,c1418 int,c1419 int,c1420 int,c1421 int,c1422 int,c1423 int,c1424 int,c1425 int,c1426 int,c1427 int,c1428 int,c1429 int,c1430 int,c1431 int,c1432 int,c1433 int,c1434 int,c1435 int,c1436 int,c1437 int,c1438 int,c1439 int,c1440 int,c1441 int,c1442 int,c1443 int,c1444 int,c1445 int,c1446 int,c1447 int,c1448 int,c1449 int,c1450 int,c1451 int,c1452 int,c1453 int,c1454 int,c1455 int,c1456 int,c1457 int,c1458 int,c1459 int,c1460 int,c1461 int,c1462 int,c1463 int,c1464 int,c1465 int,c1466 int,c1467 int,c1468 int,c1469 int,c1470 int,c1471 int,c1472 int,c1473 int,c1474 int,c1475 int,c1476 int,c1477 int,c1478 int,c1479 int,c1480 int,c1481 int,c1482 int,c1483 int,c1484 int,c1485 int,c1486 int,c1487 int,c1488 int,c1489 int,c1490 int,c1491 int,c1492 int,c1493 int,c1494 int,c1495 int,c1496 int,c1497 int,c1498 int,c1499 int,c1500 int,c1501 int,c1502 int,c1503 int,c1504 int,c1505 int,c1506 int,c1507 int,c1508 int,c1509 int,c1510 int,c1511 int,c1512 int,c1513 int,c1514 int,c1515 int,c1516 int,c1517 int,c1518 int,c1519 int,c1520 int,c1521 int,c1522 int,c1523 int,c1524 int,c1525 int,c1526 int,c1527 int,c1528 int,c1529 int,c1530 int,c1531 int,c1532 int,c1533 int,c1534 int,c1535 int,c1536 int,c1537 int,c1538 int,c1539 int,c1540 int,c1541 int,c1542 int,c1543 int,c1544 int,c1545 int,c1546 int,c1547 int,c1548 int,c1549 int,c1550 int,c1551 int,c1552 int,c1553 int,c1554 int,c1555 int,c1556 int,c1557 int,c1558 int,c1559 int,c1560 int,c1561 int,c1562 int,c1563 int,c1564 int,c1565 int,c1566 int,c1567 int,c1568 int,c1569 int,c1570 int,c1571 int,c1572 int,c1573 int,c1574 int,c1575 int,c1576 int,c1577 int,c1578 int,c1579 int,c1580 int,c1581 int,c1582 int,c1583 int,c1584 int,c1585 int,c1586 int,c1587 int,c1588 int,c1589 int,c1590 int,c1591 int,c1592 int,c1593 int,c1594 int,c1595 int,c1596 int,c1597 int,c1598 int,c1599 int,c1600 int, c1601 int +c1 int,c2 int,c3 int,c4 int,c5 int,c6 int,c7 int,c8 int,c9 int,c10 int,c11 int,c12 int,c13 int,c14 int,c15 int,c16 int,c17 int,c18 int,c19 int,c20 int,c21 int,c22 int,c23 int,c24 int,c25 int,c26 int,c27 int,c28 int,c29 int,c30 int,c31 int,c32 int,c33 int,c34 int,c35 int,c36 int,c37 int,c38 int,c39 int,c40 int,c41 int,c42 int,c43 int,c44 int,c45 int,c46 int,c47 int,c48 int,c49 int,c50 int,c51 int,c52 int,c53 int,c54 int,c55 int,c56 int,c57 int,c58 int,c59 int,c60 int,c61 int,c62 int,c63 int,c64 int,c65 int,c66 int,c67 int,c68 int,c69 int,c70 int,c71 int,c72 int,c73 int,c74 int,c75 int,c76 int,c77 int,c78 int,c79 int,c80 int,c81 int,c82 int,c83 int,c84 int,c85 int,c86 int,c87 int,c88 int,c89 int,c90 int,c91 int,c92 int,c93 int,c94 int,c95 int,c96 int,c97 int,c98 int,c99 int,c100 int,c101 int,c102 int,c103 int,c104 int,c105 int,c106 int,c107 int,c108 int,c109 int,c110 int,c111 int,c112 int,c113 int,c114 int,c115 int,c116 int,c117 int,c118 int,c119 int,c120 int,c121 int,c122 int,c123 int,c124 int,c125 int,c126 int,c127 int,c128 int,c129 int,c130 int,c131 int,c132 int,c133 int,c134 int,c135 int,c136 int,c137 int,c138 int,c139 int,c140 int,c141 int,c142 int,c143 int,c144 int,c145 int,c146 int,c147 int,c148 int,c149 int,c150 int,c151 int,c152 int,c153 int,c154 int,c155 int,c156 int,c157 int,c158 int,c159 int,c160 int,c161 int,c162 int,c163 int,c164 int,c165 int,c166 int,c167 int,c168 int,c169 int,c170 int,c171 int,c172 int,c173 int,c174 int,c175 int,c176 int,c177 int,c178 int,c179 int,c180 int,c181 int,c182 int,c183 int,c184 int,c185 int,c186 int,c187 int,c188 int,c189 int,c190 int,c191 int,c192 int,c193 int,c194 int,c195 int,c196 int,c197 int,c198 int,c199 int,c200 int,c201 int,c202 int,c203 int,c204 int,c205 int,c206 int,c207 int,c208 int,c209 int,c210 int,c211 int,c212 int,c213 int,c214 int,c215 int,c216 int,c217 int,c218 int,c219 int,c220 int,c221 int,c222 int,c223 int,c224 int,c225 int,c226 int,c227 int,c228 int,c229 int,c230 int,c231 int,c232 int,c233 int,c234 int,c235 int,c236 int,c237 int,c238 int,c239 int,c240 int,c241 int,c242 int,c243 int,c244 int,c245 int,c246 int,c247 int,c248 int,c249 int,c250 int,c251 int,c252 int,c253 int,c254 int,c255 int,c256 int,c257 int,c258 int,c259 int,c260 int,c261 int,c262 int,c263 int,c264 int,c265 int,c266 int,c267 int,c268 int,c269 int,c270 int,c271 int,c272 int,c273 int,c274 int,c275 int,c276 int,c277 int,c278 int,c279 int,c280 int,c281 int,c282 int,c283 int,c284 int,c285 int,c286 int,c287 int,c288 int,c289 int,c290 int,c291 int,c292 int,c293 int,c294 int,c295 int,c296 int,c297 int,c298 int,c299 int,c300 int,c301 int,c302 int,c303 int,c304 int,c305 int,c306 int,c307 int,c308 int,c309 int,c310 int,c311 int,c312 int,c313 int,c314 int,c315 int,c316 int,c317 int,c318 int,c319 int,c320 int,c321 int,c322 int,c323 int,c324 int,c325 int,c326 int,c327 int,c328 int,c329 int,c330 int,c331 int,c332 int,c333 int,c334 int,c335 int,c336 int,c337 int,c338 int,c339 int,c340 int,c341 int,c342 int,c343 int,c344 int,c345 int,c346 int,c347 int,c348 int,c349 int,c350 int,c351 int,c352 int,c353 int,c354 int,c355 int,c356 int,c357 int,c358 int,c359 int,c360 int,c361 int,c362 int,c363 int,c364 int,c365 int,c366 int,c367 int,c368 int,c369 int,c370 int,c371 int,c372 int,c373 int,c374 int,c375 int,c376 int,c377 int,c378 int,c379 int,c380 int,c381 int,c382 int,c383 int,c384 int,c385 int,c386 int,c387 int,c388 int,c389 int,c390 int,c391 int,c392 int,c393 int,c394 int,c395 int,c396 int,c397 int,c398 int,c399 int,c400 int,c401 int,c402 int,c403 int,c404 int,c405 int,c406 int,c407 int,c408 int,c409 int,c410 int,c411 int,c412 int,c413 int,c414 int,c415 int,c416 int,c417 int,c418 int,c419 int,c420 int,c421 int,c422 int,c423 int,c424 int,c425 int,c426 int,c427 int,c428 int,c429 int,c430 int,c431 int,c432 int,c433 int,c434 int,c435 int,c436 int,c437 int,c438 int,c439 int,c440 int,c441 int,c442 int,c443 int,c444 int,c445 int,c446 int,c447 int,c448 int,c449 int,c450 int,c451 int,c452 int,c453 int,c454 int,c455 int,c456 int,c457 int,c458 int,c459 int,c460 int,c461 int,c462 int,c463 int,c464 int,c465 int,c466 int,c467 int,c468 int,c469 int,c470 int,c471 int,c472 int,c473 int,c474 int,c475 int,c476 int,c477 int,c478 int,c479 int,c480 int,c481 int,c482 int,c483 int,c484 int,c485 int,c486 int,c487 int,c488 int,c489 int,c490 int,c491 int,c492 int,c493 int,c494 int,c495 int,c496 int,c497 int,c498 int,c499 int,c500 int,c501 int,c502 int,c503 int,c504 int,c505 int,c506 int,c507 int,c508 int,c509 int,c510 int,c511 int,c512 int,c513 int,c514 int,c515 int,c516 int,c517 int,c518 int,c519 int,c520 int,c521 int,c522 int,c523 int,c524 int,c525 int,c526 int,c527 int,c528 int,c529 int,c530 int,c531 int,c532 int,c533 int,c534 int,c535 int,c536 int,c537 int,c538 int,c539 int,c540 int,c541 int,c542 int,c543 int,c544 int,c545 int,c546 int,c547 int,c548 int,c549 int,c550 int,c551 int,c552 int,c553 int,c554 int,c555 int,c556 int,c557 int,c558 int,c559 int,c560 int,c561 int,c562 int,c563 int,c564 int,c565 int,c566 int,c567 int,c568 int,c569 int,c570 int,c571 int,c572 int,c573 int,c574 int,c575 int,c576 int,c577 int,c578 int,c579 int,c580 int,c581 int,c582 int,c583 int,c584 int,c585 int,c586 int,c587 int,c588 int,c589 int,c590 int,c591 int,c592 int,c593 int,c594 int,c595 int,c596 int,c597 int,c598 int,c599 int,c600 int,c601 int,c602 int,c603 int,c604 int,c605 int,c606 int,c607 int,c608 int,c609 int,c610 int,c611 int,c612 int,c613 int,c614 int,c615 int,c616 int,c617 int,c618 int,c619 int,c620 int,c621 int,c622 int,c623 int,c624 int,c625 int,c626 int,c627 int,c628 int,c629 int,c630 int,c631 int,c632 int,c633 int,c634 int,c635 int,c636 int,c637 int,c638 int,c639 int,c640 int,c641 int,c642 int,c643 int,c644 int,c645 int,c646 int,c647 int,c648 int,c649 int,c650 int,c651 int,c652 int,c653 int,c654 int,c655 int,c656 int,c657 int,c658 int,c659 int,c660 int,c661 int,c662 int,c663 int,c664 int,c665 int,c666 int,c667 int,c668 int,c669 int,c670 int,c671 int,c672 int,c673 int,c674 int,c675 int,c676 int,c677 int,c678 int,c679 int,c680 int,c681 int,c682 int,c683 int,c684 int,c685 int,c686 int,c687 int,c688 int,c689 int,c690 int,c691 int,c692 int,c693 int,c694 int,c695 int,c696 int,c697 int,c698 int,c699 int,c700 int,c701 int,c702 int,c703 int,c704 int,c705 int,c706 int,c707 int,c708 int,c709 int,c710 int,c711 int,c712 int,c713 int,c714 int,c715 int,c716 int,c717 int,c718 int,c719 int,c720 int,c721 int,c722 int,c723 int,c724 int,c725 int,c726 int,c727 int,c728 int,c729 int,c730 int,c731 int,c732 int,c733 int,c734 int,c735 int,c736 int,c737 int,c738 int,c739 int,c740 int,c741 int,c742 int,c743 int,c744 int,c745 int,c746 int,c747 int,c748 int,c749 int,c750 int,c751 int,c752 int,c753 int,c754 int,c755 int,c756 int,c757 int,c758 int,c759 int,c760 int,c761 int,c762 int,c763 int,c764 int,c765 int,c766 int,c767 int,c768 int,c769 int,c770 int,c771 int,c772 int,c773 int,c774 int,c775 int,c776 int,c777 int,c778 int,c779 int,c780 int,c781 int,c782 int,c783 int,c784 int,c785 int,c786 int,c787 int,c788 int,c789 int,c790 int,c791 int,c792 int,c793 int,c794 int,c795 int,c796 int,c797 int,c798 int,c799 int,c800 int,c801 int,c802 int,c803 int,c804 int,c805 int,c806 int,c807 int,c808 int,c809 int,c810 int,c811 int,c812 int,c813 int,c814 int,c815 int,c816 int,c817 int,c818 int,c819 int,c820 int,c821 int,c822 int,c823 int,c824 int,c825 int,c826 int,c827 int,c828 int,c829 int,c830 int,c831 int,c832 int,c833 int,c834 int,c835 int,c836 int,c837 int,c838 int,c839 int,c840 int,c841 int,c842 int,c843 int,c844 int,c845 int,c846 int,c847 int,c848 int,c849 int,c850 int,c851 int,c852 int,c853 int,c854 int,c855 int,c856 int,c857 int,c858 int,c859 int,c860 int,c861 int,c862 int,c863 int,c864 int,c865 int,c866 int,c867 int,c868 int,c869 int,c870 int,c871 int,c872 int,c873 int,c874 int,c875 int,c876 int,c877 int,c878 int,c879 int,c880 int,c881 int,c882 int,c883 int,c884 int,c885 int,c886 int,c887 int,c888 int,c889 int,c890 int,c891 int,c892 int,c893 int,c894 int,c895 int,c896 int,c897 int,c898 int,c899 int,c900 int,c901 int,c902 int,c903 int,c904 int,c905 int,c906 int,c907 int,c908 int,c909 int,c910 int,c911 int,c912 int,c913 int,c914 int,c915 int,c916 int,c917 int,c918 int,c919 int,c920 int,c921 int,c922 int,c923 int,c924 int,c925 int,c926 int,c927 int,c928 int,c929 int,c930 int,c931 int,c932 int,c933 int,c934 int,c935 int,c936 int,c937 int,c938 int,c939 int,c940 int,c941 int,c942 int,c943 int,c944 int,c945 int,c946 int,c947 int,c948 int,c949 int,c950 int,c951 int,c952 int,c953 int,c954 int,c955 int,c956 int,c957 int,c958 int,c959 int,c960 int,c961 int,c962 int,c963 int,c964 int,c965 int,c966 int,c967 int,c968 int,c969 int,c970 int,c971 int,c972 int,c973 int,c974 int,c975 int,c976 int,c977 int,c978 int,c979 int,c980 int,c981 int,c982 int,c983 int,c984 int,c985 int,c986 int,c987 int,c988 int,c989 int,c990 int,c991 int,c992 int,c993 int,c994 int,c995 int,c996 int,c997 int,c998 int,c999 int,c1000 int,c1001 int,c1002 int,c1003 int,c1004 int,c1005 int,c1006 int,c1007 int,c1008 int,c1009 int,c1010 int,c1011 int,c1012 int,c1013 int,c1014 int,c1015 int,c1016 int,c1017 int,c1018 int,c1019 int,c1020 int,c1021 int,c1022 int,c1023 int,c1024 int,c1025 int,c1026 int,c1027 int,c1028 int,c1029 int,c1030 int,c1031 int,c1032 int,c1033 int,c1034 int,c1035 int,c1036 int,c1037 int,c1038 int,c1039 int,c1040 int,c1041 int,c1042 int,c1043 int,c1044 int,c1045 int,c1046 int,c1047 int,c1048 int,c1049 int,c1050 int,c1051 int,c1052 int,c1053 int,c1054 int,c1055 int,c1056 int,c1057 int,c1058 int,c1059 int,c1060 int,c1061 int,c1062 int,c1063 int,c1064 int,c1065 int,c1066 int,c1067 int,c1068 int,c1069 int,c1070 int,c1071 int,c1072 int,c1073 int,c1074 int,c1075 int,c1076 int,c1077 int,c1078 int,c1079 int,c1080 int,c1081 int,c1082 int,c1083 int,c1084 int,c1085 int,c1086 int,c1087 int,c1088 int,c1089 int,c1090 int,c1091 int,c1092 int,c1093 int,c1094 int,c1095 int,c1096 int,c1097 int,c1098 int,c1099 int,c1100 int,c1101 int,c1102 int,c1103 int,c1104 int,c1105 int,c1106 int,c1107 int,c1108 int,c1109 int,c1110 int,c1111 int,c1112 int,c1113 int,c1114 int,c1115 int,c1116 int,c1117 int,c1118 int,c1119 int,c1120 int,c1121 int,c1122 int,c1123 int,c1124 int,c1125 int,c1126 int,c1127 int,c1128 int,c1129 int,c1130 int,c1131 int,c1132 int,c1133 int,c1134 int,c1135 int,c1136 int,c1137 int,c1138 int,c1139 int,c1140 int,c1141 int,c1142 int,c1143 int,c1144 int,c1145 int,c1146 int,c1147 int,c1148 int,c1149 int,c1150 int,c1151 int,c1152 int,c1153 int,c1154 int,c1155 int,c1156 int,c1157 int,c1158 int,c1159 int,c1160 int,c1161 int,c1162 int,c1163 int,c1164 int,c1165 int,c1166 int,c1167 int,c1168 int,c1169 int,c1170 int,c1171 int,c1172 int,c1173 int,c1174 int,c1175 int,c1176 int,c1177 int,c1178 int,c1179 int,c1180 int,c1181 int,c1182 int,c1183 int,c1184 int,c1185 int,c1186 int,c1187 int,c1188 int,c1189 int,c1190 int,c1191 int,c1192 int,c1193 int,c1194 int,c1195 int,c1196 int,c1197 int,c1198 int,c1199 int,c1200 int,c1201 int,c1202 int,c1203 int,c1204 int,c1205 int,c1206 int,c1207 int,c1208 int,c1209 int,c1210 int,c1211 int,c1212 int,c1213 int,c1214 int,c1215 int,c1216 int,c1217 int,c1218 int,c1219 int,c1220 int,c1221 int,c1222 int,c1223 int,c1224 int,c1225 int,c1226 int,c1227 int,c1228 int,c1229 int,c1230 int,c1231 int,c1232 int,c1233 int,c1234 int,c1235 int,c1236 int,c1237 int,c1238 int,c1239 int,c1240 int,c1241 int,c1242 int,c1243 int,c1244 int,c1245 int,c1246 int,c1247 int,c1248 int,c1249 int,c1250 int,c1251 int,c1252 int,c1253 int,c1254 int,c1255 int,c1256 int,c1257 int,c1258 int,c1259 int,c1260 int,c1261 int,c1262 int,c1263 int,c1264 int,c1265 int,c1266 int,c1267 int,c1268 int,c1269 int,c1270 int,c1271 int,c1272 int,c1273 int,c1274 int,c1275 int,c1276 int,c1277 int,c1278 int,c1279 int,c1280 int,c1281 int,c1282 int,c1283 int,c1284 int,c1285 int,c1286 int,c1287 int,c1288 int,c1289 int,c1290 int,c1291 int,c1292 int,c1293 int,c1294 int,c1295 int,c1296 int,c1297 int,c1298 int,c1299 int,c1300 int,c1301 int,c1302 int,c1303 int,c1304 int,c1305 int,c1306 int,c1307 int,c1308 int,c1309 int,c1310 int,c1311 int,c1312 int,c1313 int,c1314 int,c1315 int,c1316 int,c1317 int,c1318 int,c1319 int,c1320 int,c1321 int,c1322 int,c1323 int,c1324 int,c1325 int,c1326 int,c1327 int,c1328 int,c1329 int,c1330 int,c1331 int,c1332 int,c1333 int,c1334 int,c1335 int,c1336 int,c1337 int,c1338 int,c1339 int,c1340 int,c1341 int,c1342 int,c1343 int,c1344 int,c1345 int,c1346 int,c1347 int,c1348 int,c1349 int,c1350 int,c1351 int,c1352 int,c1353 int,c1354 int,c1355 int,c1356 int,c1357 int,c1358 int,c1359 int,c1360 int,c1361 int,c1362 int,c1363 int,c1364 int,c1365 int,c1366 int,c1367 int,c1368 int,c1369 int,c1370 int,c1371 int,c1372 int,c1373 int,c1374 int,c1375 int,c1376 int,c1377 int,c1378 int,c1379 int,c1380 int,c1381 int,c1382 int,c1383 int,c1384 int,c1385 int,c1386 int,c1387 int,c1388 int,c1389 int,c1390 int,c1391 int,c1392 int,c1393 int,c1394 int,c1395 int,c1396 int,c1397 int,c1398 int,c1399 int,c1400 int,c1401 int,c1402 int,c1403 int,c1404 int,c1405 int,c1406 int,c1407 int,c1408 int,c1409 int,c1410 int,c1411 int,c1412 int,c1413 int,c1414 int,c1415 int,c1416 int,c1417 int,c1418 int,c1419 int,c1420 int,c1421 int,c1422 int,c1423 int,c1424 int,c1425 int,c1426 int,c1427 int,c1428 int,c1429 int,c1430 int,c1431 int,c1432 int,c1433 int,c1434 int,c1435 int,c1436 int,c1437 int,c1438 int,c1439 int,c1440 int,c1441 int,c1442 int,c1443 int,c1444 int,c1445 int,c1446 int,c1447 int,c1448 int,c1449 int,c1450 int,c1451 int,c1452 int,c1453 int,c1454 int,c1455 int,c1456 int,c1457 int,c1458 int,c1459 int,c1460 int,c1461 int,c1462 int,c1463 int,c1464 int,c1465 int,c1466 int,c1467 int,c1468 int,c1469 int,c1470 int,c1471 int,c1472 int,c1473 int,c1474 int,c1475 int,c1476 int,c1477 int,c1478 int,c1479 int,c1480 int,c1481 int,c1482 int,c1483 int,c1484 int,c1485 int,c1486 int,c1487 int,c1488 int,c1489 int,c1490 int,c1491 int,c1492 int,c1493 int,c1494 int,c1495 int,c1496 int,c1497 int,c1498 int,c1499 int,c1500 int,c1501 int,c1502 int,c1503 int,c1504 int,c1505 int,c1506 int,c1507 int,c1508 int,c1509 int,c1510 int,c1511 int,c1512 int,c1513 int,c1514 int,c1515 int,c1516 int,c1517 int,c1518 int,c1519 int,c1520 int,c1521 int,c1522 int,c1523 int,c1524 int,c1525 int,c1526 int,c1527 int,c1528 int,c1529 int,c1530 int,c1531 int,c1532 int,c1533 int,c1534 int,c1535 int,c1536 int,c1537 int,c1538 int,c1539 int,c1540 int,c1541 int,c1542 int,c1543 int,c1544 int,c1545 int,c1546 int,c1547 int,c1548 int,c1549 int,c1550 int,c1551 int,c1552 int,c1553 int,c1554 int,c1555 int,c1556 int,c1557 int,c1558 int,c1559 int,c1560 int,c1561 int,c1562 int,c1563 int,c1564 int,c1565 int,c1566 int,c1567 int,c1568 int,c1569 int,c1570 int,c1571 int,c1572 int,c1573 int,c1574 int,c1575 int,c1576 int,c1577 int,c1578 int,c1579 int,c1580 int,c1581 int,c1582 int,c1583 int,c1584 int,c1585 int,c1586 int,c1587 int,c1588 int,c1589 int,c1590 int,c1591 int,c1592 int,c1593 int,c1594 int,c1595 int,c1596 int,c1597 int,c1598 int,c1599 int,c1600 int,c1601 int ) distributed by ( c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,c30,c31,c32,c33,c34,c35,c36,c37,c38,c39,c40,c41,c42,c43,c44,c45,c46,c47,c48,c49,c50,c51,c52,c53,c54,c55,c56,c57,c58,c59,c60,c61,c62,c63,c64,c65,c66,c67,c68,c69,c70,c71,c72,c73,c74,c75,c76,c77,c78,c79,c80,c81,c82,c83,c84,c85,c86,c87,c88,c89,c90,c91,c92,c93,c94,c95,c96,c97,c98,c99,c100,c101,c102,c103,c104,c105,c106,c107,c108,c109,c110,c111,c112,c113,c114,c115,c116,c117,c118,c119,c120,c121,c122,c123,c124,c125,c126,c127,c128,c129,c130,c131,c132,c133,c134,c135,c136,c137,c138,c139,c140,c141,c142,c143,c144,c145,c146,c147,c148,c149,c150,c151,c152,c153,c154,c155,c156,c157,c158,c159,c160,c161,c162,c163,c164,c165,c166,c167,c168,c169,c170,c171,c172,c173,c174,c175,c176,c177,c178,c179,c180,c181,c182,c183,c184,c185,c186,c187,c188,c189,c190,c191,c192,c193,c194,c195,c196,c197,c198,c199,c200,c201,c202,c203,c204,c205,c206,c207,c208,c209,c210,c211,c212,c213,c214,c215,c216,c217,c218,c219,c220,c221,c222,c223,c224,c225,c226,c227,c228,c229,c230,c231,c232,c233,c234,c235,c236,c237,c238,c239,c240,c241,c242,c243,c244,c245,c246,c247,c248,c249,c250,c251,c252,c253,c254,c255,c256,c257,c258,c259,c260,c261,c262,c263,c264,c265,c266,c267,c268,c269,c270,c271,c272,c273,c274,c275,c276,c277,c278,c279,c280,c281,c282,c283,c284,c285,c286,c287,c288,c289,c290,c291,c292,c293,c294,c295,c296,c297,c298,c299,c300,c301,c302,c303,c304,c305,c306,c307,c308,c309,c310,c311,c312,c313,c314,c315,c316,c317,c318,c319,c320,c321,c322,c323,c324,c325,c326,c327,c328,c329,c330,c331,c332,c333,c334,c335,c336,c337,c338,c339,c340,c341,c342,c343,c344,c345,c346,c347,c348,c349,c350,c351,c352,c353,c354,c355,c356,c357,c358,c359,c360,c361,c362,c363,c364,c365,c366,c367,c368,c369,c370,c371,c372,c373,c374,c375,c376,c377,c378,c379,c380,c381,c382,c383,c384,c385,c386,c387,c388,c389,c390,c391,c392,c393,c394,c395,c396,c397,c398,c399,c400,c401,c402,c403,c404,c405,c406,c407,c408,c409,c410,c411,c412,c413,c414,c415,c416,c417,c418,c419,c420,c421,c422,c423,c424,c425,c426,c427,c428,c429,c430,c431,c432,c433,c434,c435,c436,c437,c438,c439,c440,c441,c442,c443,c444,c445,c446,c447,c448,c449,c450,c451,c452,c453,c454,c455,c456,c457,c458,c459,c460,c461,c462,c463,c464,c465,c466,c467,c468,c469,c470,c471,c472,c473,c474,c475,c476,c477,c478,c479,c480,c481,c482,c483,c484,c485,c486,c487,c488,c489,c490,c491,c492,c493,c494,c495,c496,c497,c498,c499,c500,c501,c502,c503,c504,c505,c506,c507,c508,c509,c510,c511,c512,c513,c514,c515,c516,c517,c518,c519,c520,c521,c522,c523,c524,c525,c526,c527,c528,c529,c530,c531,c532,c533,c534,c535,c536,c537,c538,c539,c540,c541,c542,c543,c544,c545,c546,c547,c548,c549,c550,c551,c552,c553,c554,c555,c556,c557,c558,c559,c560,c561,c562,c563,c564,c565,c566,c567,c568,c569,c570,c571,c572,c573,c574,c575,c576,c577,c578,c579,c580,c581,c582,c583,c584,c585,c586,c587,c588,c589,c590,c591,c592,c593,c594,c595,c596,c597,c598,c599,c600,c601,c602,c603,c604,c605,c606,c607,c608,c609,c610,c611,c612,c613,c614,c615,c616,c617,c618,c619,c620,c621,c622,c623,c624,c625,c626,c627,c628,c629,c630,c631,c632,c633,c634,c635,c636,c637,c638,c639,c640,c641,c642,c643,c644,c645,c646,c647,c648,c649,c650,c651,c652,c653,c654,c655,c656,c657,c658,c659,c660,c661,c662,c663,c664,c665,c666,c667,c668,c669,c670,c671,c672,c673,c674,c675,c676,c677,c678,c679,c680,c681,c682,c683,c684,c685,c686,c687,c688,c689,c690,c691,c692,c693,c694,c695,c696,c697,c698,c699,c700,c701,c702,c703,c704,c705,c706,c707,c708,c709,c710,c711,c712,c713,c714,c715,c716,c717,c718,c719,c720,c721,c722,c723,c724,c725,c726,c727,c728,c729,c730,c731,c732,c733,c734,c735,c736,c737,c738,c739,c740,c741,c742,c743,c744,c745,c746,c747,c748,c749,c750,c751,c752,c753,c754,c755,c756,c757,c758,c759,c760,c761,c762,c763,c764,c765,c766,c767,c768,c769,c770,c771,c772,c773,c774,c775,c776,c777,c778,c779,c780,c781,c782,c783,c784,c785,c786,c787,c788,c789,c790,c791,c792,c793,c794,c795,c796,c797,c798,c799,c800,c801,c802,c803,c804,c805,c806,c807,c808,c809,c810,c811,c812,c813,c814,c815,c816,c817,c818,c819,c820,c821,c822,c823,c824,c825,c826,c827,c828,c829,c830,c831,c832,c833,c834,c835,c836,c837,c838,c839,c840,c841,c842,c843,c844,c845,c846,c847,c848,c849,c850,c851,c852,c853,c854,c855,c856,c857,c858,c859,c860,c861,c862,c863,c864,c865,c866,c867,c868,c869,c870,c871,c872,c873,c874,c875,c876,c877,c878,c879,c880,c881,c882,c883,c884,c885,c886,c887,c888,c889,c890,c891,c892,c893,c894,c895,c896,c897,c898,c899,c900,c901,c902,c903,c904,c905,c906,c907,c908,c909,c910,c911,c912,c913,c914,c915,c916,c917,c918,c919,c920,c921,c922,c923,c924,c925,c926,c927,c928,c929,c930,c931,c932,c933,c934,c935,c936,c937,c938,c939,c940,c941,c942,c943,c944,c945,c946,c947,c948,c949,c950,c951,c952,c953,c954,c955,c956,c957,c958,c959,c960,c961,c962,c963,c964,c965,c966,c967,c968,c969,c970,c971,c972,c973,c974,c975,c976,c977,c978,c979,c980,c981,c982,c983,c984,c985,c986,c987,c988,c989,c990,c991,c992,c993,c994,c995,c996,c997,c998,c999,c1000,c1001,c1002,c1003,c1004,c1005,c1006,c1007,c1008,c1009,c1010,c1011,c1012,c1013,c1014,c1015,c1016,c1017,c1018,c1019,c1020,c1021,c1022,c1023,c1024,c1025,c1026,c1027,c1028,c1029,c1030,c1031,c1032,c1033,c1034,c1035,c1036,c1037,c1038,c1039,c1040,c1041,c1042,c1043,c1044,c1045,c1046,c1047,c1048,c1049,c1050,c1051,c1052,c1053,c1054,c1055,c1056,c1057,c1058,c1059,c1060,c1061,c1062,c1063,c1064,c1065,c1066,c1067,c1068,c1069,c1070,c1071,c1072,c1073,c1074,c1075,c1076,c1077,c1078,c1079,c1080,c1081,c1082,c1083,c1084,c1085,c1086,c1087,c1088,c1089,c1090,c1091,c1092,c1093,c1094,c1095,c1096,c1097,c1098,c1099,c1100,c1101,c1102,c1103,c1104,c1105,c1106,c1107,c1108,c1109,c1110,c1111,c1112,c1113,c1114,c1115,c1116,c1117,c1118,c1119,c1120,c1121,c1122,c1123,c1124,c1125,c1126,c1127,c1128,c1129,c1130,c1131,c1132,c1133,c1134,c1135,c1136,c1137,c1138,c1139,c1140,c1141,c1142,c1143,c1144,c1145,c1146,c1147,c1148,c1149,c1150,c1151,c1152,c1153,c1154,c1155,c1156,c1157,c1158,c1159,c1160,c1161,c1162,c1163,c1164,c1165,c1166,c1167,c1168,c1169,c1170,c1171,c1172,c1173,c1174,c1175,c1176,c1177,c1178,c1179,c1180,c1181,c1182,c1183,c1184,c1185,c1186,c1187,c1188,c1189,c1190,c1191,c1192,c1193,c1194,c1195,c1196,c1197,c1198,c1199,c1200,c1201,c1202,c1203,c1204,c1205,c1206,c1207,c1208,c1209,c1210,c1211,c1212,c1213,c1214,c1215,c1216,c1217,c1218,c1219,c1220,c1221,c1222,c1223,c1224,c1225,c1226,c1227,c1228,c1229,c1230,c1231,c1232,c1233,c1234,c1235,c1236,c1237,c1238,c1239,c1240,c1241,c1242,c1243,c1244,c1245,c1246,c1247,c1248,c1249,c1250,c1251,c1252,c1253,c1254,c1255,c1256,c1257,c1258,c1259,c1260,c1261,c1262,c1263,c1264,c1265,c1266,c1267,c1268,c1269,c1270,c1271,c1272,c1273,c1274,c1275,c1276,c1277,c1278,c1279,c1280,c1281,c1282,c1283,c1284,c1285,c1286,c1287,c1288,c1289,c1290,c1291,c1292,c1293,c1294,c1295,c1296,c1297,c1298,c1299,c1300,c1301,c1302,c1303,c1304,c1305,c1306,c1307,c1308,c1309,c1310,c1311,c1312,c1313,c1314,c1315,c1316,c1317,c1318,c1319,c1320,c1321,c1322,c1323,c1324,c1325,c1326,c1327,c1328,c1329,c1330,c1331,c1332,c1333,c1334,c1335,c1336,c1337,c1338,c1339,c1340,c1341,c1342,c1343,c1344,c1345,c1346,c1347,c1348,c1349,c1350,c1351,c1352,c1353,c1354,c1355,c1356,c1357,c1358,c1359,c1360,c1361,c1362,c1363,c1364,c1365,c1366,c1367,c1368,c1369,c1370,c1371,c1372,c1373,c1374,c1375,c1376,c1377,c1378,c1379,c1380,c1381,c1382,c1383,c1384,c1385,c1386,c1387,c1388,c1389,c1390,c1391,c1392,c1393,c1394,c1395,c1396,c1397,c1398,c1399,c1400,c1401,c1402,c1403,c1404,c1405,c1406,c1407,c1408,c1409,c1410,c1411,c1412,c1413,c1414,c1415,c1416,c1417,c1418,c1419,c1420,c1421,c1422,c1423,c1424,c1425,c1426,c1427,c1428,c1429,c1430,c1431,c1432,c1433,c1434,c1435,c1436,c1437,c1438,c1439,c1440,c1441,c1442,c1443,c1444,c1445,c1446,c1447,c1448,c1449,c1450,c1451,c1452,c1453,c1454,c1455,c1456,c1457,c1458,c1459,c1460,c1461,c1462,c1463,c1464,c1465,c1466,c1467,c1468,c1469,c1470,c1471,c1472,c1473,c1474,c1475,c1476,c1477,c1478,c1479,c1480,c1481,c1482,c1483,c1484,c1485,c1486,c1487,c1488,c1489,c1490,c1491,c1492,c1493,c1494,c1495,c1496,c1497,c1498,c1499,c1500,c1501,c1502,c1503,c1504,c1505,c1506,c1507,c1508,c1509,c1510,c1511,c1512,c1513,c1514,c1515,c1516,c1517,c1518,c1519,c1520,c1521,c1522,c1523,c1524,c1525,c1526,c1527,c1528,c1529,c1530,c1531,c1532,c1533,c1534,c1535,c1536,c1537,c1538,c1539,c1540,c1541,c1542,c1543,c1544,c1545,c1546,c1547,c1548,c1549,c1550,c1551,c1552,c1553,c1554,c1555,c1556,c1557,c1558,c1559,c1560,c1561,c1562,c1563,c1564,c1565,c1566,c1567,c1568,c1569,c1570,c1571,c1572,c1573,c1574,c1575,c1576,c1577,c1578,c1579,c1580,c1581,c1582,c1583,c1584,c1585,c1586,c1587,c1588,c1589,c1590,c1591,c1592,c1593,c1594,c1595,c1596,c1597,c1598,c1599,c1600,c1601 diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql index 16380599bd60..d6515818e4f5 100644 --- a/src/test/regress/sql/guc.sql +++ b/src/test/regress/sql/guc.sql @@ -99,6 +99,26 @@ SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; +-- SET LOCAL persists through RELEASE (which was not true in 8.0-8.2) +BEGIN; +SHOW vacuum_cost_delay; +SHOW datestyle; +SELECT '2006-08-13 12:34:56'::timestamptz; +SAVEPOINT sp; +SET LOCAL vacuum_cost_delay TO 300; +SHOW vacuum_cost_delay; +SET LOCAL datestyle = 'Postgres, MDY'; +SHOW datestyle; +SELECT '2006-08-13 12:34:56'::timestamptz; +RELEASE SAVEPOINT sp; +SHOW vacuum_cost_delay; +SHOW datestyle; +SELECT '2006-08-13 12:34:56'::timestamptz; +ROLLBACK; +SHOW vacuum_cost_delay; +SHOW datestyle; +SELECT '2006-08-13 12:34:56'::timestamptz; + -- SET followed by SET LOCAL BEGIN; SET vacuum_cost_delay TO 400; @@ -124,6 +144,121 @@ RESET datestyle; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; +-- +-- Test DISCARD TEMP +-- +CREATE TEMP TABLE reset_test ( data text ) ON COMMIT DELETE ROWS; +SELECT relname FROM pg_class WHERE relname = 'reset_test'; +DISCARD TEMP; +SELECT relname FROM pg_class WHERE relname = 'reset_test'; + +-- +-- Test DISCARD ALL +-- + +-- do changes +DECLARE foo CURSOR WITH HOLD FOR SELECT 1; +PREPARE foo AS SELECT 1; +LISTEN foo_event; +SET vacuum_cost_delay = 13; +CREATE TEMP TABLE tmp_foo (data text) ON COMMIT DELETE ROWS; +CREATE ROLE temp_reset_user; +SET SESSION AUTHORIZATION temp_reset_user; +-- look changes +SELECT relname FROM pg_listener; +SELECT name FROM pg_prepared_statements; +SELECT name FROM pg_cursors; +SHOW vacuum_cost_delay; +SELECT relname from pg_class where relname = 'tmp_foo'; +SELECT current_user = 'temp_reset_user'; +-- discard everything +DISCARD ALL; +-- look again +SELECT relname FROM pg_listener; +SELECT name FROM pg_prepared_statements; +SELECT name FROM pg_cursors; +SHOW vacuum_cost_delay; +SELECT relname from pg_class where relname = 'tmp_foo'; +SELECT current_user = 'temp_reset_user'; +DROP ROLE temp_reset_user; + +-- +-- Tests for function-local GUC settings +-- + +set regex_flavor = advanced; + +create function report_guc(text) returns text as +$$ select current_setting($1) $$ language sql +set regex_flavor = basic; + +select report_guc('regex_flavor'), current_setting('regex_flavor'); + +-- this should draw only a warning +alter function report_guc(text) set search_path = no_such_schema; + +-- with error occurring here +select report_guc('regex_flavor'), current_setting('regex_flavor'); + +alter function report_guc(text) reset search_path set regex_flavor = extended; + +select report_guc('regex_flavor'), current_setting('regex_flavor'); + +alter function report_guc(text) reset all; + +select report_guc('regex_flavor'), current_setting('regex_flavor'); + +-- SET LOCAL is restricted by a function SET option +create or replace function myfunc(int) returns text as $$ +begin + set local regex_flavor = extended; + return current_setting('regex_flavor'); +end $$ +language plpgsql +set regex_flavor = basic; + +select myfunc(0), current_setting('regex_flavor'); + +alter function myfunc(int) reset all; + +select myfunc(0), current_setting('regex_flavor'); + +set regex_flavor = advanced; + +-- but SET isn't +create or replace function myfunc(int) returns text as $$ +begin + set regex_flavor = extended; + return current_setting('regex_flavor'); +end $$ +language plpgsql +set regex_flavor = basic; + +select myfunc(0), current_setting('regex_flavor'); + +-- In GPDB, the plan looks somewhat different from what you get on +-- PostgreSQL, so that the current_setting() in previous query is +-- evaluated before myfunc(0), and therefore it shows 'advanced'. +-- Query again to show that the myfunc(0) call actually changed +-- the setting. +select current_setting('regex_flavor'); + +set regex_flavor = advanced; + +-- it should roll back on error, though +create or replace function myfunc(int) returns text as $$ +begin + set regex_flavor = extended; + perform 1/$1; + return current_setting('regex_flavor'); +end $$ +language plpgsql +set regex_flavor = basic; + +select myfunc(0); +select current_setting('regex_flavor'); +select myfunc(1), current_setting('regex_flavor'); + SELECT min_val, max_val FROM pg_settings WHERE name = 'gp_resqueue_priority_cpucores_per_segment'; -- diff --git a/src/test/regress/sql/horology.sql b/src/test/regress/sql/horology.sql index 01193b06453b..6b588fcc9589 100644 --- a/src/test/regress/sql/horology.sql +++ b/src/test/regress/sql/horology.sql @@ -356,19 +356,19 @@ SELECT CAST(CAST(date 'today' + time with time zone '05:30' SELECT CAST(cast(date 'today' + time with time zone '03:30' + interval '1 month 04:01' as timestamp without time zone) AS time) AS "07:31:00"; -SELECT t.d1 + i.f1 AS "102" FROM TIMESTAMP_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i +SELECT t.d1 AS t, i.f1 AS i, t.d1 + i.f1 AS "add", t.d1 - i.f1 AS "subtract" + FROM TIMESTAMP_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i WHERE t.d1 BETWEEN '1990-01-01' AND '2001-01-01' - AND i.f1 BETWEEN '00:00' AND '23:00' ORDER BY 1; + AND i.f1 BETWEEN '00:00' AND '23:00' + ORDER BY 1,2; -SELECT t.d1 - i.f1 AS "102" FROM TIMESTAMP_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i - WHERE t.d1 BETWEEN '1990-01-01' AND '2001-01-01' - AND i.f1 BETWEEN '00:00' AND '23:00' ORDER BY 1; - -SELECT t.f1 + i.f1 AS "80" FROM TIME_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i ORDER BY 1; -SELECT t.f1 - i.f1 AS "80" FROM TIME_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i ORDER BY 1; +SELECT t.f1 AS t, i.f1 AS i, t.f1 + i.f1 AS "add", t.f1 - i.f1 AS "subtract" + FROM TIME_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i + ORDER BY 1,2; -SELECT t.f1 + i.f1 AS "100" FROM TIMETZ_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i ORDER BY 1; -SELECT t.f1 - i.f1 AS "100" FROM TIMETZ_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i ORDER BY 1; +SELECT t.f1 AS t, i.f1 AS i, t.f1 + i.f1 AS "add", t.f1 - i.f1 AS "subtract" + FROM TIMETZ_HOROLOGY_TBL t, INTERVAL_HOROLOGY_TBL i + ORDER BY 1,2; -- SQL9x OVERLAPS operator -- test with time zone diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql index 082bb5cb9673..d0bf487d6404 100644 --- a/src/test/regress/sql/int2.sql +++ b/src/test/regress/sql/int2.sql @@ -86,3 +86,7 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i order by f1; SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i order by f1; +-- check sane handling of INT16_MIN overflow cases +SELECT (-32768)::int2 * (-1)::int2; +SELECT (-32768)::int2 / (-1)::int2; +SELECT (-32768)::int2 % (-1)::int2; diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql index d1b42253cc4d..71c3e3c42a3f 100644 --- a/src/test/regress/sql/int4.sql +++ b/src/test/regress/sql/int4.sql @@ -125,3 +125,11 @@ SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten; SELECT 2 + 2 / 2 AS three; SELECT (2 + 2) / 2 AS two; + +-- check sane handling of INT_MIN overflow cases +SELECT (-2147483648)::int4 * (-1)::int4; +SELECT (-2147483648)::int4 / (-1)::int4; +SELECT (-2147483648)::int4 % (-1)::int4; +SELECT (-2147483648)::int4 * (-1)::int2; +SELECT (-2147483648)::int4 / (-1)::int2; +SELECT (-2147483648)::int4 % (-1)::int2; diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql index a545f54203b9..193bacd5b1c4 100644 --- a/src/test/regress/sql/int8.sql +++ b/src/test/regress/sql/int8.sql @@ -69,3 +69,11 @@ select '-9223372036854775808'::int8; select '-9223372036854775809'::int8; select '9223372036854775807'::int8; select '9223372036854775808'::int8; + +-- check sane handling of INT64_MIN overflow cases +SELECT (-9223372036854775808)::int8 * (-1)::int8; +SELECT (-9223372036854775808)::int8 / (-1)::int8; +SELECT (-9223372036854775808)::int8 % (-1)::int8; +SELECT (-9223372036854775808)::int8 * (-1)::int4; +SELECT (-9223372036854775808)::int8 / (-1)::int4; +SELECT (-9223372036854775808)::int8 % (-1)::int4; diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index 7374353dc828..11020514460d 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -131,4 +131,8 @@ SELECT '3 days 5 milliseconds'::interval; SELECT '1 second 2 seconds'::interval; -- error SELECT '10 milliseconds 20 milliseconds'::interval; -- error SELECT '5.5 seconds 3 milliseconds'::interval; -- error -SELECT '1:20:05 5 microseconds'::interval; -- error \ No newline at end of file +SELECT '1:20:05 5 microseconds'::interval; -- error + +-- check that '30 days' equals '1 month' according to the hash function +select '30 days'::interval = '1 month'::interval as t; +select interval_hash('30 days'::interval) = interval_hash('1 month'::interval) as t; diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 8f974e43bee8..1fa7efcf4230 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -330,6 +330,8 @@ on (x1 = xx1) where (xx2 is not null); -- regression test: check for bug with propagation of implied equality -- to outside an IN -- +analyze tenk1; -- ensure we get consistent plans here + select count(*) from tenk1 a where unique1 in (select unique1 from tenk1 b join tenk1 c using (unique1) where b.unique2 = 42); @@ -487,3 +489,42 @@ select * from zt2 left join zt3 on (f2 = f3) left join zv1 on (f3 = f1) where f2 = 53; + +-- +-- regression test for improper extraction of OR indexqual conditions +-- (as seen in early 8.3.x releases) +-- + +select a.unique2, a.ten, b.tenthous, b.unique2, b.hundred +from tenk1 a left join tenk1 b on a.unique2 = b.tenthous +where a.unique1 = 42 and + ((b.unique2 is null and a.ten = 2) or b.hundred = 3); + +-- +-- test for sane behavior with noncanonical merge clauses, per bug #4926 +-- + +begin; + +set enable_mergejoin = 1; +set enable_hashjoin = 0; +set enable_nestloop = 0; + +create temp table a (i integer); +create temp table b (x integer, y integer); + +select * from a left join b on i = x and i = y and x = i; + +rollback; + +-- +-- test handling of potential equivalence clauses above outer joins +-- + +select q1, unique2, thousand, hundred + from int8_tbl a left join tenk1 b on q1 = unique2 + where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123); + +select f1, unique2, case when unique2 is null then f1 else 0 end + from int4_tbl a left join tenk1 b on f1 = unique2 + where (case when unique2 is null then f1 else 0 end) = 0; diff --git a/src/test/regress/sql/limit.sql b/src/test/regress/sql/limit.sql index c15a486aff9c..3004550b6584 100644 --- a/src/test/regress/sql/limit.sql +++ b/src/test/regress/sql/limit.sql @@ -30,3 +30,12 @@ SELECT ''::text AS five, unique1, unique2, stringu1 SELECT ''::text AS five, unique1, unique2, stringu1 FROM onek ORDER BY unique1 LIMIT 5 OFFSET 900; + +-- Stress test for variable LIMIT in conjunction with bounded-heap sorting + +SELECT + (SELECT n + FROM (VALUES (1)) AS x, + (SELECT n FROM generate_series(1,10) AS n + ORDER BY n LIMIT 1 OFFSET s-1) AS y) AS z + FROM generate_series(1,10) AS s; diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql new file mode 100644 index 000000000000..40bc0e1b41a7 --- /dev/null +++ b/src/test/regress/sql/money.sql @@ -0,0 +1,59 @@ +-- +-- MONEY +-- + +CREATE TABLE money_data (m money); + +INSERT INTO money_data VALUES ('123'); +SELECT * FROM money_data; +SELECT m + '123' FROM money_data; +SELECT m + '123.45' FROM money_data; +SELECT m - '123.45' FROM money_data; +SELECT m * 2 FROM money_data; +SELECT m / 2 FROM money_data; + +-- All true +SELECT m = '$123.00' FROM money_data; +SELECT m != '$124.00' FROM money_data; +SELECT m <= '$123.00' FROM money_data; +SELECT m >= '$123.00' FROM money_data; +SELECT m < '$124.00' FROM money_data; +SELECT m > '$122.00' FROM money_data; + +-- All false +SELECT m = '$123.01' FROM money_data; +SELECT m != '$123.00' FROM money_data; +SELECT m <= '$122.99' FROM money_data; +SELECT m >= '$123.01' FROM money_data; +SELECT m > '$124.00' FROM money_data; +SELECT m < '$122.00' FROM money_data; + +SELECT cashlarger(m, '$124.00') FROM money_data; +SELECT cashsmaller(m, '$124.00') FROM money_data; +SELECT cash_words(m) FROM money_data; +SELECT cash_words(m + '1.23') FROM money_data; + +DELETE FROM money_data; +INSERT INTO money_data VALUES ('$123.45'); +SELECT * FROM money_data; + +DELETE FROM money_data; +INSERT INTO money_data VALUES ('$123.451'); +SELECT * FROM money_data; + +DELETE FROM money_data; +INSERT INTO money_data VALUES ('$123.454'); +SELECT * FROM money_data; + +DELETE FROM money_data; +INSERT INTO money_data VALUES ('$123.455'); +SELECT * FROM money_data; + +DELETE FROM money_data; +INSERT INTO money_data VALUES ('$123.456'); +SELECT * FROM money_data; + +DELETE FROM money_data; +INSERT INTO money_data VALUES ('$123.459'); +SELECT * FROM money_data; + diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql index 0f875079c454..773c2afe7faf 100644 --- a/src/test/regress/sql/oidjoins.sql +++ b/src/test/regress/sql/oidjoins.sql @@ -205,6 +205,10 @@ SELECT ctid, indrelid FROM pg_catalog.pg_index fk WHERE indrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.indrelid); +SELECT ctid, lanowner +FROM pg_catalog.pg_language fk +WHERE lanowner != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.lanowner); SELECT ctid, lanvalidator FROM pg_catalog.pg_language fk WHERE lanvalidator != 0 AND @@ -345,6 +349,74 @@ SELECT ctid, tgfoid FROM pg_catalog.pg_trigger fk WHERE tgfoid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tgfoid); +SELECT ctid, cfgnamespace +FROM pg_catalog.pg_ts_config fk +WHERE cfgnamespace != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.cfgnamespace); +SELECT ctid, cfgowner +FROM pg_catalog.pg_ts_config fk +WHERE cfgowner != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.cfgowner); +SELECT ctid, cfgparser +FROM pg_catalog.pg_ts_config fk +WHERE cfgparser != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_ts_parser pk WHERE pk.oid = fk.cfgparser); +SELECT ctid, mapcfg +FROM pg_catalog.pg_ts_config_map fk +WHERE mapcfg != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_ts_config pk WHERE pk.oid = fk.mapcfg); +SELECT ctid, mapdict +FROM pg_catalog.pg_ts_config_map fk +WHERE mapdict != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_ts_dict pk WHERE pk.oid = fk.mapdict); +SELECT ctid, dictnamespace +FROM pg_catalog.pg_ts_dict fk +WHERE dictnamespace != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.dictnamespace); +SELECT ctid, dictowner +FROM pg_catalog.pg_ts_dict fk +WHERE dictowner != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.dictowner); +SELECT ctid, dicttemplate +FROM pg_catalog.pg_ts_dict fk +WHERE dicttemplate != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_ts_template pk WHERE pk.oid = fk.dicttemplate); +SELECT ctid, prsnamespace +FROM pg_catalog.pg_ts_parser fk +WHERE prsnamespace != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.prsnamespace); +SELECT ctid, prsstart +FROM pg_catalog.pg_ts_parser fk +WHERE prsstart != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsstart); +SELECT ctid, prstoken +FROM pg_catalog.pg_ts_parser fk +WHERE prstoken != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prstoken); +SELECT ctid, prsend +FROM pg_catalog.pg_ts_parser fk +WHERE prsend != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsend); +SELECT ctid, prsheadline +FROM pg_catalog.pg_ts_parser fk +WHERE prsheadline != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsheadline); +SELECT ctid, prslextype +FROM pg_catalog.pg_ts_parser fk +WHERE prslextype != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prslextype); +SELECT ctid, tmplnamespace +FROM pg_catalog.pg_ts_template fk +WHERE tmplnamespace != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.tmplnamespace); +SELECT ctid, tmplinit +FROM pg_catalog.pg_ts_template fk +WHERE tmplinit != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tmplinit); +SELECT ctid, tmpllexize +FROM pg_catalog.pg_ts_template fk +WHERE tmpllexize != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tmpllexize); SELECT ctid, typnamespace FROM pg_catalog.pg_type fk WHERE typnamespace != 0 AND @@ -361,9 +433,9 @@ SELECT ctid, typelem FROM pg_catalog.pg_type fk WHERE typelem != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typelem); -SELECT ctid, typarray -FROM pg_catalog.pg_type fk -WHERE typarray != 0 AND +SELECT ctid, typarray +FROM pg_catalog.pg_type fk +WHERE typarray != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typarray); SELECT ctid, typinput FROM pg_catalog.pg_type fk diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 135c91c8e208..ea831420c3b9 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -294,6 +294,10 @@ WHERE c.castfunc = p.oid AND -- As of 8.2, this finds the cast from cidr to inet, because that is a -- trivial binary coercion while the other way goes through inet_to_cidr(). +-- As of 8.3, this finds the casts from xml to text, varchar, and bpchar, +-- because those are binary-compatible while the reverse goes through +-- texttoxml(), which does an XML syntax check. + SELECT * FROM pg_cast c WHERE c.castfunc = 0 AND @@ -807,6 +811,16 @@ WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND p4.amproclefttype = p3.amproclefttype AND p4.amprocrighttype = p3.amprocrighttype); +-- Also, check if there are any pg_opclass entries that don't seem to have +-- pg_amproc support. + +SELECT amname, opcname, count(*) +FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid + LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND + amproclefttype = amprocrighttype AND amproclefttype = opcintype +GROUP BY amname, amsupport, opcname, amprocfamily +HAVING count(*) != amsupport OR amprocfamily IS NULL; + -- Unfortunately, we can't check the amproc link very well because the -- signature of the function may be different for different support routines -- or different base data types. @@ -846,11 +860,10 @@ WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') -- For hash we can also do a little better: the support routines must be -- of the form hash(lefttype) returns int4. There are several cases where -- we cheat and use a hash function that is physically compatible with the --- datatype even though there's no cast, so for now we can't check that. +-- datatype even though there's no cast, so this check does find a small +-- number of entries. -SELECT p1.amprocfamily, p1.amprocnum, - p2.oid, p2.proname, - p3.opfname +SELECT p1.amprocfamily, p1.amprocnum, p2.proname, p3.opfname FROM pg_amproc AS p1, pg_proc AS p2, pg_opfamily AS p3 WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') AND p1.amprocfamily = p3.oid AND p1.amproc = p2.oid AND @@ -858,8 +871,9 @@ WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') OR proretset OR prorettype != 'int4'::regtype OR pronargs != 1 --- OR NOT physically_coercible(amproclefttype, proargtypes[0]) - OR amproclefttype != amprocrighttype); + OR NOT physically_coercible(amproclefttype, proargtypes[0]) + OR amproclefttype != amprocrighttype) +ORDER BY 1; -- Support routines that are primary members of opfamilies must be immutable -- (else it suggests that the index ordering isn't fixed). But cross-type diff --git a/src/test/regress/sql/partition.sql b/src/test/regress/sql/partition.sql index 8d10b57845f6..2a2f540fc13c 100644 --- a/src/test/regress/sql/partition.sql +++ b/src/test/regress/sql/partition.sql @@ -2852,7 +2852,7 @@ create table child_r -- issue MPP-7898. insert into child_r values (0, 'from r', 0, 0); - + drop table if exists parent_s cascade; --ignore drop table if exists child_r cascade; --ignore diff --git a/src/test/regress/sql/plancache.sql b/src/test/regress/sql/plancache.sql new file mode 100644 index 000000000000..c2e8aedbfac0 --- /dev/null +++ b/src/test/regress/sql/plancache.sql @@ -0,0 +1,164 @@ +-- +-- Tests to exercise the plan caching/invalidation mechanism +-- + +CREATE TEMP TABLE pcachetest AS SELECT * FROM int8_tbl; + +-- create and use a cached plan +PREPARE prepstmt AS SELECT * FROM pcachetest; + +EXECUTE prepstmt; + +-- and one with parameters +PREPARE prepstmt2(bigint) AS SELECT * FROM pcachetest WHERE q1 = $1; + +EXECUTE prepstmt2(123); + +-- invalidate the plans and see what happens +DROP TABLE pcachetest; + +EXECUTE prepstmt; +EXECUTE prepstmt2(123); + +-- recreate the temp table (this demonstrates that the raw plan is +-- purely textual and doesn't depend on OIDs, for instance) +CREATE TEMP TABLE pcachetest AS SELECT * FROM int8_tbl; + +EXECUTE prepstmt; +EXECUTE prepstmt2(123); + +-- prepared statements should prevent change in output tupdesc, +-- since clients probably aren't expecting that to change on the fly +ALTER TABLE pcachetest ADD COLUMN q3 bigint; + +EXECUTE prepstmt; +EXECUTE prepstmt2(123); + +-- but we're nice guys and will let you undo your mistake +ALTER TABLE pcachetest DROP COLUMN q3; + +EXECUTE prepstmt; +EXECUTE prepstmt2(123); + +-- Try it with a view, which isn't directly used in the resulting plan +-- but should trigger invalidation anyway +CREATE TEMP VIEW pcacheview AS + SELECT * FROM pcachetest; + +PREPARE vprep AS SELECT * FROM pcacheview; + +EXECUTE vprep; + +CREATE OR REPLACE TEMP VIEW pcacheview AS + SELECT q1, q2/2 AS q2 FROM pcachetest; + +EXECUTE vprep; + +-- Check basic SPI plan invalidation + +create function cache_test(int) returns int as $$ +declare total int; +begin + create temp table t1(f1 int) distributed by (f1); + insert into t1 values($1); + insert into t1 values(11); + insert into t1 values(12); + insert into t1 values(13); + select sum(f1) into total from t1; + drop table t1; + return total; +end +$$ language plpgsql; + +select cache_test(1); +select cache_test(2); +select cache_test(3); + +-- Check invalidation of plpgsql "simple expression" + +create temp view v1 as + select 2+2 as f1; + +create function cache_test_2() returns int as $$ +begin + return f1 from v1; +end$$ language plpgsql; + +select cache_test_2(); + +create or replace temp view v1 as + select 2+2+4 as f1; +select cache_test_2(); + +create or replace temp view v1 as + select 2+2+4+(select max(unique1) from tenk1) as f1; +select cache_test_2(); + +--- Check that change of search_path is ignored by replans + +create schema s1 + create table abc (f1 int); + +create schema s2 + create table abc (f1 int); + +insert into s1.abc values(123); +insert into s2.abc values(456); + +set search_path = s1; + +prepare p1 as select f1 from abc; + +execute p1; + +set search_path = s2; + +select f1 from abc; + +execute p1; + +alter table s1.abc add column f2 float8; -- force replan + +execute p1; + +drop schema s1 cascade; +drop schema s2 cascade; + +reset search_path; + +-- Check that invalidation deals with regclass constants + +create temp sequence seq; + +prepare p2 as select nextval('seq'); + +execute p2; + +drop sequence seq; + +create temp sequence seq; + +execute p2; + +-- Check DDL via SPI, immediately followed by SPI plan re-use +-- (bug in original coding) + +create function cachebug() returns void as $$ +declare r int; +begin + drop table if exists temptable cascade; + -- Ignore NOTICE about missing DISTRIBUTED BY. It was annoying here, as + -- usually you would only see it on the first invocation, but sometimes + -- you'd also get it on the second invocation, if the plan cache + -- got invalidated in between the invocations. + set client_min_messages=warning; + create temp table temptable as select * from generate_series(1,3) as f1; + reset client_min_messages; + create temp view vv as select * from temptable; + for r in select * from vv order by f1 loop + raise notice '%', r; + end loop; +end$$ language plpgsql; + +select cachebug(); +select cachebug(); diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 1d75f43377e3..178d96f93409 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -1917,8 +1917,8 @@ $$ language plpgsql CONTAINS SQL; begin; ---select refcursor_test1('test1'); ---fetch next from test1; +select refcursor_test1('test1'); +fetch next in test1; --select refcursor_test1('test2'); --fetch all from test2; @@ -2441,6 +2441,229 @@ select footest(); drop function footest(); +-- test scrollable cursor support + +create function sc_test() returns setof integer as $$ +declare + c scroll cursor for select f1 from int4_tbl; + x integer; +begin + open c; + fetch last from c into x; + while found loop + return next x; + fetch prior from c into x; + end loop; + close c; +end; +$$ language plpgsql; + +select * from sc_test(); + +create or replace function sc_test() returns setof integer as $$ +declare + c no scroll cursor for select f1 from int4_tbl; + x integer; +begin + open c; + fetch last from c into x; + while found loop + return next x; + fetch prior from c into x; + end loop; + close c; +end; +$$ language plpgsql; + +select * from sc_test(); -- fails because of NO SCROLL specification + +create or replace function sc_test() returns setof integer as $$ +declare + c refcursor; + x integer; +begin + open c scroll for select f1 from int4_tbl; + fetch last from c into x; + while found loop + return next x; + fetch prior from c into x; + end loop; + close c; +end; +$$ language plpgsql; + +select * from sc_test(); + +create or replace function sc_test() returns setof integer as $$ +declare + c refcursor; + x integer; +begin + open c scroll for execute 'select f1 from int4_tbl'; + fetch last from c into x; + while found loop + return next x; + fetch relative -2 from c into x; + end loop; + close c; +end; +$$ language plpgsql; + +select * from sc_test(); + +create or replace function sc_test() returns setof integer as $$ +declare + c cursor for select * from generate_series(1, 10); + x integer; +begin + open c; + loop + move relative 2 in c; + if not found then + exit; + end if; + fetch next from c into x; + if found then + return next x; + end if; + end loop; + close c; +end; +$$ language plpgsql; + +select * from sc_test(); + +drop function sc_test(); + +-- test qualified variable names + +create function pl_qual_names (param1 int) returns void as $$ +<> +declare + param1 int := 1; +begin + <> + declare + param1 int := 2; + begin + raise notice 'param1 = %', param1; + raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1; + raise notice 'outerblock.param1 = %', outerblock.param1; + raise notice 'innerblock.param1 = %', innerblock.param1; + end; +end; +$$ language plpgsql; + +select pl_qual_names(42); + +drop function pl_qual_names(int); + +-- tests for RETURN QUERY +create function ret_query1(out int, out int) returns setof record as $$ +begin + $1 := -1; + $2 := -2; + return next; + return query select x + 1, x * 10 from generate_series(0, 10) s (x); + return next; +end; +$$ language plpgsql; + +select * from ret_query1(); + +create type record_type as (x text, y int, z boolean); + +create or replace function ret_query2(lim int) returns setof record_type as $$ +begin + return query select md5(s.x::text), s.x, s.x > 0 + from generate_series(-8, lim) s (x) where s.x % 2 = 0; +end; +$$ language plpgsql; + +select * from ret_query2(8); + +-- Test for appropriate cleanup of non-simple expression evaluations +-- (bug in all versions prior to August 2010) + +CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$ +DECLARE + arr text[]; + lr text; + i integer; +BEGIN + arr := array[array['foo','bar'], array['baz', 'quux']]; + lr := 'fool'; + i := 1; + -- use sub-SELECTs to make expressions non-simple + arr[(SELECT i)][(SELECT i+1)] := (SELECT lr); + RETURN arr; +END; +$$ LANGUAGE plpgsql; + +SELECT nonsimple_expr_test(); + +DROP FUNCTION nonsimple_expr_test(); + +CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$ +declare + i integer NOT NULL := 0; +begin + begin + i := (SELECT NULL::integer); -- should throw error + exception + WHEN OTHERS THEN + i := (SELECT 1::integer); + end; + return i; +end; +$$ LANGUAGE plpgsql; + +SELECT nonsimple_expr_test(); + +DROP FUNCTION nonsimple_expr_test(); + +-- +-- Test cases involving recursion and error recovery in simple expressions +-- (bugs in all versions before October 2010). The problems are most +-- easily exposed by mutual recursion between plpgsql and sql functions. +-- + +create function recurse(float8) returns float8 as +$$ +begin + if ($1 > 0) then + return sql_recurse($1 - 1); + else + return $1; + end if; +end; +$$ language plpgsql; + +-- "limit" is to prevent this from being inlined +create function sql_recurse(float8) returns float8 as +$$ select recurse($1) limit 1; $$ language sql; + +select recurse(5); + +create function error1(text) returns text language sql as +$$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$; + +create function error2(p_name_table text) returns text language plpgsql as $$ +begin + return error1(p_name_table); +end$$; + +BEGIN; +create table public.stuffs (stuff text); +SAVEPOINT a; +select error2('nonexistent.stuffs'); +ROLLBACK TO a; +select error2('public.stuffs'); +rollback; + +drop function error2(p_name_table text); +drop function error1(text); + -- Test anonymous code blocks. DO $$ diff --git a/src/test/regress/sql/plpgsql_cache.sql b/src/test/regress/sql/plpgsql_cache.sql index 9369d33bd1e9..50328717f05b 100644 --- a/src/test/regress/sql/plpgsql_cache.sql +++ b/src/test/regress/sql/plpgsql_cache.sql @@ -5,16 +5,18 @@ -- Testing various scenarios where plans will not be cached. -- MPP-16204 +set client_min_messages = 'warning'; +drop table if exists cache_tab cascade; +drop function if exists cache_test(); +drop function if exists cache_test(int); +reset client_min_messages; + -- -- ************************************************************ -- * Repro with drop table inside a function -- * - Multiple executions should not raise an error -- ************************************************************ -- -drop table if exists cache_tab cascade; - -drop function if exists cache_test(); - create function cache_test() returns void as $$ begin @@ -56,17 +58,20 @@ create function cache_test(id int) returns int as $$ declare v_int int; begin - select c1 from cache_tab where c2 = id INTO v_int; + select c1 from cache_tab where c2 = id::text INTO v_int; return v_int; end; $$ language plpgsql READS SQL DATA; select * from cache_test(1); +-- ALTER TABLE prints a NOTICE with unpredictable temp table's name +set client_min_messages='warning'; alter table cache_tab split default partition start (11) inclusive end (20) exclusive into (partition part2, partition def); +reset client_min_messages; -- following should not fail. select * from cache_test(2); @@ -96,7 +101,7 @@ create function cache_test(var int) returns varchar as $$ declare v_name varchar(20) DEFAULT 'zzzz'; begin - select name from cache_tab into v_name where id = var; + select name from cache_tab into v_name where id = var; return v_name; end; $$ language plpgsql READS SQL DATA; @@ -299,10 +304,13 @@ $$ language plpgsql READS SQL DATA; select cache_test(100); +-- ALTER TABLE prints a NOTICE with unpredictable temp table's name +set client_min_messages='warning'; alter table cache_tab split default partition start (11) inclusive end (20) exclusive into (partition part2, partition def); +reset client_min_messages; select cache_test(100); @@ -458,14 +466,6 @@ drop table cache_temp; -- ************************************************************ -- --- start_matchsubs --- --- m|ERROR:\s+relation with OID \d+ does not exist| --- s|ERROR:\s+relation with OID \d+ does not exist|ERROR: relation with OID DUMMY does not exist| --- --- end_matchsubs - - create table cache_tab(c1 int, c2 int) distributed randomly; drop function if exists cache_test(count int); @@ -481,12 +481,16 @@ end; $$ language plpgsql MODIFIES SQL DATA; select cache_test(5); +-- should return 5 rows +select * from cache_tab; drop table if exists cache_tab; create table cache_tab(c1 int, c2 int) distributed randomly; select cache_test(5); +-- should return 5 rows +select * from cache_tab; drop function cache_test(count int); @@ -504,111 +508,183 @@ begin end; $$ language plpgsql MODIFIES SQL DATA; --- this will fail -select cache_test(5); - -set gp_plpgsql_clear_cache_always = on; - --- this will pass select cache_test(5); +-- should return 1 row +select * from cache_tab; drop table if exists cache_tab; drop function cache_test(count int) cascade; --- -- ************************************************************ --- * testing guc +-- * A function that queries a table that's dropped and recreated -- ************************************************************ -- +-- This used to fail on GPDB 4.3, but works after the PostgreSQL 8.3 merge, +-- thanks to upstream plan cache invalidation. (The old GPDB code didn't +-- force plans to be recomputed in the same transaction, only across +-- transactions. There was a GUC called gp_plpgsql_clear_cache_always that +-- you could set, and made this work, though. But that's no longer needed). -drop table if exists cache_tab cascade; - -drop function if exists cache_test(); +create table cache_tab (t text); +insert into cache_tab values ('b'); -create function cache_test() returns void as +create function cache_test(p text) returns integer as $$ begin - drop table if exists cache_tab; - create table cache_tab (id int) distributed randomly; - insert into cache_tab values (1); + return (select count(*) from cache_tab where t = p); end; -$$ language plpgsql MODIFIES SQL DATA; - +$$ language plpgsql; BEGIN; -select cache_test(); - --- this will fail -select cache_test(); +-- Run the function. This caches the plan for the select inside the +-- function. +select cache_test('b'); -COMMIT; +-- Drop and re-create the table +drop table cache_tab; +create table cache_tab (t text); +insert into cache_tab values ('b'); -set gp_plpgsql_clear_cache_always = on; - -select cache_test(); +-- Re-run the function. +select cache_test('b'); +COMMIT; drop table cache_tab; +drop function cache_test(text); -drop function cache_test(); - --- -- ************************************************************ --- * testing guc +-- * A function that calls another function, and the other function is +-- * dropped and recreated. -- ************************************************************ -- -drop table if exists cache_tab cascade; +-- This depends on plan cache invalidation support added in PostgreSQL 8.4. -drop function if exists cache_test(); +-- Create a function, and another function that calls the first one. +create or replace function get_dummy_string(t text) returns text as $$ +begin + return 'foo ' || t; +end; +$$ language plpgsql; -set gp_plpgsql_clear_cache_always = off; -create function cache_test() returns void as +create or replace function cache_test(t text) returns text as $$ -declare count int; begin - count := 3; - while count > 0 - loop - drop table if exists cache_tab; - create table cache_tab (id int) distributed randomly; - insert into cache_tab values (1); - count := count - 1; - end loop; + return get_dummy_string(t); end; -$$ language plpgsql MODIFIES SQL DATA; +$$ language plpgsql; --- this will fail -select cache_test(); +-- Run the function, to warm the plan cache with the function +-- call to get_dummy_string(). +select cache_test(''); -set gp_plpgsql_clear_cache_always = on; +-- Drop and re-create get_dummy_string() function. +drop function get_dummy_string(text); +create or replace function get_dummy_string(t text) returns text as $$ +begin + return 'bar ' || t; +end; +$$ language plpgsql; --- this will pass -select cache_test(); +-- Re-run the function +select cache_test(''); -set gp_plpgsql_clear_cache_always = off; +drop function get_dummy_string(text); +drop function cache_test(text); -drop function cache_test(); +-- ************************************************************ +-- * A function that calls another function, and the other function is +-- * dropped and recreated. +-- ************************************************************ -create function cache_test() returns void as +-- Create a function, and another function that calls the first one. +create or replace function get_dummy_string(t text) returns text as $$ +begin + return 'foo ' || t; +end; +$$ language plpgsql; + +create or replace function cache_test(t text) returns text as $$ -declare count int; begin - count := 3; - while count > 0 - loop - set gp_plpgsql_clear_cache_always = on; - drop table if exists cache_tab; - create table cache_tab (id int) distributed randomly; - insert into cache_tab values (1); - count := count - 1; - end loop; + return get_dummy_string(t); end; -$$ language plpgsql MODIFIES SQL DATA; +$$ language plpgsql; -select cache_test(); +-- Run the function, to warm the plan cache with the function +-- call to get_dummy_string(). +select cache_test(''); + +-- Also run the function as part of a query so that the function +-- is executed in segments rather than the master. (Without ORCA. +-- With ORCA, the query is planned differently and runs on the +-- master anyway). +create temporary table cache_tab (t text); +insert into cache_tab values ('b'); + +select cache_test(t) from cache_tab; + +-- Drop and re-create get_dummy_string() function. +drop function get_dummy_string(text); +create or replace function get_dummy_string(t text) returns text as $$ +begin + return 'bar' || t; +end; +$$ language plpgsql; + +-- Re-run the function +select cache_test(''); +select cache_test(t) from cache_tab; + +drop function get_dummy_string(text); +drop function cache_test(text); drop table cache_tab; -drop function cache_test(); +-- ************************************************************ +-- * A function that calls another function, and the other function +-- * is dropped and recreated. Same as previous tests, but the +-- * function is executed IMMUTABLE, to test that plan cache +-- * invalidation also works when the function is inlined. +-- ************************************************************ +-- +-- To make sure that plan invalidation works also when the function +-- is inlined. + +-- Create a function, and another function that calls the first one. +create or replace function get_dummy_string(t text) returns text as $$ +begin + return 'foo ' || t; +end; +$$ language plpgsql IMMUTABLE; + +create or replace function cache_test(t text) returns text as +$$ +begin + return get_dummy_string(t); +end; +$$ language plpgsql IMMUTABLE; + +create temporary table cache_tab (t text); +insert into cache_tab values ('b'); + +-- Run the function, to warm the plan cache. +select cache_test(''); +select cache_test(t) from cache_tab; + +-- Drop and re-create get_dummy_string() function. +drop function get_dummy_string(text); +create or replace function get_dummy_string(t text) returns text as $$ +begin + return 'bar' || t; +end; +$$ language plpgsql; + +-- Re-run the function +select cache_test(''); +select cache_test(t) from cache_tab; + +drop function get_dummy_string(text); +drop function cache_test(text); diff --git a/src/test/regress/sql/point.sql b/src/test/regress/sql/point.sql index ad965573c032..108a5fb6a911 100644 --- a/src/test/regress/sql/point.sql +++ b/src/test/regress/sql/point.sql @@ -53,7 +53,7 @@ SELECT '' AS two, p.* FROM POINT_TBL p SELECT '' AS six, p.f1, p.f1 <-> point '(0,0)' AS dist FROM POINT_TBL p - ORDER BY dist ; + ORDER BY dist; SELECT '' AS thirtysix, p1.f1 AS point1, p2.f1 AS point2, p1.f1 <-> p2.f1 AS dist FROM POINT_TBL p1, POINT_TBL p2 diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql index c5da7eb71007..ac4b51d95102 100644 --- a/src/test/regress/sql/polymorphism.sql +++ b/src/test/regress/sql/polymorphism.sql @@ -402,11 +402,47 @@ select case when $1 then $2 else $3 end $$ language sql; -- by having the same value for the distributed column for multiple rows. -- We need this to ensure that the NOTICE raised by bleat function gets returned -- in the same order. -create table int4_tbl_new(f1 int, f2 int) distributed by(f1); -insert into int4_tbl_new values(1, 123456), (1, -2147483647), (1, 0), (1, -123456), (1, 2147483647); +create table int4_tbl_new(f0 int, f1 int) distributed by(f0); +insert into int4_tbl_new values(1, 0), (1, 123456), (1, -123456), (1, 2147483647), (1, -2147483647); -- Note this would fail with integer overflow, never mind wrong bleat() output, -- if the CASE expression were not successfully inlined -select f2, sql_if(f2 > 0, bleat(f2), bleat(f2 + 1)) from int4_tbl_new; +select f1, sql_if(f1 > 0, bleat(f1), bleat(f1 + 1)) from int4_tbl_new; select q2, sql_if(q2 > 0, q2, q2 + 1) from int8_tbl; + +-- another kind of polymorphic aggregate + +create function add_group(grp anyarray, ad anyelement, size integer) + returns anyarray + as $$ +begin + if grp is null then + return array[ad]; + end if; + if array_upper(grp, 1) < size then + return grp || ad; + end if; + return grp; +end; +$$ + language plpgsql immutable; + +create aggregate build_group(anyelement, integer) ( + SFUNC = add_group, + STYPE = anyarray +); + +select build_group(q1,3) from (select q1 from int8_tbl order by q1) as t; + +-- this should fail because stype isn't compatible with arg +create aggregate build_group(int8, integer) ( + SFUNC = add_group, + STYPE = int2[] +); + +-- but we can make a non-poly agg from a poly sfunc if types are OK +create aggregate build_group(int8, integer) ( + SFUNC = add_group, + STYPE = int8[] +); diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql index 6ac4b33f139d..3b17b1fa8a28 100644 --- a/src/test/regress/sql/portals.sql +++ b/src/test/regress/sql/portals.sql @@ -4,51 +4,51 @@ SET optimizer_disable_missing_stats_collection=true; BEGIN; -DECLARE foo1 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo1 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo2 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo3 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo3 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo4 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo5 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo5 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo6 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo7 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo7 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo8 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo9 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo9 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo10 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo11 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo11 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo12 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo13 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo14 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo15 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo16 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo17 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo18 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo19 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo20 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo21 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo22 CURSOR FOR SELECT * FROM tenk2 ORDER BY 1,2,3,4; -DECLARE foo23 CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; FETCH 1 in foo1; @@ -125,11 +125,11 @@ CLOSE foo12; -- record this in the system view as well (don't query the time field there -- however) -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors ORDER BY name; +SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; END; -SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors ORDER BY name; +SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; -- -- NO SCROLL disallows backward fetching @@ -137,7 +137,7 @@ SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors OR BEGIN; -DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; FETCH 1 FROM foo24; @@ -178,7 +178,7 @@ CLOSE foo25; BEGIN; -DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1 ORDER BY 1,2,3,4; +DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1 ORDER BY unique2; ROLLBACK; @@ -257,3 +257,139 @@ ROLLBACK; PREPARE cprep AS SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors ORDER BY name; EXECUTE cprep; + +-- test CLOSE ALL; +SELECT name FROM pg_cursors ORDER BY 1; +CLOSE ALL; +SELECT name FROM pg_cursors ORDER BY 1; +BEGIN; +DECLARE foo1 CURSOR WITH HOLD FOR SELECT 1; +DECLARE foo2 CURSOR WITHOUT HOLD FOR SELECT 1; +SELECT name FROM pg_cursors ORDER BY 1; +CLOSE ALL; +SELECT name FROM pg_cursors ORDER BY 1; +COMMIT; + +-- +-- Tests for updatable cursors +-- + +-- In GPDB, we use a dummy column as distribution key, so that all the +-- rows land on the same segment. Otherwise the order the cursor returns +-- the rows is unstable. +CREATE TEMP TABLE uctest(f1 int, f2 text, distkey text) distributed by (distkey); +INSERT INTO uctest VALUES (1, 'one'), (2, 'two'), (3, 'three'); +SELECT f1, f2 FROM uctest; + +-- Check DELETE WHERE CURRENT +BEGIN; +DECLARE c1 CURSOR FOR SELECT f1, f2 FROM uctest; +FETCH 2 FROM c1; +DELETE FROM uctest WHERE CURRENT OF c1; +-- should show deletion +SELECT f1, f2 FROM uctest; +-- cursor did not move +FETCH ALL FROM c1; +-- cursor is insensitive +--MOVE BACKWARD ALL IN c1; -- backwards scans not supported in GPDB +--FETCH ALL FROM c1; +COMMIT; +-- should still see deletion +SELECT f1, f2 FROM uctest; + +-- Check UPDATE WHERE CURRENT; this time use FOR UPDATE +BEGIN; +DECLARE c1 CURSOR FOR SELECT f1, f2 FROM uctest FOR UPDATE; +FETCH c1; +UPDATE uctest SET f1 = 8 WHERE CURRENT OF c1; +SELECT f1, f2 FROM uctest; +COMMIT; +SELECT f1, f2 FROM uctest; + +-- Check repeated-update and update-then-delete cases +BEGIN; +DECLARE c1 CURSOR FOR SELECT f1, f2 FROM uctest; +FETCH c1; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; +SELECT f1, f2 FROM uctest; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- currently broken on GPDB! (does nothing) +SELECT f1, f2 FROM uctest; +-- insensitive cursor should not show effects of updates or deletes +--FETCH RELATIVE 0 FROM c1; +DELETE FROM uctest WHERE CURRENT OF c1; -- currently broken on GPDB! (does nothing) +SELECT f1, f2 FROM uctest; +DELETE FROM uctest WHERE CURRENT OF c1; -- no-op +SELECT f1, f2 FROM uctest; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op +SELECT f1, f2 FROM uctest; +--FETCH RELATIVE 0 FROM c1; +ROLLBACK; +SELECT f1, f2 FROM uctest; + +BEGIN; +DECLARE c1 CURSOR FOR SELECT f1, f2 FROM uctest FOR UPDATE; +FETCH c1; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; +SELECT f1, f2 FROM uctest; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- currently broken on GPDB! (does nothing) +SELECT f1, f2 FROM uctest; +DELETE FROM uctest WHERE CURRENT OF c1; -- currently broken on GPDB! (does nothing) +SELECT f1, f2 FROM uctest; +DELETE FROM uctest WHERE CURRENT OF c1; -- no-op +SELECT f1, f2 FROM uctest; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op +SELECT f1, f2 FROM uctest; +--- sensitive cursors can't currently scroll back, so this is an error: +FETCH RELATIVE 0 FROM c1; +ROLLBACK; +SELECT f1, f2 FROM uctest; + +-- Check inheritance cases +CREATE TEMP TABLE ucchild () inherits (uctest); +INSERT INTO ucchild values(100, 'hundred'); +SELECT f1, f2 FROM uctest; + +BEGIN; +DECLARE c1 CURSOR FOR SELECT f1, f2 FROM uctest; +FETCH 1 FROM c1; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; +FETCH 1 FROM c1; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; +FETCH 1 FROM c1; +UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; +FETCH 1 FROM c1; +COMMIT; +SELECT f1, f2 FROM uctest; + +-- Check various error cases + +DELETE FROM uctest WHERE CURRENT OF c1; -- fail, no such cursor +DECLARE cx CURSOR WITH HOLD FOR SELECT * FROM uctest; +DELETE FROM uctest WHERE CURRENT OF cx; -- fail, can't use held cursor +BEGIN; +DECLARE c CURSOR FOR SELECT * FROM tenk2; +DELETE FROM uctest WHERE CURRENT OF c; -- fail, cursor on wrong table +ROLLBACK; +BEGIN; +DECLARE c CURSOR FOR SELECT * FROM tenk1 JOIN tenk2 USING (unique1); +DELETE FROM tenk1 WHERE CURRENT OF c; -- fail, cursor is on a join +ROLLBACK; +BEGIN; +DECLARE c CURSOR FOR SELECT f1,count(*) FROM uctest GROUP BY f1; +DELETE FROM uctest WHERE CURRENT OF c; -- fail, cursor is on aggregation +ROLLBACK; +BEGIN; +DECLARE c1 CURSOR FOR SELECT * FROM uctest; +DELETE FROM uctest WHERE CURRENT OF c1; -- fail, no current row +ROLLBACK; + +-- WHERE CURRENT OF may someday work with views, but today is not that day. +-- For now, just make sure it errors out cleanly. +CREATE TEMP VIEW ucview AS SELECT f1, f2 FROM uctest; +CREATE RULE ucrule AS ON DELETE TO ucview DO INSTEAD + DELETE FROM uctest WHERE f1 = OLD.f1; +BEGIN; +DECLARE c1 CURSOR FOR SELECT * FROM ucview; +FETCH FROM c1; +DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported +ROLLBACK; diff --git a/src/test/regress/sql/prepare.sql b/src/test/regress/sql/prepare.sql index 9a53d6883dbc..1b5d12f5f33a 100644 --- a/src/test/regress/sql/prepare.sql +++ b/src/test/regress/sql/prepare.sql @@ -38,7 +38,8 @@ EXECUTE q2('regression'); PREPARE q3(text, int, float, boolean, oid, smallint) AS SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR - ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int) ORDER BY 1,2,3,4; + ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int) + ORDER BY unique1; EXECUTE q3('AAAAxx', 5::smallint, 10.5::float, false, 500::oid, 4::bigint); @@ -56,7 +57,8 @@ PREPARE q4(nonexistenttype) AS SELECT $1; -- create table as execute PREPARE q5(int, text) AS - SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2; + SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2 + ORDER BY unique1; CREATE TEMPORARY TABLE q5_prep_results AS EXECUTE q5(200, 'DTAAAA'); SELECT * FROM q5_prep_results ORDER BY 1,2,3,4; @@ -68,3 +70,9 @@ PREPARE q7(unknown) AS SELECT name, statement, parameter_types FROM pg_prepared_statements ORDER BY name; + +-- test DEALLOCATE ALL; +DEALLOCATE ALL; +SELECT name, statement, parameter_types FROM pg_prepared_statements + ORDER BY name; + diff --git a/src/test/regress/sql/qp_misc_jiras.sql b/src/test/regress/sql/qp_misc_jiras.sql index 0f3a17ad517f..51ad9f39fc52 100644 --- a/src/test/regress/sql/qp_misc_jiras.sql +++ b/src/test/regress/sql/qp_misc_jiras.sql @@ -20,7 +20,7 @@ create table qp_misc_jiras.tbl1318(dummy integer, aa text not null); create index concurrently a_daa on qp_misc_jiras.tbl1318(dummy,aa); alter table qp_misc_jiras.tbl1318 alter column aa type integer using bit_length(aa); drop index qp_misc_jiras.tbl1318_daa; -alter table qp_misc_jiras.tbl1318 alter column aa type integer using bit_length(aa); +alter table qp_misc_jiras.tbl1318 alter column aa type integer using bit_length(aa::text); drop table qp_misc_jiras.tbl1318; create table qp_misc_jiras.tbl1318(dummy integer, aa text not null); @@ -847,8 +847,8 @@ END ('2009-06-30 00:00:00'::timestamp without time zone) EVERY ('1 day'::interva ); insert into qp_misc_jiras.tbl6419_test values( 123, '2009-06-01', 12, '2009-06-01 01:01:01', 'aaaaaa'); -select * from qp_misc_jiras.tbl6419_test where icedt = (select partitionrangestart FROM pg_partitions where tablename='test1' and schemaname='public' and partitionrank=1); -select * from qp_misc_jiras.tbl6419_test where '2009-12-12'::date = (select 'test'::text); +select * from qp_misc_jiras.tbl6419_test where icedt::text = (select partitionrangestart FROM pg_partitions where tablename='test1' and schemaname='public' and partitionrank=1); +select * from qp_misc_jiras.tbl6419_test where '2009-12-12'::date::text = (select 'test'::text); drop table qp_misc_jiras.tbl6419_test; -- start_matchsubs diff --git a/src/test/regress/sql/qp_query_execution.sql b/src/test/regress/sql/qp_query_execution.sql index 791e373e310d..9288b4c96cc3 100644 --- a/src/test/regress/sql/qp_query_execution.sql +++ b/src/test/regress/sql/qp_query_execution.sql @@ -155,7 +155,7 @@ analyze foo_p; analyze bar; -select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = 6;', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = 6;', 'Hash Left Join', 'Hash Left Join'); select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = 6 order by 1, 2 desc limit 10; select qx_count_operator('explain select foo_p.k, foo_p.t from foo_p left outer join bar on foo_p.k = bar.k where foo_p.t is not null and foo_p.p = 6;', 'Hash Left Join', 'Hash Left Join'); @@ -211,7 +211,7 @@ analyze b; select qx_count_operator('explain select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6;', 'Hash Left Join', 'Hash Left Join'); select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6 order by 1, 2 desc limit 10; -select qx_count_operator('explain select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = E''6SOME NUMBER''', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = E''6SOME NUMBER''', 'Hash Left Join', 'Hash Left Join'); select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = '6SOME NUMBER' order by 1, 2 desc limit 10; -- Varchar in the select list with a broadcast on top of an append with flow node @@ -252,7 +252,7 @@ analyze b; select qx_count_operator('explain select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6;', 'Hash Left Join', 'Hash Left Join'); select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6 order by 1, 2 desc limit 10; -select qx_count_operator('explain select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = 6;', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = 6;', 'Hash Left Join', 'Hash Left Join'); select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = 6 order by 1, 2 asc limit 10; -- Partitioned tables with decimal type distribution keys @@ -276,7 +276,7 @@ analyze bar; select qx_count_operator('explain select foo_p.k, foo_p.t from foo_p left outer join bar on foo_p.k = bar.k where foo_p.t is not null and foo_p.p = 6;', 'Hash Left Join', 'Hash Left Join'); select foo_p.k, foo_p.t from foo_p left outer join bar on foo_p.k = bar.k where foo_p.t is not null and foo_p.p = 6 order by 1, 2 desc limit 10; -select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = 6.00;', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = 6.00;', 'Hash Left Join', 'Hash Left Join'); select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = 6.00 order by 1, 2 desc limit 10; -- Partitioned tables with character type distribution keys used in predicates @@ -300,7 +300,7 @@ analyze b; select qx_count_operator('explain select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6;', 'Hash Left Join', 'Hash Left Join'); select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6 order by 1, 2 asc limit 10; -select qx_count_operator('explain select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = E''6SOME NUMBER''', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = E''6SOME NUMBER''', 'Hash Left Join', 'Hash Left Join'); select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = '6SOME NUMBER' order by 1, 2 asc limit 10; -- Partitioned tables on both sides of a join @@ -324,7 +324,7 @@ analyze bar_p; select qx_count_operator('explain select foo_p.k, foo_p.t from foo_p left outer join bar_p on foo_p.k = bar_p.k where foo_p.t is not null and foo_p.p = 6;', 'Hash Left Join', 'Hash Left Join'); select foo_p.k, foo_p.t from foo_p left outer join bar_p on foo_p.k = bar_p.k where foo_p.t is not null and foo_p.p = 6 order by 1, 2 desc limit 10; -select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.k where foo_p.t is not null and foo_p.a = 6;', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.k where foo_p.t is not null and foo_p.a = 6;', 'Hash Left Join', 'Hash Left Join'); select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.k where foo_p.t is not null and foo_p.a = 6 order by 1, 2 asc limit 10; select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.k and foo_p.k = bar_p.k where foo_p.t is not null and foo_p.a = 6 and bar_p.a = 14;', 'Nested Loop', 'Hash Join'); @@ -342,7 +342,7 @@ select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.k a select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.k and foo_p.b = bar_p.b where foo_p.t is not null and foo_p.a = 6;', 'Hash Left Join', 'Hash Left Join'); select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.k and foo_p.b = bar_p.b where foo_p.t is not null and foo_p.a = 6 order by 1, 2 desc limit 10; -select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.a where foo_p.t is not null and foo_p.a = 6;', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.a where foo_p.t is not null and foo_p.a = 6;', 'Hash Left Join', 'Hash Left Join'); select foo_p.b, foo_p.t from foo_p left outer join bar_p on foo_p.a = bar_p.a where foo_p.t is not null and foo_p.a = 6 order by 1, 2 asc limit 10; -- Queries where equality predicate is not an immediate constant @@ -362,11 +362,11 @@ insert into bar select i % 7, i % 6, i % 9, i || 'SOME NUMBER', i % 4 from gener analyze foo_p; analyze bar; -select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = (array[1])[1];', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = (array[1])[1];', 'Hash Left Join', 'Hash Left Join'); select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = (array[1])[1] order by 1, 2 desc limit 10; create function mytest(integer) returns integer as 'select $1/100' language sql; -select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = mytest(100);', 'Nested Loop', 'Hash Left Join'); +select qx_count_operator('explain select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = mytest(100);', 'Hash Left Join', 'Hash Left Join'); select foo_p.b, foo_p.t from foo_p left outer join bar on foo_p.a = bar.k where foo_p.t is not null and foo_p.a = mytest(100) order by 1, 2 asc limit 10; drop function if exists mytest(integer); diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql index de9c51b0ce10..138a87c2d747 100644 --- a/src/test/regress/sql/rangefuncs.sql +++ b/src/test/regress/sql/rangefuncs.sql @@ -261,3 +261,35 @@ DROP FUNCTION dup(anyelement); -- fails, no way to deduce outputs CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; + +-- test case for a whole-row-variable bug +create function foo1(n integer, out a text, out b text) + returns setof record + language sql + as $$ select 'foo ' || i, 'bar ' || i from generate_series(1,$1) i $$; + +set work_mem='64kB'; +select t.a, t, t.a from foo1(10000) t limit 1; +reset work_mem; +select t.a, t, t.a from foo1(10000) t limit 1; + +drop function foo1(n integer); + +-- check handling of a SQL function with multiple OUT params (bug #5777) + +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2.1) $$ language sql; + +select * from foobar(); + +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2) $$ language sql; + +select * from foobar(); -- fail + +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2.1, 3) $$ language sql; + +select * from foobar(); -- fail + +drop function foobar(); diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql index dd5dfc0e3122..2f5ac2223a85 100644 --- a/src/test/regress/sql/rowtypes.sql +++ b/src/test/regress/sql/rowtypes.sql @@ -109,5 +109,7 @@ select thousand, tenthous from tenk1 where (thousand, tenthous) >= (997, 5000) order by thousand, tenthous; --- empty row constructor is valid +-- Check some corner cases involving empty rowtypes select ROW(); +select ROW() IS NULL; +select ROW() = ROW(); diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql index 7584a0fc93b6..df0baac0a9b2 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -851,6 +851,21 @@ create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3 insert into rule_and_refint_t3 values (1, 11, 13, 'row7'); insert into rule_and_refint_t3 values (1, 13, 11, 'row8'); +-- +-- test conversion of table to view (needed to load some pg_dump files) +-- + +create table fooview (x int, y text); +select xmin, * from fooview; + +create rule "_RETURN" as on select to fooview do instead + select 1 as x, 'aaa'::text as y; + +select * from fooview; +select xmin, * from fooview; -- fail, views don't have such a column + +drop view fooview; + -- -- check for planner problems with complex inherited UPDATES -- @@ -881,3 +896,41 @@ select * from id_ordered ORDER BY 1; set client_min_messages to warning; -- suppress cascade notices drop table id cascade; +reset client_min_messages; + +-- +-- check corner case where an entirely-dummy subplan is created by +-- constraint exclusion +-- + +create temp table t1 (a integer primary key); + +create temp table t1_1 (check (a >= 0 and a < 10)) inherits (t1); +create temp table t1_2 (check (a >= 10 and a < 20)) inherits (t1); + +create rule t1_ins_1 as on insert to t1 + where new.a >= 0 and new.a < 10 + do instead + insert into t1_1 values (new.a); +create rule t1_ins_2 as on insert to t1 + where new.a >= 10 and new.a < 20 + do instead + insert into t1_2 values (new.a); + +create rule t1_upd_1 as on update to t1 + where old.a >= 0 and old.a < 10 + do instead + update t1_1 set a = new.a where a = old.a; +create rule t1_upd_2 as on update to t1 + where old.a >= 10 and old.a < 20 + do instead + update t1_2 set a = new.a where a = old.a; + +set constraint_exclusion = on; + +insert into t1 select * from generate_series(5,19,1) g; +update t1 set a = 4 where a = 5; + +select * from only t1; +select * from only t1_1; +select * from only t1_2; diff --git a/src/test/regress/sql/select.sql b/src/test/regress/sql/select.sql index 4e3b0a540cd3..6673ccbc1922 100644 --- a/src/test/regress/sql/select.sql +++ b/src/test/regress/sql/select.sql @@ -59,10 +59,15 @@ SELECT onek.unique1, onek.string4 FROM onek -- test partial btree indexes -- -- As of 7.2, planner probably won't pick an indexscan without stats, --- so ANALYZE first. +-- so ANALYZE first. Also, we want to prevent it from picking a bitmapscan +-- followed by sort, because that could hide index ordering problems. -- ANALYZE onek2; +SET enable_seqscan TO off; +SET enable_bitmapscan TO off; +SET enable_sort TO off; + -- -- awk '{if($1<10){print $0;}else{next;}}' onek.data | sort +0n -1 -- @@ -81,6 +86,10 @@ SELECT onek2.unique1, onek2.stringu1 FROM onek2 SELECT onek2.unique1, onek2.stringu1 FROM onek2 WHERE onek2.unique1 > 980 ORDER BY 1; +RESET enable_seqscan; +RESET enable_bitmapscan; +RESET enable_sort; + SELECT two, stringu1, ten, string4 INTO TABLE tmp @@ -195,6 +204,11 @@ select sillysrf(-1) order by 1; drop function sillysrf(int); +-- X = X isn't a no-op, it's effectively X IS NOT NULL assuming = is strict +-- (see bug #5084) +select * from (values (2),(null),(1)) v(k) where k = k order by k; +select * from (values (2),(null),(1)) v(k) where k = k; + -- Test unsupported sorting operators CREATE TABLE nosort (i int); INSERT INTO nosort VALUES(1), (2); diff --git a/src/test/regress/sql/sirv_functions.sql b/src/test/regress/sql/sirv_functions.sql index 69701955c4ed..41e806b4979d 100644 --- a/src/test/regress/sql/sirv_functions.sql +++ b/src/test/regress/sql/sirv_functions.sql @@ -5519,7 +5519,7 @@ BEGIN EXECUTE 'DROP TABLE countries_results'; - RETURN lang_delta || area_delta || gnp_delta ; + RETURN lang_delta::text || area_delta::text || gnp_delta::text ; END; $$ @@ -5577,7 +5577,7 @@ BEGIN EXECUTE 'DROP TABLE countries_results'; - RETURN lang_delta || area_delta || gnp_delta ; + RETURN lang_delta::text || area_delta::text || gnp_delta::text ; END; $$ @@ -5771,7 +5771,7 @@ BEGIN EXECUTE 'DROP TABLE countries_results'; - RETURN lang_delta || area_delta || gnp_delta ; + RETURN lang_delta::text || area_delta::text || gnp_delta::text ; END; $$ @@ -5978,7 +5978,7 @@ BEGIN EXECUTE 'DROP TABLE countries_results'; - RETURN lang_delta || area_delta || gnp_delta ; + RETURN lang_delta::text || area_delta::text || gnp_delta::text ; END; $$ @@ -6213,7 +6213,7 @@ BEGIN EXECUTE 'SELECT max(lang_count - ' || min_languages || '), max(area - ' || min_area || '), max(gnp - ' || min_gnp || ') FROM countries_results' into lang_delta,area_delta,gnp_delta; EXECUTE 'DROP TABLE countries_results'; - RETURN lang_delta || area_delta || gnp_delta ; + RETURN lang_delta::text || area_delta::text || gnp_delta::text ; END; $$ @@ -6385,7 +6385,7 @@ BEGIN EXECUTE 'SELECT max(lang_count - ' || min_languages || '), max(area - ' || min_area || '), max(gnp - ' || min_gnp || ') FROM countries_results' into lang_delta,area_delta,gnp_delta; EXECUTE 'DROP TABLE countries_results'; - RETURN lang_delta || area_delta || gnp_delta ; + RETURN lang_delta::text || area_delta::text || gnp_delta::text ; END; $$ @@ -6703,7 +6703,7 @@ BEGIN EXECUTE 'DROP TABLE countries_results'; - RETURN lang_delta || area_delta || gnp_delta ; + RETURN lang_delta::text || area_delta::text || gnp_delta::text ; END; $$ @@ -6752,7 +6752,7 @@ BEGIN EXECUTE 'DROP TABLE countries_results'; - RETURN lang_delta || area_delta || gnp_delta ; + RETURN lang_delta::text || area_delta::text || gnp_delta::text ; END; $$ diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index cde38b3a379e..4a72a949f298 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -6,7 +6,7 @@ -- -- conditio sine qua non -SHOW stats_start_collector; -- must be on +SHOW track_counts; -- must be on -- wait to let any prior tests finish dumping out stats; -- else our messages might get lost due to contention @@ -51,15 +51,15 @@ begin end $$ language plpgsql; --- enable statistics -SET stats_block_level = on; -SET stats_row_level = on; - -- do a seqscan SELECT count(*) FROM tenk2; -- do an indexscan SELECT count(*) FROM tenk2 WHERE unique1 = 1; +-- force the rate-limiting logic in pgstat_report_tabstat() to time out +-- and send a message +SELECT pg_sleep(1.0); + -- wait for stats collector to update SELECT wait_for_stats(); diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql index 9b436c38c3a2..bf44f64d46ce 100644 --- a/src/test/regress/sql/strings.sql +++ b/src/test/regress/sql/strings.sql @@ -128,7 +128,7 @@ SELECT regexp_matches('foobarbequebaz', $re$(bar)(.+)?(beque)$re$); SELECT regexp_matches('foobarbequebaz', $re$barbeque$re$); -- give me errors -SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque)$re$, 'zipper'); +SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque)$re$, 'gz'); SELECT regexp_matches('foobarbequebaz', $re$(barbeque$re$); SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque){2,1}$re$); @@ -146,9 +146,13 @@ SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPed ovEr THE lazy dOG', 'e' -- no match of pattern SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumped over the lazy dog', 'nomatch') AS foo; SELECT regexp_split_to_array('the quick brown fox jumped over the lazy dog', 'nomatch'); +-- some corner cases +SELECT regexp_split_to_array('123456','1'); +SELECT regexp_split_to_array('123456','6'); +SELECT regexp_split_to_array('123456','.'); -- errors SELECT foo, length(foo) FROM regexp_split_to_table('thE QUick bROWn FOx jUMPed ovEr THE lazy dOG', 'e', 'zippy') AS foo; -SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPed ovEr THE lazy dOG', 'e', 'zippy'); +SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPed ovEr THE lazy dOG', 'e', 'iz'); -- global option meaningless for regexp_split SELECT foo, length(foo) FROM regexp_split_to_table('thE QUick bROWn FOx jUMPed ovEr THE lazy dOG', 'e', 'g') AS foo; SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPed ovEr THE lazy dOG', 'e', 'g'); @@ -159,7 +163,7 @@ SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPed ovEr THE lazy dOG', 'e' -- E021-11 position expression SELECT POSITION('4' IN '1234567890') = '4' AS "4"; -SELECT POSITION(5 IN '1234567890') = '5' AS "5"; +SELECT POSITION('5' IN '1234567890') = '5' AS "5"; -- T312 character overlay function SELECT OVERLAY('abcdef' PLACING '45' FROM 4) AS "abc45f"; @@ -265,6 +269,19 @@ SELECT 'hawkeye' NOT ILIKE 'H%Eye' AS "false"; SELECT 'Hawkeye' ILIKE 'h%' AS "true"; SELECT 'Hawkeye' NOT ILIKE 'h%' AS "false"; +-- +-- test %/_ combination cases, cf bugs #4821 and #5478 +-- + +SELECT 'foo' LIKE '_%' as t, 'f' LIKE '_%' as t, '' LIKE '_%' as f; +SELECT 'foo' LIKE '%_' as t, 'f' LIKE '%_' as t, '' LIKE '%_' as f; + +SELECT 'foo' LIKE '__%' as t, 'foo' LIKE '___%' as t, 'foo' LIKE '____%' as f; +SELECT 'foo' LIKE '%__' as t, 'foo' LIKE '%___' as t, 'foo' LIKE '%____' as f; + +SELECT 'jack' LIKE '%____%' AS t; + + -- -- test implicit type conversion -- @@ -382,12 +399,12 @@ DROP TABLE toasttest; -- test internally compressing datums --- note this tests compressing a datum to a very small size which tests a --- particular case in the packed varlena where very small compressed datums --- must be given a 4-byte header because there are no bits to indicate +-- this tests compressing a datum to a very small size which exercises a +-- corner case in packed-varlena handling: even though small, the compressed +-- datum must be given a 4-byte header because there are no bits to indicate -- compression in a 1-byte header -CREATE TABLE toasttest (c char(2048)); +CREATE TABLE toasttest (c char(4096)); INSERT INTO toasttest VALUES('x'); SELECT length(c), c::text FROM toasttest; SELECT c FROM toasttest; diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index f9ca6e01ec03..5c13ffbdf786 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -8,6 +8,21 @@ SELECT 1 AS zero WHERE 1 NOT IN (SELECT 1); SELECT 1 AS zero WHERE 1 IN (SELECT 2); +-- Check grammar's handling of extra parens in assorted contexts + +SELECT * FROM (SELECT 1 AS x) ss; +SELECT * FROM ((SELECT 1 AS x)) ss; + +(SELECT 2) UNION SELECT 2; +((SELECT 2)) UNION SELECT 2; + +SELECT ((SELECT 2) UNION SELECT 2); +SELECT (((SELECT 2)) UNION SELECT 2); + +SELECT (SELECT ARRAY[1,2,3])[1]; +SELECT ((SELECT ARRAY[1,2,3]))[2]; +SELECT (((SELECT ARRAY[1,2,3])))[3]; + -- Set up some simple test tables CREATE TABLE SUBSELECT_TBL ( @@ -218,9 +233,9 @@ create rule shipped_view_insert as on insert to shipped_view do instead insert into parts (partnum, cost) values (1, 1234.56); insert into shipped_view (ordnum, partnum, value) - values (0, 1, (select cost from parts where partnum = 1)); + values (0, 1, (select cost from parts where partnum = '1')); -select * from shipped_view ORDER BY 1,2; +select * from shipped_view; create rule shipped_view_update as on update to shipped_view do instead update shipped set partnum = new.partnum, value = new.value @@ -246,3 +261,107 @@ select * from ( select min(unique1) from tenk1 as a where not exists (select 1 from tenk1 as b where b.unique2 = 10000) ) ss; + +-- +-- Test that an IN implemented using a UniquePath does unique-ification +-- with the right semantics, as per bug #4113. (Unfortunately we have +-- no simple way to ensure that this test case actually chooses that type +-- of plan, but it does in releases 7.4-8.3. Note that an ordering difference +-- here might mean that some other plan type is being used, rendering the test +-- pointless.) +-- + +create temp table numeric_table (num_col numeric); +insert into numeric_table values (1), (1.000000000000000000001), (2), (3); + +create temp table float_table (float_col float8); +insert into float_table values (1), (2), (3); + +select * from float_table + where float_col in (select num_col from numeric_table); + +select * from numeric_table + where num_col in (select float_col from float_table); + +-- +-- Test case for bug #4290: bogus calculation of subplan param sets +-- + +create temp table ta (id int primary key, val int); + +insert into ta values(1,1); +insert into ta values(2,2); + +create temp table tb (id int primary key, aval int); + +insert into tb values(1,1); +insert into tb values(2,1); +insert into tb values(3,2); +insert into tb values(4,2); + +create temp table tc (id int primary key, aid int); + +insert into tc values(1,1); +insert into tc values(2,2); + +select + ( select min(tb.id) from tb + where tb.aval = (select ta.val from ta where ta.id = tc.aid) ) as min_tb_id +from tc; + +-- +-- Test case for 8.3 "failed to locate grouping columns" bug +-- + +create temp table t1 (f1 numeric(14,0), f2 varchar(30)); + +select * from + (select distinct f1, f2, (select f2 from t1 x where x.f1 = up.f1) as fs + from t1 up) ss +group by f1,f2,fs; + +-- +-- Check that whole-row Vars reading the result of a subselect don't include +-- any junk columns therein +-- +-- This test works in upstream PostgreSQL but triggers a problem in GPDB; in +-- Greenplum the typmods must be the same between all segments and the master +-- but PostgreSQL executor defers typmod assignment to ExecEvalWholeRowVar() +-- which is too late for Greenplum since the plan has already been dispatched. +-- This should be fixed but requires new infrastructure. +-- + +select q from (select max(f1) from int4_tbl group by f1 order by f1) q; + +-- +-- Test case for sublinks pushed down into subselects via join alias expansion +-- + +select + (select sq1) as qq1 +from + (select exists(select 1 from int4_tbl where f1 = q2) as sq1, 42 as dummy + from int8_tbl) sq0 + join + int4_tbl i4 on dummy = i4.f1; + +-- +-- Test case for cross-type partial matching in hashed subplan (bug #7597) +-- + +create temp table outer_7597 (f1 int4, f2 int4); +insert into outer_7597 values (0, 0); +insert into outer_7597 values (1, 0); +insert into outer_7597 values (0, null); +insert into outer_7597 values (1, null); + +create temp table inner_7597(c1 int8, c2 int8); +insert into inner_7597 values(0, null); + +select * from outer_7597 where (f1, f2) not in (select * from inner_7597); + +-- +-- Test case for premature memory release during hashing of subplan output +-- + +select '1'::text in (select '1'::name union all select '1'::name); diff --git a/src/test/regress/sql/temp.sql b/src/test/regress/sql/temp.sql index 2372c87474fd..b13475faceb7 100644 --- a/src/test/regress/sql/temp.sql +++ b/src/test/regress/sql/temp.sql @@ -128,10 +128,10 @@ create temp table whereami (f1 text); insert into whereami values ('temp'); create function public.whoami() returns text - as $$select 'public'::text$$ language sql CONTAINS SQL; + as $$select 'public'::text$$ language sql; create function pg_temp.whoami() returns text - as $$select 'temp'::text$$ language sql CONTAINS SQL; + as $$select 'temp'::text$$ language sql; -- default should have pg_temp implicitly first, but only for tables select * from whereami; diff --git a/src/test/regress/sql/text.sql b/src/test/regress/sql/text.sql index 18d2258df140..2fe2131290be 100644 --- a/src/test/regress/sql/text.sql +++ b/src/test/regress/sql/text.sql @@ -13,6 +13,22 @@ INSERT INTO TEXT_TBL VALUES ('hi de ho neighbor'); SELECT '' AS two, * FROM TEXT_TBL order by f1; +-- As of 8.3 we have removed most implicit casts to text, so that for example +-- this no longer works: + +select length(42); + +-- But as a special exception for usability's sake, we still allow implicit +-- casting to text in concatenations, so long as the other input is text or +-- an unknown literal. So these work: + +select 'four: '::text || 2+2; +select 'four: ' || 2+2; + +-- but not this: + +select 3 || 4.0; + -- -- TEXT CASTING TO/FROM ANY TYPE -- diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index 4e1d6881942f..03a4945206a3 100644 --- a/src/test/regress/sql/timestamptz.sql +++ b/src/test/regress/sql/timestamptz.sql @@ -144,7 +144,7 @@ INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 2001'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 -0097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 5097 BC'); --- Alternate field order that we've historically supported (sort of) +-- Alternative field order that we've historically supported (sort of) -- with regular and POSIXy timezone specs SELECT 'Wed Jul 11 10:51:14 America/New_York 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 GMT-4 2001'::timestamptz; diff --git a/src/test/regress/sql/transactions.sql b/src/test/regress/sql/transactions.sql index b97b6b68993e..ef3375841d85 100644 --- a/src/test/regress/sql/transactions.sql +++ b/src/test/regress/sql/transactions.sql @@ -212,7 +212,7 @@ SELECT 1; -- this should work -- check non-transactional behavior of cursors BEGIN; - DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY 1; + DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY unique2; SAVEPOINT one; FETCH 10 FROM c; ROLLBACK TO SAVEPOINT one; @@ -220,7 +220,7 @@ BEGIN; RELEASE SAVEPOINT one; FETCH 10 FROM c; CLOSE c; - DECLARE c CURSOR FOR SELECT unique2/0 FROM tenk1 ORDER BY 1; + DECLARE c CURSOR FOR SELECT unique2/0 FROM tenk1 ORDER BY unique2; SAVEPOINT two; FETCH 10 FROM c; ROLLBACK TO SAVEPOINT two; diff --git a/src/test/regress/sql/tsdicts.sql b/src/test/regress/sql/tsdicts.sql new file mode 100644 index 000000000000..f36e63a31107 --- /dev/null +++ b/src/test/regress/sql/tsdicts.sql @@ -0,0 +1,120 @@ +--Test text search dictionaries and configurations + +-- Test ISpell dictionary with ispell affix file +CREATE TEXT SEARCH DICTIONARY ispell ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); + +SELECT ts_lexize('ispell', 'skies'); +SELECT ts_lexize('ispell', 'bookings'); +SELECT ts_lexize('ispell', 'booking'); +SELECT ts_lexize('ispell', 'foot'); +SELECT ts_lexize('ispell', 'foots'); +SELECT ts_lexize('ispell', 'rebookings'); +SELECT ts_lexize('ispell', 'rebooking'); +SELECT ts_lexize('ispell', 'rebook'); +SELECT ts_lexize('ispell', 'unbookings'); +SELECT ts_lexize('ispell', 'unbooking'); +SELECT ts_lexize('ispell', 'unbook'); + +SELECT ts_lexize('ispell', 'footklubber'); +SELECT ts_lexize('ispell', 'footballklubber'); +SELECT ts_lexize('ispell', 'ballyklubber'); +SELECT ts_lexize('ispell', 'footballyklubber'); + +-- Test ISpell dictionary with hunspell affix file +CREATE TEXT SEARCH DICTIONARY hunspell ( + Template=ispell, + DictFile=ispell_sample, + AffFile=hunspell_sample +); + +SELECT ts_lexize('hunspell', 'skies'); +SELECT ts_lexize('hunspell', 'bookings'); +SELECT ts_lexize('hunspell', 'booking'); +SELECT ts_lexize('hunspell', 'foot'); +SELECT ts_lexize('hunspell', 'foots'); +SELECT ts_lexize('hunspell', 'rebookings'); +SELECT ts_lexize('hunspell', 'rebooking'); +SELECT ts_lexize('hunspell', 'rebook'); +SELECT ts_lexize('hunspell', 'unbookings'); +SELECT ts_lexize('hunspell', 'unbooking'); +SELECT ts_lexize('hunspell', 'unbook'); + +SELECT ts_lexize('hunspell', 'footklubber'); +SELECT ts_lexize('hunspell', 'footballklubber'); +SELECT ts_lexize('hunspell', 'ballyklubber'); +SELECT ts_lexize('hunspell', 'footballyklubber'); + +-- Synonim dictionary +CREATE TEXT SEARCH DICTIONARY synonym ( + Template=synonym, + Synonyms=synonym_sample +); + +SELECT ts_lexize('synonym', 'PoStGrEs'); +SELECT ts_lexize('synonym', 'Gogle'); + +-- Create and simple test thesaurus dictionary +-- More tests in configuration checks because ts_lexize() +-- cannot pass more than one word to thesaurus. +CREATE TEXT SEARCH DICTIONARY thesaurus ( + Template=thesaurus, + DictFile=thesaurus_sample, + Dictionary=english_stem +); + +SELECT ts_lexize('thesaurus', 'one'); + +-- Test ispell dictionary in configuration +CREATE TEXT SEARCH CONFIGURATION ispell_tst ( + COPY=english +); + +ALTER TEXT SEARCH CONFIGURATION ispell_tst ALTER MAPPING FOR + word, numword, asciiword, hword, numhword, asciihword, hword_part, hword_numpart, hword_asciipart + WITH ispell, english_stem; + +SELECT to_tsvector('ispell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); +SELECT to_tsquery('ispell_tst', 'footballklubber'); +SELECT to_tsquery('ispell_tst', 'footballyklubber:b & rebookings:A & sky'); + +-- Test ispell dictionary with hunspell affix in configuration +CREATE TEXT SEARCH CONFIGURATION hunspell_tst ( + COPY=ispell_tst +); + +ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING + REPLACE ispell WITH hunspell; + +SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); +SELECT to_tsquery('hunspell_tst', 'footballklubber'); +SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); + +-- Test synonym dictionary in configuration +CREATE TEXT SEARCH CONFIGURATION synonym_tst ( + COPY=english +); + +ALTER TEXT SEARCH CONFIGURATION synonym_tst ALTER MAPPING FOR + asciiword, hword_asciipart, asciihword + WITH synonym, english_stem; + +SELECT to_tsvector('synonym_tst', 'Postgresql is often called as postgres or pgsql and pronounced as postgre'); +SELECT to_tsvector('synonym_tst', 'Most common mistake is to write Gogle instead of Google'); + +-- test thesaurus in configuration +-- see thesaurus_sample.ths to understand 'odd' resulting tsvector +CREATE TEXT SEARCH CONFIGURATION thesaurus_tst ( + COPY=synonym_tst +); + +ALTER TEXT SEARCH CONFIGURATION thesaurus_tst ALTER MAPPING FOR + asciiword, hword_asciipart, asciihword + WITH synonym, thesaurus, english_stem; + +SELECT to_tsvector('thesaurus_tst', 'one postgres one two one two three one'); +SELECT to_tsvector('thesaurus_tst', 'Supernovae star is very new star and usually called supernovae (abbrevation SN)'); +SELECT to_tsvector('thesaurus_tst', 'Booking tickets is looking like a booking a tickets'); diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql new file mode 100644 index 000000000000..e437e1686519 --- /dev/null +++ b/src/test/regress/sql/tsearch.sql @@ -0,0 +1,315 @@ +set optimizer_disable_missing_stats_collection = on; +-- +-- Sanity checks for text search catalogs +-- +-- NB: we assume the oidjoins test will have caught any dangling links, +-- that is OID or REGPROC fields that are not zero and do not match some +-- row in the linked-to table. However, if we want to enforce that a link +-- field can't be 0, we have to check it here. + +-- Find unexpected zero link entries + +SELECT oid, prsname +FROM pg_ts_parser +WHERE prsnamespace = 0 OR prsstart = 0 OR prstoken = 0 OR prsend = 0 OR + -- prsheadline is optional + prslextype = 0; + +SELECT oid, dictname +FROM pg_ts_dict +WHERE dictnamespace = 0 OR dictowner = 0 OR dicttemplate = 0; + +SELECT oid, tmplname +FROM pg_ts_template +WHERE tmplnamespace = 0 OR tmpllexize = 0; -- tmplinit is optional + +SELECT oid, cfgname +FROM pg_ts_config +WHERE cfgnamespace = 0 OR cfgowner = 0 OR cfgparser = 0; + +SELECT mapcfg, maptokentype, mapseqno +FROM pg_ts_config_map +WHERE mapcfg = 0 OR mapdict = 0; + +-- Look for pg_ts_config_map entries that aren't one of parser's token types +SELECT * FROM + ( SELECT oid AS cfgid, (ts_token_type(cfgparser)).tokid AS tokid + FROM pg_ts_config ) AS tt +RIGHT JOIN pg_ts_config_map AS m + ON (tt.cfgid=m.mapcfg AND tt.tokid=m.maptokentype) +WHERE + tt.cfgid IS NULL OR tt.tokid IS NULL; + +-- test basic text search behavior without indexes, then with + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; + +create index wowidx on test_tsvector using gist (a); + +SET enable_seqscan=OFF; + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; + +RESET enable_seqscan; + +DROP INDEX wowidx; + +--CREATE INDEX wowidx ON test_tsvector USING gin (a); + +--SET enable_seqscan=OFF; + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; + +RESET enable_seqscan; +INSERT INTO test_tsvector VALUES ('???', 'DFG:1A,2B,6C,10 FGH'); +SELECT * FROM ts_stat('SELECT a FROM test_tsvector') ORDER BY ndoc DESC, nentry DESC, word LIMIT 10; +SELECT * FROM ts_stat('SELECT a FROM test_tsvector', 'AB') ORDER BY ndoc DESC, nentry DESC, word; + +--dictionaries and to_tsvector + +SELECT ts_lexize('english_stem', 'skies'); +SELECT ts_lexize('english_stem', 'identity'); + +SELECT * FROM ts_token_type('default'); + +SELECT * FROM ts_parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 +/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 + wow < jqw <> qwerty'); + +SELECT to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 +/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 + wow < jqw <> qwerty'); + +SELECT length(to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 +/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 + wow < jqw <> qwerty')); + +-- ts_debug + +SELECT * from ts_debug('english', 'abc&nm1;def©ghiõjkl'); + +-- to_tsquery + +SELECT to_tsquery('english', 'qwe & sKies '); +SELECT to_tsquery('simple', 'qwe & sKies '); +SELECT to_tsquery('english', '''the wether'':dc & '' sKies '':BC '); +SELECT to_tsquery('english', 'asd&(and|fghj)'); +SELECT to_tsquery('english', '(asd&and)|fghj'); +SELECT to_tsquery('english', '(asd&!and)|fghj'); +SELECT to_tsquery('english', '(the|and&(i&1))&fghj'); + +SELECT plainto_tsquery('english', 'the and z 1))& fghj'); +SELECT plainto_tsquery('english', 'foo bar') && plainto_tsquery('english', 'asd'); +SELECT plainto_tsquery('english', 'foo bar') || plainto_tsquery('english', 'asd fg'); +SELECT plainto_tsquery('english', 'foo bar') || !!plainto_tsquery('english', 'asd fg'); +SELECT plainto_tsquery('english', 'foo bar') && 'asd | fg'; + +SELECT ts_rank_cd(to_tsvector('english', ' +Day after day, day after day, + We stuck, nor breath nor motion, +As idle as a painted Ship + Upon a painted Ocean. +Water, water, every where + And all the boards did shrink; +Water, water, every where, + Nor any drop to drink. +S. T. Coleridge (1772-1834) +'), to_tsquery('english', 'paint&water')); + +SELECT ts_rank_cd(to_tsvector('english', ' +Day after day, day after day, + We stuck, nor breath nor motion, +As idle as a painted Ship + Upon a painted Ocean. +Water, water, every where + And all the boards did shrink; +Water, water, every where, + Nor any drop to drink. +S. T. Coleridge (1772-1834) +'), to_tsquery('english', 'breath&motion&water')); + +SELECT ts_rank_cd(to_tsvector('english', ' +Day after day, day after day, + We stuck, nor breath nor motion, +As idle as a painted Ship + Upon a painted Ocean. +Water, water, every where + And all the boards did shrink; +Water, water, every where, + Nor any drop to drink. +S. T. Coleridge (1772-1834) +'), to_tsquery('english', 'ocean')); + +--headline tests +SELECT ts_headline('english', ' +Day after day, day after day, + We stuck, nor breath nor motion, +As idle as a painted Ship + Upon a painted Ocean. +Water, water, every where + And all the boards did shrink; +Water, water, every where, + Nor any drop to drink. +S. T. Coleridge (1772-1834) +', to_tsquery('english', 'paint&water')); + +SELECT ts_headline('english', ' +Day after day, day after day, + We stuck, nor breath nor motion, +As idle as a painted Ship + Upon a painted Ocean. +Water, water, every where + And all the boards did shrink; +Water, water, every where, + Nor any drop to drink. +S. T. Coleridge (1772-1834) +', to_tsquery('english', 'breath&motion&water')); + +SELECT ts_headline('english', ' +Day after day, day after day, + We stuck, nor breath nor motion, +As idle as a painted Ship + Upon a painted Ocean. +Water, water, every where + And all the boards did shrink; +Water, water, every where, + Nor any drop to drink. +S. T. Coleridge (1772-1834) +', to_tsquery('english', 'ocean')); + +SELECT ts_headline('english', ' + + + +Sea view wow foo bar qq +YES   +ff-bg + + +', +to_tsquery('english', 'sea&foo'), 'HighlightAll=true'); + +--Rewrite sub system + +CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT); +\set ECHO none +\copy test_tsquery from stdin +'New York' new & york | big & apple | nyc +Moscow moskva | moscow +'Sanct Peter' Peterburg | peter | 'Sanct Peterburg' +'foo bar qq' foo & (bar | qq) & city +\. +\set ECHO all + +ALTER TABLE test_tsquery ADD COLUMN keyword tsquery; +UPDATE test_tsquery SET keyword = to_tsquery('english', txtkeyword); +ALTER TABLE test_tsquery ADD COLUMN sample tsquery; +UPDATE test_tsquery SET sample = to_tsquery('english', txtsample::text); + + +SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; +SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york'; +SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york'; +SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york'; +SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; + +CREATE INDEX bt_tsq ON test_tsquery (keyword); + +SET enable_seqscan=OFF; + +SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; +SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york'; +SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york'; +SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york'; +SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; + +RESET enable_seqscan; + +SELECT ts_rewrite('foo & bar & qq & new & york', 'new & york'::tsquery, 'big & apple | nyc | new & york & city'); + +SELECT ts_rewrite('moscow', 'SELECT keyword, sample FROM test_tsquery'::text ); +SELECT ts_rewrite('moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'::text ); +SELECT ts_rewrite('bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'::text ); + +SELECT ts_rewrite( 'moscow', 'SELECT keyword, sample FROM test_tsquery'); +SELECT ts_rewrite( 'moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'); +SELECT ts_rewrite( 'bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'); + + +SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; +SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow'; +SELECT keyword FROM test_tsquery WHERE keyword <@ 'new'; +SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow'; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; + +CREATE INDEX qq ON test_tsquery USING gist (keyword tsquery_ops); +SET enable_seqscan=OFF; + +SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; +SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow'; +SELECT keyword FROM test_tsquery WHERE keyword <@ 'new'; +SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow'; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; +SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; + +RESET enable_seqscan; + +--test GUC +SET default_text_search_config=simple; + +SELECT to_tsvector('SKIES My booKs'); +SELECT plainto_tsquery('SKIES My booKs'); +SELECT to_tsquery('SKIES & My | booKs'); + +SET default_text_search_config=english; + +SELECT to_tsvector('SKIES My booKs'); +SELECT plainto_tsquery('SKIES My booKs'); +SELECT to_tsquery('SKIES & My | booKs'); + +--trigger + +-- GPDB doesn't allow updating the distribution key, so create a synthetic +-- distribution key column. +alter table test_tsvector add column distkey int4; +alter table test_tsvector set distributed by (distkey); + +CREATE TRIGGER tsvectorupdate +BEFORE UPDATE OR INSERT ON test_tsvector +FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(a, 'pg_catalog.english', t); + +SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); +INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); +SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); +UPDATE test_tsvector SET t = null WHERE t = '345 qwerty'; +SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); + +INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); + +SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); diff --git a/src/test/regress/sql/tstypes.sql b/src/test/regress/sql/tstypes.sql new file mode 100644 index 000000000000..49afc3b23a28 --- /dev/null +++ b/src/test/regress/sql/tstypes.sql @@ -0,0 +1,97 @@ +--Base tsvector test + +SELECT '1'::tsvector; +SELECT '1 '::tsvector; +SELECT ' 1'::tsvector; +SELECT ' 1 '::tsvector; +SELECT '1 2'::tsvector; +SELECT '''1 2'''::tsvector; +SELECT E'''1 \\''2'''::tsvector; +SELECT E'''1 \\''2''3'::tsvector; +SELECT E'''1 \\''2'' 3'::tsvector; +SELECT E'''1 \\''2'' '' 3'' 4 '::tsvector; +SELECT $$'\\as' ab\c ab\\c AB\\\c ab\\\\c$$::tsvector; +SELECT tsvectorin(tsvectorout($$'\\as' ab\c ab\\c AB\\\c ab\\\\c$$::tsvector)); +SELECT '''w'':4A,3B,2C,1D,5 a:8'; +SELECT 'a:3A b:2a'::tsvector || 'ba:1234 a:1B'; +SELECT setweight('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd zxc:81,567,222A'::tsvector, 'c'); +SELECT strip('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd'::tsvector); + +--Base tsquery test +SELECT '1'::tsquery; +SELECT '1 '::tsquery; +SELECT ' 1'::tsquery; +SELECT ' 1 '::tsquery; +SELECT '''1 2'''::tsquery; +SELECT E'''1 \\''2'''::tsquery; +SELECT '!1'::tsquery; +SELECT '1|2'::tsquery; +SELECT '1|!2'::tsquery; +SELECT '!1|2'::tsquery; +SELECT '!1|!2'::tsquery; +SELECT '!(!1|!2)'::tsquery; +SELECT '!(!1|2)'::tsquery; +SELECT '!(1|!2)'::tsquery; +SELECT '!(1|2)'::tsquery; +SELECT '1&2'::tsquery; +SELECT '!1&2'::tsquery; +SELECT '1&!2'::tsquery; +SELECT '!1&!2'::tsquery; +SELECT '(1&2)'::tsquery; +SELECT '1&(2)'::tsquery; +SELECT '!(1)&2'::tsquery; +SELECT '!(1&2)'::tsquery; +SELECT '1|2&3'::tsquery; +SELECT '1|(2&3)'::tsquery; +SELECT '(1|2)&3'::tsquery; +SELECT '1|2&!3'::tsquery; +SELECT '1|!2&3'::tsquery; +SELECT '!1|2&3'::tsquery; +SELECT '!1|(2&3)'::tsquery; +SELECT '!(1|2)&3'::tsquery; +SELECT '(!1|2)&3'::tsquery; +SELECT '1|(2|(4|(5|6)))'::tsquery; +SELECT '1|2|4|5|6'::tsquery; +SELECT '1&(2&(4&(5&6)))'::tsquery; +SELECT '1&2&4&5&6'::tsquery; +SELECT '1&(2&(4&(5|6)))'::tsquery; +SELECT '1&(2&(4&(5|!6)))'::tsquery; +SELECT E'1&(''2''&('' 4''&(\\|5 | ''6 \\'' !|&'')))'::tsquery; +SELECT $$'\\as'$$::tsquery; + +SELECT 'a' < 'b & c'::tsquery as "true"; +SELECT 'a' > 'b & c'::tsquery as "false"; +SELECT 'a | f' < 'b & c'::tsquery as "true"; +SELECT 'a | ff' < 'b & c'::tsquery as "false"; +SELECT 'a | f | g' < 'b & c'::tsquery as "false"; + +SELECT numnode( 'new'::tsquery ); +SELECT numnode( 'new & york'::tsquery ); +SELECT numnode( 'new & york | qwery'::tsquery ); + +SELECT 'foo & bar'::tsquery && 'asd'; +SELECT 'foo & bar'::tsquery || 'asd & fg'; +SELECT 'foo & bar'::tsquery || !!'asd & fg'::tsquery; +SELECT 'foo & bar'::tsquery && 'asd | fg'; + +-- tsvector-tsquery operations + +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca' as "true"; +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:B' as "true"; +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:A' as "true"; +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:C' as "false"; +SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:CB' as "true"; + +SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a | s'); +SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a | s'); +SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a | s'); +SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a & s'); +SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a & s'); +SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a & s'); + +SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a | s'); +SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a | s'); +SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a | s'); +SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a & s'); +SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a & s'); +SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a & s'); diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql new file mode 100644 index 000000000000..12090e1f28b1 --- /dev/null +++ b/src/test/regress/sql/txid.sql @@ -0,0 +1,54 @@ +-- txid_snapshot data type and related functions + +-- i/o +select '12:13:'::txid_snapshot; +select '12:18:14,16'::txid_snapshot; + +-- errors +select '31:12:'::txid_snapshot; +select '0:1:'::txid_snapshot; +select '12:13:0'::txid_snapshot; +select '12:16:14,13'::txid_snapshot; +select '12:16:14,14'::txid_snapshot; + +create temp table snapshot_test ( + nr integer, + snap txid_snapshot +); + +insert into snapshot_test values (1, '12:13:'); +insert into snapshot_test values (2, '12:20:13,15,18'); +insert into snapshot_test values (3, '100001:100009:100005,100007,100008'); +insert into snapshot_test values (4, '100:150: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'); +select snap from snapshot_test order by nr; + +select txid_snapshot_xmin(snap), + txid_snapshot_xmax(snap), + txid_snapshot_xip(snap) +from snapshot_test order by nr, 1, 2, 3; + +select id, txid_visible_in_snapshot(id, snap) +from snapshot_test, generate_series(11, 21) id +where nr = 2; + +-- test bsearch +select id, txid_visible_in_snapshot(id, snap) +from snapshot_test, generate_series(90, 160) id +where nr = 4; + +-- test current values also +select txid_current() >= txid_snapshot_xmin(txid_current_snapshot()); + +-- we can't assume current is always less than xmax, however + +select txid_visible_in_snapshot(txid_current(), txid_current_snapshot()); + +-- test 64bitness + +select txid_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013'; +select txid_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); +select txid_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + +-- test 64bit overflow +SELECT txid_snapshot '1:9223372036854775807:3'; +SELECT txid_snapshot '1:9223372036854775808:3'; diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql index 1b6eaab9ded7..7202a2fb0738 100644 --- a/src/test/regress/sql/type_sanity.sql +++ b/src/test/regress/sql/type_sanity.sql @@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typnamespace = 0 OR (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR - (p1.typtype not in ('b', 'c', 'd', 'p')) OR + (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm')); @@ -50,15 +50,22 @@ FROM pg_type as p1 WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR (p1.typtype != 'c' AND p1.typrelid != 0); --- Look for basic types that don't have an array type. +-- Look for basic or enum types that don't have an array type. -- NOTE: as of 8.0, this check finds smgr and unknown. SELECT p1.oid, p1.typname FROM pg_type as p1 -WHERE p1.typtype in ('b') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS +WHERE p1.typtype in ('b','e') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND - p2.typelem = p1.oid); + p2.typelem = p1.oid and p1.typarray = p2.oid); + +-- Make sure typarray points to a varlena array type of our own base +SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, + p2.typelem, p2.typlen +FROM pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid) +WHERE p1.typarray <> 0 AND + (p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> -1); -- Text conversion routines must be provided. diff --git a/src/test/regress/sql/union_gp.sql b/src/test/regress/sql/union_gp.sql index 10aee97b1ef9..c26c82ef3882 100644 --- a/src/test/regress/sql/union_gp.sql +++ b/src/test/regress/sql/union_gp.sql @@ -15,8 +15,7 @@ select ARRAY[1, 2, 3] union select distinct null::integer[]; select 1 intersect (select 1, 2 union all select 3, 4); select 1 a, row_number() over (partition by 'a') union all (select 1 a , 2 b); --- This can preserve domain types, but we keep compatibility for now --- See MPP-7509 +-- This should preserve domain types select pg_typeof(a) from (select 'a'::information_schema.sql_identifier a union all select 'b'::information_schema.sql_identifier)a; diff --git a/src/timezone/README b/src/timezone/README index 4db3148e0cd3..201b2938814d 100644 --- a/src/timezone/README +++ b/src/timezone/README @@ -3,9 +3,12 @@ src/timezone/README Timezone ======== -This is a PostgreSQL adapted version of the timezone library from: +This is a PostgreSQL adapted version of the timezone library from +http://www.iana.org/time-zones - ftp://elsie.nci.nih.gov/pub/tzcode*.tar.gz +The source code can be found at: + + ftp://ftp.iana.org/tz/releases/tzcode*.tar.gz The code is currently synced with release 2010c. There are many cosmetic (and not so cosmetic) differences from the original tzcode library, but @@ -13,7 +16,7 @@ diffs in the upstream version should usually be propagated to our version. The data files under data/ are an exact copy of the latest data set from: - ftp://elsie.nci.nih.gov/pub/tzdata*.tar.gz + ftp://ftp.iana.org/tz/releases/tzdata*.tar.gz Since time zone rules change frequently in some parts of the world, we should endeavor to update the data files before each PostgreSQL diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index 44d2298e0d3a..41614c8fca5e 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -387,15 +387,15 @@ identify_system_timezone(void) * enough to identify DST transition rules, since everybody switches on * Sundays.) This is sufficient to cover most of the Unix time_t range, * and we don't want to look further than that since many systems won't - * have sane TZ behavior further back anyway. The further back the zone - * matches, the better we score it. This may seem like a rather random - * way of doing things, but experience has shown that system-supplied - * timezone definitions are likely to have DST behavior that is right for - * the recent past and not so accurate further back. Scoring in this way - * allows us to recognize zones that have some commonality with the zic - * database, without insisting on exact match. (Note: we probe Thursdays, - * not Sundays, to avoid triggering DST-transition bugs in localtime - * itself.) + * have sane TZ behavior further back anyway. The further + * back the zone matches, the better we score it. This may seem like a + * rather random way of doing things, but experience has shown that + * system-supplied timezone definitions are likely to have DST behavior + * that is right for the recent past and not so accurate further back. + * Scoring in this way allows us to recognize zones that have some + * commonality with the zic database, without insisting on exact match. + * (Note: we probe Thursdays, not Sundays, to avoid triggering + * DST-transition bugs in localtime itself.) */ tnow = time(NULL); tm = localtime(&tnow); @@ -404,7 +404,6 @@ identify_system_timezone(void) thisyear = tm->tm_year + 1900; t = build_time_t(thisyear, 1, 15); - /* * Round back to GMT midnight Thursday. This depends on the knowledge * that the time_t origin is Thu Jan 01 1970. (With a different origin @@ -644,7 +643,7 @@ static const struct /* * This list was built from the contents of the registry at * HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time - * Zones on Windows XP Professional SP2 + * Zones on Windows 2003 R2. * * The zones have been matched to zic timezones by looking at the cities * listed in the win32 display name (in the comment here) in most cases. @@ -669,6 +668,10 @@ static const struct "Arabic Standard Time", "Arabic Daylight Time", "Asia/Baghdad" }, /* (GMT+03:00) Baghdad */ + { + "Argentina Standard Time", "Argentina Daylight Time", + "America/Buenos_Aires" + }, /* (GMT-03:00) Buenos Aires */ { "Armenian Standard Time", "Armenian Daylight Time", "Asia/Yerevan" @@ -685,10 +688,18 @@ static const struct "AUS Eastern Standard Time", "AUS Eastern Daylight Time", "Australia/Canberra" }, /* (GMT+10:00) Canberra, Melbourne, Sydney */ + { + "Azerbaijan Standard Time", "Azerbaijan Daylight Time", + "Asia/Baku" + }, /* (GMT+04:00) Baku */ { "Azores Standard Time", "Azores Daylight Time", "Atlantic/Azores" }, /* (GMT-01:00) Azores */ + { + "Bangladesh Standard Time", "Bangladesh Daylight Time", + "Asia/Dhaka" + }, /* (GMT+06:00) Dhaka */ { "Canada Central Standard Time", "Canada Central Daylight Time", "Canada/Saskatchewan" @@ -705,14 +716,19 @@ static const struct "Cen. Australia Standard Time", "Cen. Australia Daylight Time", "Australia/Adelaide" }, /* (GMT+09:30) Adelaide */ + /* Central America (other than Mexico) generally does not observe DST */ { "Central America Standard Time", "Central America Daylight Time", - "CST6CDT" + "CST6" }, /* (GMT-06:00) Central America */ { "Central Asia Standard Time", "Central Asia Daylight Time", "Asia/Dhaka" }, /* (GMT+06:00) Astana, Dhaka */ + { + "Central Brazilian Standard Time", "Central Brazilian Daylight Time", + "America/Cuiaba" + }, /* (GMT-04:00) Cuiaba */ { "Central Europe Standard Time", "Central Europe Daylight Time", "Europe/Belgrade" @@ -825,10 +841,18 @@ static const struct "Jordan Standard Time", "Jordan Daylight Time", "Asia/Amman" }, /* (GMT+02:00) Amman */ + { + "Kamchatka Standard Time", "Kamchatka Daylight Time", + "Asia/Kamchatka" + }, /* (GMT+12:00) Petropavlovsk-Kamchatsky */ { "Korea Standard Time", "Korea Daylight Time", "Asia/Seoul" }, /* (GMT+09:00) Seoul */ + { + "Mauritius Standard Time", "Mauritius Daylight Time", + "Indian/Mauritius" + }, /* (GMT+04:00) Port Louis */ { "Mexico Standard Time", "Mexico Daylight Time", "America/Mexico_City" @@ -850,6 +874,10 @@ static const struct "Montevideo Standard Time", "Montevideo Daylight Time", "America/Montevideo" }, /* (GMT-03:00) Montevideo */ + { + "Morocco Standard Time", "Morocco Daylight Time", + "Africa/Casablanca" + }, /* (GMT) Casablanca */ { "Mountain Standard Time", "Mountain Daylight Time", "US/Mountain" @@ -865,8 +893,12 @@ static const struct }, /* (GMT+06:30) Rangoon */ { "N. Central Asia Standard Time", "N. Central Asia Daylight Time", - "Asia/Almaty" - }, /* (GMT+06:00) Almaty, Novosibirsk */ + "Asia/Novosibirsk" + }, /* (GMT+06:00) Novosibirsk */ + { + "Namibia Standard Time", "Namibia Daylight Time", + "Africa/Windhoek" + }, /* (GMT+02:00) Windhoek */ { "Namibia Standard Time", "Namibia Daylight Time", "Africa/Windhoek" @@ -904,6 +936,14 @@ static const struct "Pacific Standard Time (Mexico)", "Pacific Daylight Time (Mexico)", "America/Tijuana" }, /* (GMT-08:00) Tijuana, Baja California */ + { + "Pakistan Standard Time", "Pakistan Daylight Time", + "Asia/Karachi" + }, /* (GMT+05:00) Islamabad, Karachi */ + { + "Paraguay Standard Time", "Paraguay Daylight Time", + "America/Asuncion" + }, /* (GMT-04:00) Asuncion */ { "Romance Standard Time", "Romance Daylight Time", "Europe/Brussels" @@ -962,6 +1002,10 @@ static const struct "Tonga Standard Time", "Tonga Daylight Time", "Pacific/Tongatapu" }, /* (GMT+13:00) Nuku'alofa */ + { + "Ulaanbaatar Standard Time", "Ulaanbaatar Daylight Time", + "Asia/Ulaanbaatar", + }, /* (GMT+08:00) Ulaanbaatar */ { "US Eastern Standard Time", "US Eastern Daylight Time", "US/Eastern" @@ -970,6 +1014,26 @@ static const struct "US Mountain Standard Time", "US Mountain Daylight Time", "US/Arizona" }, /* (GMT-07:00) Arizona */ + { + "Coordinated Universal Time", "Coordinated Universal Time", + "UTC" + }, /* (GMT) Coordinated Universal Time */ + { + "UTC+12", "UTC+12", + "Etc/GMT+12" + }, /* (GMT+12:00) Coordinated Universal Time+12 */ + { + "UTC-02", "UTC-02", + "Etc/GMT-02" + }, /* (GMT-02:00) Coordinated Universal Time-02 */ + { + "UTC-11", "UTC-11", + "Etc/GMT-11" + }, /* (GMT-11:00) Coordinated Universal Time-11 */ + { + "Venezuela Standard Time", "Venezuela Daylight Time", + "America/Caracas", + }, /* (GMT-04:30) Caracas */ { "Vladivostok Standard Time", "Vladivostok Daylight Time", "Asia/Vladivostok" @@ -1091,9 +1155,10 @@ identify_system_timezone(void) if ((r = RegQueryValueEx(key, "Std", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS) { ereport(WARNING, - (errmsg_internal("could not query value for 'std' to identify Windows timezone: %i", (int) r))); + (errmsg_internal("could not query value for 'std' to identify Windows timezone \"%s\": %i", + keyname, (int) r))); RegCloseKey(key); - break; + continue; /* Proceed to look at the next timezone */ } if (strcmp(tzname, zonename) == 0) { @@ -1107,9 +1172,10 @@ identify_system_timezone(void) if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS) { ereport(WARNING, - (errmsg_internal("could not query value for 'dlt' to identify Windows timezone: %i", (int) r))); + (errmsg_internal("could not query value for 'dlt' to identify Windows timezone \"%s\": %i", + keyname, (int) r))); RegCloseKey(key); - break; + continue; /* Proceed to look at the next timezone */ } if (strcmp(tzname, zonename) == 0) { diff --git a/src/timezone/tznames/Asia.txt b/src/timezone/tznames/Asia.txt index 5b3291833cfe..a84a0f5029d2 100644 --- a/src/timezone/tznames/Asia.txt +++ b/src/timezone/tznames/Asia.txt @@ -4,7 +4,7 @@ # a template for timezones you could need. See the `Date/Time Support' # appendix in the PostgreSQL documentation for more information. # -# $PostgreSQL: pgsql/src/timezone/tznames/Asia.txt,v 1.3 2006/12/15 16:54:43 tgl Exp $ +# $PostgreSQL: pgsql/src/timezone/tznames/Asia.txt,v 1.3.2.2 2010/05/11 22:37:05 tgl Exp $ # # CONFLICT! ADT is not unique diff --git a/src/timezone/tznames/Default b/src/timezone/tznames/Default index dc2edb1f822b..a0204dacf320 100755 --- a/src/timezone/tznames/Default +++ b/src/timezone/tznames/Default @@ -4,7 +4,7 @@ # timezone_abbreviations to 'Default'. See the `Date/Time Support' # appendix in the PostgreSQL documentation for more information. # -# $PostgreSQL: pgsql/src/timezone/tznames/Default,v 1.3 2006/12/15 16:54:43 tgl Exp $ +# $PostgreSQL: pgsql/src/timezone/tznames/Default,v 1.4.2.5 2010/08/26 19:59:08 tgl Exp $ #################### AFRICA #################### diff --git a/src/tools/major_release_split b/src/tools/major_release_split new file mode 100755 index 000000000000..37f8111ca33a --- /dev/null +++ b/src/tools/major_release_split @@ -0,0 +1,30 @@ +: + +# This program takes release.sgml and breaks it up into +# per-major-release files that can be copied to the proper +# CVS tree. + +[ "$#" -ne 1 ] && echo "Usage: $0 release_sgml_file" 1>&2 && exit 1 + +FILE="$1" + +trap "rm -f /tmp/preamble" 0 1 2 3 15 + +# Create the SGML preamble file +# Copy from the start of the file to the first "sect1" heading +grep -B 1000000 "`sed -n '/ /tmp/preamble + +# Create per-major-release files +# spin over all "sect1" releases to find major release numbers +sed -n 's/^ *> "$RELEASE"-"`basename $FILE`" +done + diff --git a/src/tools/msvc/Genbki.pm b/src/tools/msvc/Genbki.pm index 5c1e7ec55095..07f36e1b32ef 100755 --- a/src/tools/msvc/Genbki.pm +++ b/src/tools/msvc/Genbki.pm @@ -33,27 +33,21 @@ sub genbki $version =~ /^(\d+\.\d+)/ || die "Bad format verison $version\n"; my $majorversion = $1; - # In PG 8.2, NAMEDATALEN is in postgres_ext.h. it moves - # to pg_config_manual.h in PG 8.3 my $pgext = read_file("src/include/pg_config_manual.h"); $pgext =~ /^#define\s+NAMEDATALEN\s+(\d+)$/mg || die "Could not read NAMEDATALEN from pg_config_manual.h\n"; my $namedatalen = $1; my $pgauthid = read_file("src/include/catalog/pg_authid.h"); - $pgauthid =~ /^#define\s+BOOTSTRAP_SUPERUSERID\s+(\d+)\s*$/mg + $pgauthid =~ /^#define\s+BOOTSTRAP_SUPERUSERID\s+(\d+)$/mg || die "Could not read BOOTSTRAUP_SUPERUSERID from pg_authid.h\n"; my $bootstrapsuperuserid = $1; my $pgnamespace = read_file("src/include/catalog/pg_namespace.h"); - $pgnamespace =~ /^#define\s+PG_CATALOG_NAMESPACE\s+(\d+)\s*$/mg + $pgnamespace =~ /^#define\s+PG_CATALOG_NAMESPACE\s+(\d+)$/mg || die "Could not read PG_CATALOG_NAMESPACE from pg_namespace.h\n"; my $pgcatalognamespace = $1; - $pgnamespace =~ /^#define\s+PG_TOAST_NAMESPACE\s+(\d+)\s*$/mg - || die "Could not read PG_TOAST_NAMESPACE from pg_namespace.h\n"; - my $pgtoastnamespace = $1; - my $indata = ""; while (@_) @@ -78,9 +72,6 @@ sub genbki $indata =~ s{PGUID}{$bootstrapsuperuserid}g; $indata =~ s{NAMEDATALEN}{$namedatalen}g; $indata =~ s{PGNSP}{$pgcatalognamespace}g; - $indata =~ s{TOASTNSP}{$pgtoastnamespace}g; - - #print $indata; my $bki = ""; my $desc = ""; diff --git a/src/tools/msvc/Install.pm b/src/tools/msvc/Install.pm index fd5c80622354..fd94985450d2 100755 --- a/src/tools/msvc/Install.pm +++ b/src/tools/msvc/Install.pm @@ -93,10 +93,17 @@ sub Install ); GenerateConversionScript($target); GenerateTimezoneFiles($target,$conf); + GenerateTsearchFiles($target); + CopySetOfFiles('Stopword files', + [ glob ("src\\backend\\snowball\\stopwords\\*.stop") ], + $target . '/share/tsearch_data/'); + CopySetOfFiles('Dictionaries sample files', + [ glob ("src\\backend\\tsearch\\*_sample.*" ) ], + $target . '/share/tsearch_data/'); CopyContribFiles($config,$target); CopyIncludeFiles($target); - GenerateNLSFiles($target,$config->{nls},$majorver) if ($config->{nls}); + GenerateNLSFiles($target,$config->{nls}) if ($config->{nls}); print "Installation complete.\n"; } @@ -182,8 +189,8 @@ sub CopySolutionOutput # Static lib, such as libpgport, only used internally during build, don't install next; } - lcopy("$conf\\$pf\\$pf.$ext","$target\\$dir\\$pf.$ext") || carp "Could not copy $conf\\$pf\\$pf.$ext to $target\\$dir\\$pf.$ext\n"; - lcopy("$conf\\$pf\\$pf.pdb","$target\\symbols\\$pf.pdb") || carp "Could not copy $pf.pdb\n"; + lcopy("$conf\\$pf\\$pf.$ext","$target\\$dir\\$pf.$ext") || croak "Could not copy $pf.$ext\n"; + lcopy("$conf\\$pf\\$pf.pdb","$target\\symbols\\$pf.pdb") || croak "Could not copy $pf.pdb\n"; print "."; } print "\n"; @@ -311,7 +318,21 @@ sub CopyContribFiles foreach my $f (split /\s+/,$flist) { lcopy('contrib/' . $d . '/' . $f,$target . '/share/contrib/' . basename($f)) - || carp("Could not copy file $f in contrib $d"); + || croak("Could not copy file $f in contrib $d"); + print '.'; + } + } + + $flist = ''; + if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) {$flist .= $1} + if ($flist ne '') + { + $flist = ParseAndCleanRule($flist, $mf); + + foreach my $f (split /\s+/,$flist) + { + lcopy('contrib/' . $d . '/' . $f,$target . '/share/tsearch_data/' . basename($f)) + || croak("Could not copy file $f in contrib $d"); print '.'; } } @@ -329,7 +350,7 @@ sub CopyContribFiles foreach my $f (split /\s+/,$flist) { lcopy('contrib/' . $d . '/' . $f, $target . '/doc/contrib/' . $f) - || carp("Could not copy file $f in contrib $d"); + || croak("Could not copy file $f in contrib $d"); print '.'; } } @@ -373,7 +394,7 @@ sub CopyIncludeFiles 'src/include/', 'postgres_ext.h', 'pg_config.h', 'pg_config_os.h', 'pg_config_manual.h' ); lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/') - || carp 'Could not copy libpq-fs.h'; + || croak 'Could not copy libpq-fs.h'; CopyFiles('Libpq headers', $target . '/include/', 'src/interfaces/libpq/', @@ -389,8 +410,8 @@ sub CopyIncludeFiles $target . '/include/internal/', 'src/include/', 'c.h', 'port.h', 'postgres_fe.h' ); - lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/') - || carp 'Could not copy pqcomm.h'; + lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/') + || croak 'Could not copy pqcomm.h'; CopyFiles( 'Server headers', @@ -403,15 +424,16 @@ sub CopyIncludeFiles my $D; opendir($D, 'src/include') || croak "Could not opendir on src/include!\n"; - while (my $d = readdir($D)) + # some xcopy progs don't like mixed slash style paths + (my $ctarget = $target) =~ s!/!\\!g; + while (my $d = readdir($D)) { next if ($d =~ /^\./); next if ($d eq 'CVS'); - next unless (-d 'src/include/' . $d); + next unless (-d "src/include/$d"); - EnsureDirectories($target . '/include/server', $d); - system( - "xcopy /s /i /q /r /y src\\include\\$d\\*.h \"$target\\include\\server\\$d\\\"") + EnsureDirectories("$target/include/server/$d"); + system(qq{xcopy /s /i /q /r /y src\\include\\$d\\*.h "$ctarget\\include\\server\\$d\\"}) && croak("Failed to copy include directory $d\n"); } closedir($D); @@ -444,17 +466,18 @@ sub GenerateNLSFiles print "Installing NLS files..."; EnsureDirectories($target, "share/locale"); my @flist; - File::Find::find({wanted => - sub { /^nls\.mk\z/s && - ! push(@flist, $File::Find::name); - } + File::Find::find({wanted => + sub { /^nls\.mk\z/s && + !push(@flist, $File::Find::name); + } }, "src"); foreach (@flist) { - my $prgm = DetermineCatalogName($_); s/nls.mk/po/; my $dir = $_; next unless ($dir =~ /([^\/]+)\/po$/); + my $prgm = $1; + $prgm = 'postgres' if ($prgm eq 'backend'); foreach (glob("$dir/*.po")) { my $lang; diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 81658ce2e456..3ef70ba60ecc 100755 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -13,6 +13,8 @@ use Project; use Solution; use Cwd; use File::Copy; +use Config; +use List::Util qw(first); use Exporter; our (@ISA, @EXPORT_OK); @@ -47,7 +49,7 @@ sub mkvcbuild our @pgportfiles = qw( chklocale.c crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c - getaddrinfo.c gettimeofday.c kill.c open.c rand.c + unsetenv.c getaddrinfo.c gettimeofday.c kill.c open.c rand.c snprintf.c strlcat.c strlcpy.c copydir.c dirmod.c exec.c noblock.c path.c pipe.c pgsleep.c pgstrcasecmp.c qsort.c qsort_arg.c sprompt.c thread.c getopt.c getopt_long.c dirent.c rint.c win32env.c win32error.c glob.c); @@ -81,6 +83,13 @@ sub mkvcbuild $postgres->FullExportDLL('postgres.lib'); + my $snowball = $solution->AddProject('dict_snowball','dll','','src\backend\snowball'); + $snowball->RelocateFiles('src\backend\snowball\libstemmer', sub { + return shift !~ /dict_snowball.c$/; + }); + $snowball->AddIncludeDir('src\include\snowball'); + $snowball->AddReference($postgres); + my $plpgsql = $solution->AddProject('plpgsql','dll','PLs','src\pl\plpgsql\src'); $plpgsql->AddFiles('src\pl\plpgsql\src','scan.l','gram.y'); $plpgsql->AddReference($postgres); diff --git a/src/tools/msvc/Project.pm b/src/tools/msvc/Project.pm index a9323941995d..9519d508e923 100755 --- a/src/tools/msvc/Project.pm +++ b/src/tools/msvc/Project.pm @@ -306,7 +306,7 @@ sub AddResourceFile my ($self, $dir, $desc, $ico) = @_; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); - my $d = ($year - 100) . "$yday"; + my $d = sprintf("%02d%03d", ($year - 100), $yday); if (Solution::IsNewer("$dir\\win32ver.rc",'src\port\win32ver.rc')) { diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index 282987e7db17..11db734b2704 100755 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -100,6 +100,7 @@ sub DetermineToolVersions print "Detected hardware platform: $self->{platform}\n"; } + # Return 1 if $oldfile is newer than $newfile, or if $newfile doesn't exist. # Special case - if config.pl has changed, always return 1 sub IsNewer diff --git a/src/tools/msvc/build.pl b/src/tools/msvc/build.pl index 66b5c4c59baf..eb19a380ec00 100755 --- a/src/tools/msvc/build.pl +++ b/src/tools/msvc/build.pl @@ -28,7 +28,6 @@ BEGIN require "./buildenv.pl"; } -# set up the project our $config; require "config_default.pl"; require "config.pl" if (-f "src/tools/msvc/config.pl"); diff --git a/src/tools/msvc/builddoc.bat b/src/tools/msvc/builddoc.bat index 7268ae207f4c..2bd9cc65f208 100755 --- a/src/tools/msvc/builddoc.bat +++ b/src/tools/msvc/builddoc.bat @@ -12,7 +12,7 @@ IF EXIST ..\msvc IF EXIST ..\..\..\src cd ..\..\.. IF NOT EXIST doc\src\sgml\version.sgml goto noversion IF NOT EXIST src\tools\msvc\buildenv.pl goto nobuildenv -perl -e "require 'src/tools/msvc/buildenv.pl'; while(($k,$v) = each %ENV) { print qq[\@SET $k=$v\n]; }" > bldenv.bat +perl -e "require 'src/tools/msvc/buildenv.pl'; while(($k,$v) = each %%ENV) { print qq[\@SET $k=$v\n]; }" > bldenv.bat CALL bldenv.bat del bldenv.bat :nobuildenv diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat index 7995a19630e5..c001a07489c7 100755 --- a/src/tools/msvc/clean.bat +++ b/src/tools/msvc/clean.bat @@ -9,21 +9,8 @@ if exist ..\msvc if exist ..\..\..\src cd ..\..\.. if exist debug rd /s /q debug if exist release rd /s /q release -if exist _UpgradeReport_Files rd /s /q _UpgradeReport_Files -if exist backup rd /s /q backup -if exist ipch rd /s /q ipch for %%f in (*.vcproj) do del %%f -REM Also clean Visual Studio 2010 files -for %%f in (*.vcxproj) do del %%f -for %%f in (*.vcxproj.filters) do del %%f -for %%f in (*.user) do del %%f -for %%f in (UpgradeLog*.XML) do del %%f if exist pgsql.sln del /q pgsql.sln -if exist pgsql.sln.cache del /q pgsql.sln.cache -if exist pgsql.ncb del /q pgsql.ncb -if exist pgsql.sdf del /q pgsql.sdf -if exist pgsql.vssscc del /q pgsql.vssscc -if exist MSSCCPRJ.SCC del /q MSSCCPRJ.SCC del /s /q src\bin\win32ver.rc 2> NUL del /s /q src\interfaces\win32ver.rc 2> NUL if exist src\backend\win32ver.rc del /q src\backend\win32ver.rc @@ -31,21 +18,18 @@ if exist src\backend\win32ver.rc del /q src\backend\win32ver.rc REM Delete files created with GenerateFiles() in Solution.pm if exist src\include\pg_config.h del /q src\include\pg_config.h if exist src\include\pg_config_os.h del /q src\include\pg_config_os.h -if %DIST%==1 if exist src\backend\parser\gram.h del /q src\backend\parser\gram.h +if %DIST%==1 if exist src\backend\parser\parse.h del /q src\backend\parser\parse.h if exist src\include\utils\fmgroids.h del /q src\include\utils\fmgroids.h -if exist src\include\utils\probes.h del /q src\include\utils\probes.h -if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h if exist src\backend\utils\fmgrtab.c del /q src\backend\utils\fmgrtab.c if exist src\backend\catalog\postgres.bki del /q src\backend\catalog\postgres.bki -if %DIST%==1 if exist src\backend\catalog\postgres.description del /q src\backend\catalog\postgres.description if exist src\backend\catalog\postgres.description del /q src\backend\catalog\postgres.description if exist src\backend\catalog\postgres.shdescription del /q src\backend\catalog\postgres.shdescription -if %DIST%==1 if exist src\backend\catalog\schemapg.h del /q src\backend\catalog\schemapg.h if %DIST%==1 if exist src\backend\parser\scan.c del /q src\backend\parser\scan.c if %DIST%==1 if exist src\backend\parser\gram.c del /q src\backend\parser\gram.c if %DIST%==1 if exist src\backend\bootstrap\bootscanner.c del /q src\backend\bootstrap\bootscanner.c if %DIST%==1 if exist src\backend\bootstrap\bootparse.c del /q src\backend\bootstrap\bootparse.c +if %DIST%==1 if exist src\backend\bootstrap\bootstrap_tokens.h del /q src\backend\bootstrap\bootstrap_tokens.h if %DIST%==1 if exist src\backend\utils\misc\guc-file.c del /q src\backend\utils\misc\guc-file.c @@ -67,13 +51,16 @@ if exist src\pl\plperl\spi.c del /q src\pl\plperl\spi.c if %DIST%==1 if exist src\pl\plpgsql\src\pl_scan.c del /q src\pl\plpgsql\src\pl_scan.c if %DIST%==1 if exist src\pl\plpgsql\src\pl_gram.c del /q src\pl\plpgsql\src\pl_gram.c if %DIST%==1 if exist src\pl\plpgsql\src\pl_gram.h del /q src\pl\plpgsql\src\pl_gram.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl.tab.h del /q src\pl\plpgsql\src\pl.tab.h if %DIST%==1 if exist src\bin\psql\psqlscan.c del /q src\bin\psql\psqlscan.c if %DIST%==1 if exist contrib\cube\cubescan.c del /q contrib\cube\cubescan.c if %DIST%==1 if exist contrib\cube\cubeparse.c del /q contrib\cube\cubeparse.c +if %DIST%==1 if exist contrib\cube\cubeparse.h del /q contrib\cube\cubeparse.h if %DIST%==1 if exist contrib\seg\segscan.c del /q contrib\seg\segscan.c if %DIST%==1 if exist contrib\seg\segparse.c del /q contrib\seg\segparse.c +if %DIST%==1 if exist contrib\seg\segparse.h del /q contrib\seg\segparse.h if exist src\test\regress\tmp_check rd /s /q src\test\regress\tmp_check if exist contrib\spi\refint.dll del /q contrib\spi\refint.dll diff --git a/src/tools/msvc/gendef.pl b/src/tools/msvc/gendef.pl index b8538dd79b8b..db24468fe556 100755 --- a/src/tools/msvc/gendef.pl +++ b/src/tools/msvc/gendef.pl @@ -1,4 +1,9 @@ my @def; +# +# Script that generates a .DEF file for all objects in a directory +# +# $PostgreSQL: pgsql/src/tools/msvc/gendef.pl,v 1.8 2008/01/31 16:30:24 adunstan Exp $ +# # # Script that generates a .DEF file for all objects in a directory diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 3cf331bfe5cf..813b80c77ade 100755 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -180,6 +180,7 @@ sub contribcheck my $mstat = 0; foreach my $module (glob("*")) { + next if ($module eq 'xml2' && ! $config->{xml}); next unless -d "$module/sql" && -d "$module/expected" && (-f "$module/Makefile" || -f "$module/GNUmakefile"); diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index 05f69ef1f3a8..22ea5d51f484 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -26,6 +26,14 @@ then INDENT=indent fi +if [ "$#" -lt 2 ] +then echo "Usage: `basename $0` typedefs file [...]" 1>&2 + exit 1 +fi + +TYPEDEFS="$1" +shift + trap "rm -f /tmp/$$ /tmp/$$a" 0 1 2 3 15 entab /dev/null if [ "$?" -ne 0 ] diff --git a/src/tools/version_stamp.pl b/src/tools/version_stamp.pl index dd50a43fda45..b9be30f63b16 100644 --- a/src/tools/version_stamp.pl +++ b/src/tools/version_stamp.pl @@ -23,7 +23,7 @@ # Major version is hard-wired into the script. We update it when we branch # a new development version. $major1 = 8; -$major2 = 4; +$major2 = 3; # Validate argument and compute derived variables $minor = shift; diff --git a/src/tutorial/syscat.source b/src/tutorial/syscat.source index f093adda8edc..65617d4a45f9 100755 --- a/src/tutorial/syscat.source +++ b/src/tutorial/syscat.source @@ -4,10 +4,10 @@ -- sample queries to the system catalogs -- -- --- Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group +-- Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group -- Portions Copyright (c) 1994, Regents of the University of California -- --- $PostgreSQL: pgsql/src/tutorial/syscat.source,v 1.18 2007/01/05 22:20:05 momjian Exp $ +-- $PostgreSQL: pgsql/src/tutorial/syscat.source,v 1.19 2008/01/01 19:46:01 momjian Exp $ -- ---------------------------------------------------------------------------