diff --git a/configure b/configure index 20ee31aab871..d8c69dcd61c4 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for Greenplum Database 7.0.0-alpha.0. +# Generated by GNU Autoconf 2.69 for Greenplum Database 8.0.0-alpha.0. # # Report bugs to . # @@ -582,8 +582,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Greenplum Database' PACKAGE_TARNAME='greenplum-database' -PACKAGE_VERSION='7.0.0-alpha.0' -PACKAGE_STRING='Greenplum Database 7.0.0-alpha.0' +PACKAGE_VERSION='8.0.0-alpha.0' +PACKAGE_STRING='Greenplum Database 8.0.0-alpha.0' PACKAGE_BUGREPORT='support@greenplum.org' PACKAGE_URL='' @@ -673,6 +673,8 @@ PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CC ax_pthread_config +EGREP +GREP SED ZIC python_additional_libs @@ -712,9 +714,6 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX -ELF_SYS -EGREP -GREP with_apr_config with_libcurl with_rt @@ -1481,7 +1480,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 7.0.0-alpha.0 to adapt to many kinds of systems. +\`configure' configures Greenplum Database 8.0.0-alpha.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1547,7 +1546,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Greenplum Database 7.0.0-alpha.0:";; + short | recursive ) echo "Configuration of Greenplum Database 8.0.0-alpha.0:";; esac cat <<\_ACEOF @@ -1729,7 +1728,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Greenplum Database configure 7.0.0-alpha.0 +Greenplum Database configure 8.0.0-alpha.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2482,7 +2481,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 7.0.0-alpha.0, which was +It was created by Greenplum Database $as_me 8.0.0-alpha.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2830,7 +2829,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu -PG_PACKAGE_VERSION=12beta2 +PG_PACKAGE_VERSION=13alpha0 @@ -9570,170 +9569,6 @@ fi - -# -# Elf -# - -# Assume system is ELF if it predefines __ELF__ as 1, -# otherwise believe host_os based default. -case $host_os in - freebsd1*|freebsd2*) elf=no;; - freebsd3*|freebsd4*) elf=yes;; -esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#if __ELF__ - yes -#endif - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "yes" >/dev/null 2>&1; then : - ELF_SYS=true -else - if test "X$elf" = "Xyes" ; then - ELF_SYS=true -else - ELF_SYS= -fi -fi -rm -f conftest* - - - # # Assignments # @@ -11325,6 +11160,137 @@ $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : @@ -22526,7 +22492,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 7.0.0-alpha.0, which was +This file was extended by Greenplum Database $as_me 8.0.0-alpha.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -22596,7 +22562,7 @@ _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 7.0.0-alpha.0 +Greenplum Database config.status 8.0.0-alpha.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.in b/configure.in index af8265ba2710..2d04c9462380 100644 --- a/configure.in +++ b/configure.in @@ -20,8 +20,8 @@ m4_pattern_forbid(^PGAC_)dnl to catch undefined macros dnl The PACKAGE_VERSION from upstream PostgreSQL is maintained in the dnl PG_PACKAGE_VERSION variable, when merging make sure to update this dnl variable with the merge conflict from the AC_INIT() statement. -AC_INIT([Greenplum Database], [7.0.0-alpha.0], [support@greenplum.org]) -[PG_PACKAGE_VERSION=12beta2] +AC_INIT([Greenplum Database], [8.0.0-alpha.0], [support@greenplum.org]) +[PG_PACKAGE_VERSION=13alpha0] AC_SUBST(PG_PACKAGE_VERSION) dnl m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.69], [], [m4_fatal([Autoconf version 2.69 is required. @@ -1155,30 +1155,6 @@ PGAC_ARG_REQ(with, apr-config, [PATH], [path to apr-1-config utility]) AC_SUBST(with_apr_config) -# -# Elf -# - -# Assume system is ELF if it predefines __ELF__ as 1, -# otherwise believe host_os based default. -case $host_os in - freebsd1*|freebsd2*) elf=no;; - freebsd3*|freebsd4*) elf=yes;; -esac - -AC_EGREP_CPP(yes, -[#if __ELF__ - yes -#endif -], -[ELF_SYS=true], -[if test "X$elf" = "Xyes" ; then - ELF_SYS=true -else - ELF_SYS= -fi]) -AC_SUBST(ELF_SYS) - # # Assignments # diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c index 9126c18d0e17..55a3a4bbe0c2 100644 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@ -377,11 +377,20 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace, if (state->heapallindexed) { + int64 total_pages; int64 total_elems; uint64 seed; - /* Size Bloom filter based on estimated number of tuples in index */ - total_elems = (int64) state->rel->rd_rel->reltuples; + /* + * Size Bloom filter based on estimated number of tuples in index, + * while conservatively assuming that each block must contain at least + * MaxIndexTuplesPerPage / 5 non-pivot tuples. (Non-leaf pages cannot + * contain non-pivot tuples. That's okay because they generally make + * up no more than about 1% of all pages in the index.) + */ + total_pages = RelationGetNumberOfBlocks(rel); + total_elems = Max(total_pages * (MaxIndexTuplesPerPage / 5), + (int64) state->rel->rd_rel->reltuples); /* Random seed relies on backend srandom() call to avoid repetition */ seed = random(); /* Create Bloom filter to fingerprint index */ @@ -425,8 +434,6 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace, } else { - int64 total_pages; - /* * Extra readonly downlink check. * @@ -437,7 +444,6 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace, * splits and page deletions, though. This is taken care of in * bt_downlink_missing_check(). */ - total_pages = (int64) state->rel->rd_rel->relpages; state->downlinkfilter = bloom_create(total_pages, work_mem, seed); } } diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c index 2be0055c3fbb..b72036687608 100644 --- a/contrib/cube/cube.c +++ b/contrib/cube/cube.c @@ -100,7 +100,7 @@ bool g_cube_leaf_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy); bool g_cube_internal_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy); /* -** Auxiliary funxtions +** Auxiliary functions */ static double distance_1D(double a1, double a2, double b1, double b2); static bool cube_is_point_internal(NDBOX *cube); @@ -590,7 +590,7 @@ g_cube_picksplit(PG_FUNCTION_ARGS) v->spl_nright++; } } - *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */ + *left = *right = FirstOffsetNumber; /* sentinel value */ v->spl_ldatum = PointerGetDatum(datum_l); v->spl_rdatum = PointerGetDatum(datum_r); diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index f4447adabd41..909c31a9a4ee 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -361,8 +361,7 @@ fileGetOptions(Oid foreigntableid, ForeignServer *server; ForeignDataWrapper *wrapper; List *options; - ListCell *lc, - *prev; + ListCell *lc; /* * Extract options from FDW objects. We ignore user mappings because @@ -388,7 +387,6 @@ fileGetOptions(Oid foreigntableid, */ *filename = NULL; *is_program = false; - prev = NULL; foreach(lc, options) { DefElem *def = (DefElem *) lfirst(lc); @@ -396,17 +394,16 @@ fileGetOptions(Oid foreigntableid, if (strcmp(def->defname, "filename") == 0) { *filename = defGetString(def); - options = list_delete_cell(options, lc, prev); + options = foreach_delete_current(options, lc); break; } else if (strcmp(def->defname, "program") == 0) { *filename = defGetString(def); *is_program = true; - options = list_delete_cell(options, lc, prev); + options = foreach_delete_current(options, lc); break; } - prev = lc; } /* diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h index b689eb7dedf1..f03fdf9add92 100644 --- a/contrib/intarray/_int.h +++ b/contrib/intarray/_int.h @@ -85,12 +85,6 @@ typedef struct #define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) ) -/* - * types for functions - */ -typedef ArrayType *(*formarray) (ArrayType *, ArrayType *); -typedef void (*formfloat) (ArrayType *, float *); - /* * useful functions */ diff --git a/contrib/jsonb_plperl/jsonb_plperlu--1.0.sql b/contrib/jsonb_plperl/jsonb_plperlu--1.0.sql index 5a5e475ad30b..aa84b37bef72 100644 --- a/contrib/jsonb_plperl/jsonb_plperlu--1.0.sql +++ b/contrib/jsonb_plperl/jsonb_plperlu--1.0.sql @@ -1,4 +1,4 @@ -/* contrib/json_plperl/jsonb_plperl--1.0.sql */ +/* contrib/jsonb_plperl/jsonb_plperlu--1.0.sql */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION jsonb_plperlu" to load this file. \quit diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c index 56bf39d145b0..b576676746dc 100644 --- a/contrib/ltree/ltxtquery_io.c +++ b/contrib/ltree/ltxtquery_io.c @@ -368,7 +368,7 @@ queryin(char *buf) state.str = tmp; } - /* set user friendly-operand view */ + /* set user-friendly operand view */ memcpy((void *) GETOPERAND(query), (void *) state.op, state.sumlen); pfree(state.op); diff --git a/contrib/passwordcheck/passwordcheck.c b/contrib/passwordcheck/passwordcheck.c index b161e0f14605..c3fb5a9c085a 100644 --- a/contrib/passwordcheck/passwordcheck.c +++ b/contrib/passwordcheck/passwordcheck.c @@ -26,10 +26,14 @@ PG_MODULE_MAGIC; +/* Saved hook value in case of unload */ +static check_password_hook_type prev_check_password_hook = NULL; + /* passwords shorter than this will be rejected */ #define MIN_PWD_LENGTH 8 extern void _PG_init(void); +extern void _PG_fini(void); /* * check_password @@ -55,6 +59,11 @@ check_password(const char *username, Datum validuntil_time, bool validuntil_null) { + if (prev_check_password_hook) + prev_check_password_hook(username, shadow_pass, + password_type, validuntil_time, + validuntil_null); + if (password_type != PASSWORD_TYPE_PLAINTEXT) { /* @@ -133,5 +142,16 @@ void _PG_init(void) { /* activate password checks when the module is loaded */ + prev_check_password_hook = check_password_hook; check_password_hook = check_password; } + +/* + * Module unload function + */ +void +_PG_fini(void) +{ + /* uninstall hook */ + check_password_hook = prev_check_password_hook; +} diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out index c759763be217..6787ec1efdae 100644 --- a/contrib/pg_stat_statements/expected/pg_stat_statements.out +++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out @@ -354,6 +354,93 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; SELECT pg_stat_statements_reset() | 1 | 1 (5 rows) +-- +-- queries with locking clauses +-- +CREATE TABLE pgss_a (id integer PRIMARY KEY); +CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a); +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- control query +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id; + id | id | a_id +----+----+------ +(0 rows) + +-- test range tables +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE; + id | id | a_id +----+----+------ +(0 rows) + +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_a; + id | id | a_id +----+----+------ +(0 rows) + +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b; + id | id | a_id +----+----+------ +(0 rows) + +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_a, pgss_b; -- matches plain "FOR UPDATE" + id | id | a_id +----+----+------ +(0 rows) + +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a; + id | id | a_id +----+----+------ +(0 rows) + +-- test strengths +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR NO KEY UPDATE; + id | id | a_id +----+----+------ +(0 rows) + +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR SHARE; + id | id | a_id +----+----+------ +(0 rows) + +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR KEY SHARE; + id | id | a_id +----+----+------ +(0 rows) + +-- test wait policies +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE NOWAIT; + id | id | a_id +----+----+------ +(0 rows) + +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED; + id | id | a_id +----+----+------ +(0 rows) + +SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + calls | query +-------+------------------------------------------------------------------------------------------ + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR KEY SHARE + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR NO KEY UPDATE + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR SHARE + 2 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE NOWAIT + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_a + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a + 1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED + 1 | SELECT pg_stat_statements_reset() +(11 rows) + +DROP TABLE pgss_a, pgss_b CASCADE; -- -- utility commands -- diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 5b65aca83172..3af9638a22a4 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -333,6 +333,7 @@ static void AppendJumble(pgssJumbleState *jstate, const unsigned char *item, Size size); static void JumbleQuery(pgssJumbleState *jstate, Query *query); static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable); +static void JumbleRowMarks(pgssJumbleState *jstate, List *rowMarks); static void JumbleExpr(pgssJumbleState *jstate, Node *node); static void RecordConstLocation(pgssJumbleState *jstate, int location); static char *generate_normalized_query(pgssJumbleState *jstate, const char *query, @@ -787,7 +788,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query) Assert(query->queryId == UINT64CONST(0)); /* Safety check... */ - if (!pgss || !pgss_hash) + if (!pgss || !pgss_hash || !pgss_enabled()) return; /* @@ -1992,7 +1993,7 @@ qtext_load_file(Size *buffer_size) return NULL; } - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(LOG, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE))); @@ -2430,7 +2431,7 @@ JumbleQuery(pgssJumbleState *jstate, Query *query) JumbleExpr(jstate, (Node *) query->sortClause); JumbleExpr(jstate, query->limitOffset); JumbleExpr(jstate, query->limitCount); - /* we ignore rowMarks */ + JumbleRowMarks(jstate, query->rowMarks); JumbleExpr(jstate, query->setOperations); } @@ -2489,6 +2490,26 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable) } } +/* + * Jumble a rowMarks list + */ +static void +JumbleRowMarks(pgssJumbleState *jstate, List *rowMarks) +{ + ListCell *lc; + + foreach(lc, rowMarks) + { + RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc); + if (!rowmark->pushedDown) + { + APP_JUMB(rowmark->rti); + APP_JUMB(rowmark->strength); + APP_JUMB(rowmark->waitPolicy); + } + } +} + /* * Jumble an expression tree * diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql index aef890d893d8..8b527070d46b 100644 --- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql +++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql @@ -177,6 +177,37 @@ SELECT PLUS_ONE(1); SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; +-- +-- queries with locking clauses +-- +CREATE TABLE pgss_a (id integer PRIMARY KEY); +CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a); + +SELECT pg_stat_statements_reset(); + +-- control query +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id; + +-- test range tables +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE; +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_a; +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b; +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_a, pgss_b; -- matches plain "FOR UPDATE" +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a; + +-- test strengths +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR NO KEY UPDATE; +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR SHARE; +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR KEY SHARE; + +-- test wait policies +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE NOWAIT; +SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED; + +SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + +DROP TABLE pgss_a, pgss_b CASCADE; + -- -- utility commands -- diff --git a/contrib/pg_trgm/trgm_regexp.c b/contrib/pg_trgm/trgm_regexp.c index c239d8cea6e9..3ad5731ae801 100644 --- a/contrib/pg_trgm/trgm_regexp.c +++ b/contrib/pg_trgm/trgm_regexp.c @@ -1013,9 +1013,7 @@ addKey(TrgmNFA *trgmNFA, TrgmState *state, TrgmStateKey *key) { regex_arc_t *arcs; TrgmStateKey destKey; - ListCell *cell, - *prev, - *next; + ListCell *cell; int i, arcsCount; @@ -1030,13 +1028,10 @@ addKey(TrgmNFA *trgmNFA, TrgmState *state, TrgmStateKey *key) * redundancy. We can drop either old key(s) or the new key if we find * redundancy. */ - prev = NULL; - cell = list_head(state->enterKeys); - while (cell) + foreach(cell, state->enterKeys) { TrgmStateKey *existingKey = (TrgmStateKey *) lfirst(cell); - next = lnext(cell); if (existingKey->nstate == key->nstate) { if (prefixContains(&existingKey->prefix, &key->prefix)) @@ -1050,15 +1045,10 @@ addKey(TrgmNFA *trgmNFA, TrgmState *state, TrgmStateKey *key) * The new key covers this old key. Remove the old key, it's * no longer needed once we add this key to the list. */ - state->enterKeys = list_delete_cell(state->enterKeys, - cell, prev); + state->enterKeys = foreach_delete_current(state->enterKeys, + cell); } - else - prev = cell; } - else - prev = cell; - cell = next; } /* No redundancy, so add this key to the state's list */ diff --git a/contrib/pgcrypto/pgcrypto.c b/contrib/pgcrypto/pgcrypto.c index 8ec04fab1c0c..92337ca1e9bc 100644 --- a/contrib/pgcrypto/pgcrypto.c +++ b/contrib/pgcrypto/pgcrypto.c @@ -525,20 +525,8 @@ PG_FUNCTION_INFO_V1(pg_random_uuid); Datum pg_random_uuid(PG_FUNCTION_ARGS) { - uint8 *buf = (uint8 *) palloc(UUID_LEN); - - /* Generate random bits. */ - if (!pg_strong_random(buf, UUID_LEN)) - px_THROW_ERROR(PXE_NO_RANDOM); - - /* - * Set magic numbers for a "version 4" (pseudorandom) UUID, see - * http://tools.ietf.org/html/rfc4122#section-4.4 - */ - buf[6] = (buf[6] & 0x0f) | 0x40; /* "version" field */ - buf[8] = (buf[8] & 0x3f) | 0x80; /* "variant" field */ - - PG_RETURN_UUID_P((pg_uuid_t *) buf); + /* redirect to built-in function */ + return gen_random_uuid(fcinfo); } static void * diff --git a/contrib/pgcrypto/pgp-compress.c b/contrib/pgcrypto/pgp-compress.c index eaeb221877f9..2adaf2c8755f 100644 --- a/contrib/pgcrypto/pgp-compress.c +++ b/contrib/pgcrypto/pgp-compress.c @@ -311,7 +311,7 @@ pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src) { return pullf_create(res, &decompress_filter, ctx, src); } -#else /* !HAVE_ZLIB */ +#else /* !HAVE_LIBZ */ int pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst) diff --git a/contrib/pgcrypto/pgp-decrypt.c b/contrib/pgcrypto/pgp-decrypt.c index 7d8951cda416..eed0f6ad2d5f 100644 --- a/contrib/pgcrypto/pgp-decrypt.c +++ b/contrib/pgcrypto/pgp-decrypt.c @@ -355,7 +355,7 @@ mdc_finish(PGP_Context *ctx, PullFilter *src, int len) if (len != 20) return PXE_PGP_CORRUPT_DATA; - /* mdc_read should not call md_update */ + /* mdc_read should not call px_md_update */ ctx->in_mdc_pkt = 1; /* read data */ diff --git a/contrib/pgcrypto/pgp.c b/contrib/pgcrypto/pgp.c index 716e267b7836..11c483369949 100644 --- a/contrib/pgcrypto/pgp.c +++ b/contrib/pgcrypto/pgp.c @@ -54,7 +54,6 @@ struct digest_info { const char *name; int code; - const char *int_name; }; struct cipher_info diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c index 2c807537266c..4bae176e09eb 100644 --- a/contrib/pgstattuple/pgstatindex.c +++ b/contrib/pgstattuple/pgstatindex.c @@ -727,7 +727,7 @@ pgstathashindex(PG_FUNCTION_ARGS) } /* ------------------------------------------------- - * GetHashPageStatis() + * GetHashPageStats() * * Collect statistics of single hash page * ------------------------------------------------- diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 6da4c834bf8c..19f928ec598d 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -1531,7 +1531,7 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, { Assert(fpinfo->jointype == JOIN_INNER); Assert(fpinfo->joinclauses == NIL); - appendStringInfo(buf, "%s", join_sql_o.data); + appendBinaryStringInfo(buf, join_sql_o.data, join_sql_o.len); return; } } @@ -1552,7 +1552,7 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, { Assert(fpinfo->jointype == JOIN_INNER); Assert(fpinfo->joinclauses == NIL); - appendStringInfo(buf, "%s", join_sql_i.data); + appendBinaryStringInfo(buf, join_sql_i.data, join_sql_i.len); return; } } @@ -1861,7 +1861,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root, { List *ignore_conds = NIL; - appendStringInfo(buf, " FROM "); + appendStringInfoString(buf, " FROM "); deparseFromExprForRel(buf, root, foreignrel, true, rtindex, &ignore_conds, params_list); remote_conds = list_concat(remote_conds, ignore_conds); @@ -1944,7 +1944,7 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root, { List *ignore_conds = NIL; - appendStringInfo(buf, " USING "); + appendStringInfoString(buf, " USING "); deparseFromExprForRel(buf, root, foreignrel, true, rtindex, &ignore_conds, params_list); remote_conds = list_concat(remote_conds, ignore_conds); @@ -2610,7 +2610,7 @@ deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context) { deparseExpr(lfirst(lowlist_item), context); appendStringInfoChar(buf, ':'); - lowlist_item = lnext(lowlist_item); + lowlist_item = lnext(node->reflowerindexpr, lowlist_item); } deparseExpr(lfirst(uplist_item), context); appendStringInfoChar(buf, ']'); @@ -2673,7 +2673,7 @@ deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context) { if (!first) appendStringInfoString(buf, ", "); - if (use_variadic && lnext(arg) == NULL) + if (use_variadic && lnext(node->args, arg) == NULL) appendStringInfoString(buf, "VARIADIC "); deparseExpr((Expr *) lfirst(arg), context); first = false; @@ -3001,7 +3001,7 @@ deparseAggref(Aggref *node, deparse_expr_cxt *context) first = false; /* Add VARIADIC */ - if (use_variadic && lnext(arg) == NULL) + if (use_variadic && lnext(node->args, arg) == NULL) appendStringInfoString(buf, "VARIADIC "); deparseExpr((Expr *) n, context); diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 76cd3dc792c3..fce077ee33b9 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -4505,20 +4505,51 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel, /* In what follows, do not risk leaking any PGresults. */ PG_TRY(); { + char fetch_sql[64]; + int fetch_size; + ListCell *lc; + res = pgfdw_exec_query(conn, sql.data); if (PQresultStatus(res) != PGRES_COMMAND_OK) pgfdw_report_error(ERROR, res, conn, false, sql.data); PQclear(res); res = NULL; + /* + * Determine the fetch size. The default is arbitrary, but shouldn't + * be enormous. + */ + fetch_size = 100; + foreach(lc, server->options) + { + DefElem *def = (DefElem *) lfirst(lc); + + if (strcmp(def->defname, "fetch_size") == 0) + { + fetch_size = strtol(defGetString(def), NULL, 10); + break; + } + } + foreach(lc, table->options) + { + DefElem *def = (DefElem *) lfirst(lc); + + if (strcmp(def->defname, "fetch_size") == 0) + { + fetch_size = strtol(defGetString(def), NULL, 10); + break; + } + } + + /* Construct command to fetch rows from remote. */ + snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u", + fetch_size, cursor_number); + /* Retrieve and process rows a batch at a time. */ for (;;) { - char fetch_sql[64]; - int fetch_size; int numrows; int i; - ListCell *lc; /* Allow users to cancel long query */ CHECK_FOR_INTERRUPTS(); @@ -4529,33 +4560,7 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel, * then just adjust rowstoskip and samplerows appropriately. */ - /* The fetch size is arbitrary, but shouldn't be enormous. */ - fetch_size = 100; - foreach(lc, server->options) - { - DefElem *def = (DefElem *) lfirst(lc); - - if (strcmp(def->defname, "fetch_size") == 0) - { - fetch_size = strtol(defGetString(def), NULL, 10); - break; - } - } - foreach(lc, table->options) - { - DefElem *def = (DefElem *) lfirst(lc); - - if (strcmp(def->defname, "fetch_size") == 0) - { - fetch_size = strtol(defGetString(def), NULL, 10); - break; - } - } - /* Fetch some rows */ - snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u", - fetch_size, cursor_number); - res = pgfdw_exec_query(conn, fetch_sql); /* On error, report the original query, not the FETCH. */ if (PQresultStatus(res) != PGRES_TUPLES_OK) diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c index 0cffb69c76be..8edd3df643c7 100644 --- a/contrib/sepgsql/database.c +++ b/contrib/sepgsql/database.c @@ -63,7 +63,7 @@ sepgsql_database_post_create(Oid databaseId, const char *dtemplate) * check db_database:{getattr} permission */ initStringInfo(&audit_name); - appendStringInfo(&audit_name, "%s", quote_identifier(dtemplate)); + appendStringInfoString(&audit_name, quote_identifier(dtemplate)); sepgsql_avc_check_perms_label(tcontext, SEPG_CLASS_DB_DATABASE, SEPG_DB_DATABASE__GETATTR, @@ -101,8 +101,8 @@ sepgsql_database_post_create(Oid databaseId, const char *dtemplate) * check db_database:{create} permission */ resetStringInfo(&audit_name); - appendStringInfo(&audit_name, "%s", - quote_identifier(NameStr(datForm->datname))); + appendStringInfoString(&audit_name, + quote_identifier(NameStr(datForm->datname))); sepgsql_avc_check_perms_label(ncontext, SEPG_CLASS_DB_DATABASE, SEPG_DB_DATABASE__CREATE, diff --git a/contrib/sepgsql/expected/misc.out b/contrib/sepgsql/expected/misc.out index f37d98154fe2..b2c01e03dedf 100644 --- a/contrib/sepgsql/expected/misc.out +++ b/contrib/sepgsql/expected/misc.out @@ -81,11 +81,11 @@ LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_re SELECT MIN(x), AVG(x) FROM t1; LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="column x of table t1" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.avg(integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4_avg_accum(bigint[],integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int8_avg(bigint[])" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" min | avg -----+--------------------- 1 | 50.5000000000000000 @@ -98,11 +98,11 @@ LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_reg LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="column o of table t1p_ones" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_tens" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="column o of table t1p_tens" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.avg(integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4_avg_accum(bigint[],integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int8_avg(bigint[])" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" min | avg -----+--------------------- 0 | 49.5000000000000000 @@ -111,11 +111,11 @@ LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_re SELECT MIN(o), AVG(o) FROM t1p_ones; LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_ones" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="column o of table t1p_ones" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.avg(integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4_avg_accum(bigint[],integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int8_avg(bigint[])" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" min | avg -----+-------------------- 0 | 4.5000000000000000 @@ -124,11 +124,11 @@ LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_re SELECT MIN(o), AVG(o) FROM t1p_tens; LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_tens" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="column o of table t1p_tens" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.avg(integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4_avg_accum(bigint[],integer)" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int8_avg(bigint[])" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" min | avg -----+--------------------- 10 | 54.5000000000000000 diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c index 0b0d21c7f5d2..2b6a41509316 100644 --- a/contrib/sepgsql/label.c +++ b/contrib/sepgsql/label.c @@ -207,23 +207,16 @@ sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { ListCell *cell; - ListCell *prev; - ListCell *next; if (event == SUBXACT_EVENT_ABORT_SUB) { - prev = NULL; - for (cell = list_head(client_label_pending); cell; cell = next) + foreach(cell, client_label_pending) { pending_label *plabel = lfirst(cell); - next = lnext(cell); - if (plabel->subid == mySubid) client_label_pending - = list_delete_cell(client_label_pending, cell, prev); - else - prev = cell; + = foreach_delete_current(client_label_pending, cell); } } } @@ -676,7 +669,7 @@ quote_object_name(const char *src1, const char *src2, if (src1) { temp = quote_identifier(src1); - appendStringInfo(&result, "%s", temp); + appendStringInfoString(&result, temp); if (src1 != temp) pfree((void *) temp); } diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c index fbc044e818c9..bc2be8427af1 100644 --- a/contrib/sepgsql/selinux.c +++ b/contrib/sepgsql/selinux.c @@ -702,7 +702,7 @@ sepgsql_audit_log(bool denied, appendStringInfo(&buf, " %s", av_name); } } - appendStringInfo(&buf, " }"); + appendStringInfoString(&buf, " }"); /* * Call external audit module, if loaded diff --git a/contrib/sepgsql/sepgsql-regtest.te b/contrib/sepgsql/sepgsql-regtest.te index e5d65243e6be..5d9af1a0ddb4 100644 --- a/contrib/sepgsql/sepgsql-regtest.te +++ b/contrib/sepgsql/sepgsql-regtest.te @@ -31,6 +31,9 @@ userdom_base_user_template(sepgsql_regtest_superuser) userdom_manage_home_role(sepgsql_regtest_superuser_r, sepgsql_regtest_superuser_t) userdom_exec_user_home_content_files(sepgsql_regtest_superuser_t) userdom_write_user_tmp_sockets(sepgsql_regtest_superuser_t) + +auth_read_passwd(sepgsql_regtest_superuser_t) + optional_policy(` postgresql_stream_connect(sepgsql_regtest_superuser_t) postgresql_unconfined(sepgsql_regtest_superuser_t) @@ -60,6 +63,9 @@ userdom_base_user_template(sepgsql_regtest_dba) userdom_manage_home_role(sepgsql_regtest_dba_r, sepgsql_regtest_dba_t) userdom_exec_user_home_content_files(sepgsql_regtest_dba_t) userdom_write_user_tmp_sockets(sepgsql_regtest_user_t) + +auth_read_passwd(sepgsql_regtest_dba_t) + optional_policy(` postgresql_admin(sepgsql_regtest_dba_t, sepgsql_regtest_dba_r) postgresql_stream_connect(sepgsql_regtest_dba_t) @@ -98,6 +104,9 @@ userdom_base_user_template(sepgsql_regtest_user) userdom_manage_home_role(sepgsql_regtest_user_r, sepgsql_regtest_user_t) userdom_exec_user_home_content_files(sepgsql_regtest_user_t) userdom_write_user_tmp_sockets(sepgsql_regtest_user_t) + +auth_read_passwd(sepgsql_regtest_user_t) + optional_policy(` postgresql_role(sepgsql_regtest_user_r, sepgsql_regtest_user_t) postgresql_stream_connect(sepgsql_regtest_user_t) @@ -126,6 +135,8 @@ userdom_manage_home_role(sepgsql_regtest_pool_r, sepgsql_regtest_pool_t) userdom_exec_user_home_content_files(sepgsql_regtest_pool_t) userdom_write_user_tmp_sockets(sepgsql_regtest_pool_t) +auth_read_passwd(sepgsql_regtest_pool_t) + type sepgsql_regtest_foo_t; type sepgsql_regtest_var_t; type sepgsql_regtest_foo_table_t; diff --git a/contrib/sepgsql/uavc.c b/contrib/sepgsql/uavc.c index 10a73a7eee33..d7200523887f 100644 --- a/contrib/sepgsql/uavc.c +++ b/contrib/sepgsql/uavc.c @@ -92,24 +92,20 @@ static void sepgsql_avc_reclaim(void) { ListCell *cell; - ListCell *next; - ListCell *prev; int index; while (avc_num_caches >= avc_threshold - AVC_NUM_RECLAIM) { index = avc_lru_hint; - prev = NULL; - for (cell = list_head(avc_slots[index]); cell; cell = next) + foreach(cell, avc_slots[index]) { avc_cache *cache = lfirst(cell); - next = lnext(cell); if (!cache->hot_cache) { avc_slots[index] - = list_delete_cell(avc_slots[index], cell, prev); + = foreach_delete_current(avc_slots[index], cell); pfree(cache->scontext); pfree(cache->tcontext); @@ -122,7 +118,6 @@ sepgsql_avc_reclaim(void) else { cache->hot_cache = false; - prev = cell; } } avc_lru_hint = (avc_lru_hint + 1) % AVC_NUM_SLOTS; diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c index 33955a478edb..a0ded67b9aa5 100644 --- a/contrib/test_decoding/test_decoding.c +++ b/contrib/test_decoding/test_decoding.c @@ -518,9 +518,9 @@ pg_decode_truncate(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, || change->data.truncate.cascade) { if (change->data.truncate.restart_seqs) - appendStringInfo(ctx->out, " restart_seqs"); + appendStringInfoString(ctx->out, " restart_seqs"); if (change->data.truncate.cascade) - appendStringInfo(ctx->out, " cascade"); + appendStringInfoString(ctx->out, " cascade"); } else appendStringInfoString(ctx->out, " (no-flags)"); diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml index b27eb6f2aae0..0f11897c9977 100644 --- a/doc/src/sgml/ref/alter_foreign_table.sgml +++ b/doc/src/sgml/ref/alter_foreign_table.sgml @@ -227,8 +227,8 @@ ALTER FOREIGN TABLE [ IF EXISTS ] name Backward compatibility syntax for removing the oid - system column. As oid system columns cannot be added anymore, this never - has an effect. + system column. As oid system columns cannot be added + anymore, this never has an effect. diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml index 0b5c8741ebd8..c02612c2b8a4 100644 --- a/doc/src/sgml/ref/create_aggregate.sgml +++ b/doc/src/sgml/ref/create_aggregate.sgml @@ -224,7 +224,7 @@ CREATE [ OR REPLACE ] AGGREGATE name. This requires specifying the MSFUNC, MINVFUNC, and MSTYPE parameters, and optionally - the MSPACE, MFINALFUNC, + the MSSPACE, MFINALFUNC, MFINALFUNC_EXTRA, MFINALFUNC_MODIFY, and MINITCOND parameters. Except for MINVFUNC, these parameters work like the corresponding simple-aggregate parameters diff --git a/doc/src/sgml/ref/create_conversion.sgml b/doc/src/sgml/ref/create_conversion.sgml index 4ddbcfacef2a..67b4bd26bed3 100644 --- a/doc/src/sgml/ref/create_conversion.sgml +++ b/doc/src/sgml/ref/create_conversion.sgml @@ -28,12 +28,15 @@ CREATE [ DEFAULT ] CONVERSION name CREATE CONVERSION defines a new conversion between - character set encodings. Also, conversions that - are marked DEFAULT can be used for automatic encoding - conversion between - client and server. For this purpose, two conversions, from encoding A to - B and from encoding B to A, must be defined. - + two character set encodings. + + + + Conversions that are marked DEFAULT can be used for + automatic encoding conversion between client and server. To support that + usage, two conversions, from encoding A to B and + from encoding B to A, must be defined. + To be able to create a conversion, you must have EXECUTE privilege @@ -122,6 +125,13 @@ conv_proc( Notes + + Neither the source nor the destination encoding can + be SQL_ASCII, as the server's behavior for cases + involving the SQL_ASCII encoding is + hard-wired. + + Use DROP CONVERSION to remove user-defined conversions. diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index b2c9e241c2f1..4014f6703bba 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -25,6 +25,7 @@ CREATE DATABASE name [ [ WITH ] [ OWNER [=] user_name ] [ TEMPLATE [=] template ] [ ENCODING [=] encoding ] + [ LOCALE [=] locale ] [ LC_COLLATE [=] lc_collate ] [ LC_CTYPE [=] lc_ctype ] [ TABLESPACE [=] tablespace_name ] @@ -111,6 +112,26 @@ CREATE DATABASE name + + locale + + + This is a shortcut for setting LC_COLLATE + and LC_CTYPE at once. If you specify this, + you cannot specify either of those parameters. + + + + The other locale settings , , , and + are not fixed per database and are not + set by this command. If you want to make them the default for a + specific database, you can use ALTER DATABASE + ... SET. + + + + lc_collate @@ -287,7 +308,7 @@ CREATE DATABASE sales OWNER salesapp TABLESPACE salesspace; To create a database music with a different locale: CREATE DATABASE music - LC_COLLATE 'sv_SE.utf8' LC_CTYPE 'sv_SE.utf8' + LOCALE 'sv_SE.utf8' TEMPLATE template0; In this example, the TEMPLATE template0 clause is required if @@ -300,7 +321,7 @@ CREATE DATABASE music different character set encoding: CREATE DATABASE music2 - LC_COLLATE 'sv_SE.iso885915' LC_CTYPE 'sv_SE.iso885915' + LOCALE 'sv_SE.iso885915' ENCODING LATIN9 TEMPLATE template0; diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml index aa79962e42f7..5092408b0fc7 100644 --- a/doc/src/sgml/ref/initdb.sgml +++ b/doc/src/sgml/ref/initdb.sgml @@ -216,11 +216,10 @@ PostgreSQL documentation Use checksums on data pages to help detect corruption by the I/O system that would otherwise be silent. Enabling checksums - may incur a noticeable performance penalty. This option can only - be set during initialization, and cannot be changed later. If - set, checksums are calculated for all objects, in all databases. All - checksum failures will be reported in the view. + may incur a noticeable performance penalty. If set, checksums + are calculated for all objects, in all databases. All checksum + failures will be reported in the + view. diff --git a/doc/src/sgml/ref/pg_checksums.sgml b/doc/src/sgml/ref/pg_checksums.sgml index 33706d1d97ac..162bafdb3313 100644 --- a/doc/src/sgml/ref/pg_checksums.sgml +++ b/doc/src/sgml/ref/pg_checksums.sgml @@ -120,7 +120,7 @@ PostgreSQL documentation to be written safely to disk. This option causes pg_checksums to return without waiting, which is faster, but means that a subsequent operating system crash can leave - the updated data folder corrupt. Generally, this option is useful + the updated data directory corrupt. Generally, this option is useful for testing but should not be used on a production installation. This option has no effect when using --check. diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index 1de124a68717..9d306c66ff02 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -749,7 +749,7 @@ PostgreSQL documentation - Use the specified value of extra_float_digits when dumping + Use the specified value of when dumping floating-point data, instead of the maximum available precision. Routine dumps made for backup purposes should not use this option. diff --git a/doc/src/sgml/ref/pg_receivewal.sgml b/doc/src/sgml/ref/pg_receivewal.sgml index 0506120c00bf..177e9211c08f 100644 --- a/doc/src/sgml/ref/pg_receivewal.sgml +++ b/doc/src/sgml/ref/pg_receivewal.sgml @@ -52,7 +52,17 @@ PostgreSQL documentation Unlike the WAL receiver of a PostgreSQL standby server, pg_receivewal by default flushes WAL data only when a WAL file is closed. The option must be specified to flush WAL data - in real time. + in real time. Since pg_receivewal does not + apply WAL, you should not allow it to become a synchronous standby when + equals + remote_apply. If it does, it will appear to be a + standby that never catches up, and will cause transaction commits to + block. To avoid this, you should either configure an appropriate value + for , or specify + application_name for + pg_receivewal that does not match it, or + change the value of synchronous_commit to + something other than remote_apply. diff --git a/doc/src/sgml/ref/pg_rewind.sgml b/doc/src/sgml/ref/pg_rewind.sgml index b8ada8a97771..878b5a55391c 100644 --- a/doc/src/sgml/ref/pg_rewind.sgml +++ b/doc/src/sgml/ref/pg_rewind.sgml @@ -197,7 +197,7 @@ PostgreSQL documentation to be written safely to disk. This option causes pg_rewind to return without waiting, which is faster, but means that a subsequent operating system crash can leave - the synchronized data folder corrupt. Generally, this option is + the synchronized data directory corrupt. Generally, this option is useful for testing but should not be used when creating a production installation. diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml index e3b73a4cf51d..816f9cc4c7e0 100644 --- a/doc/src/sgml/ref/pgbench.sgml +++ b/doc/src/sgml/ref/pgbench.sgml @@ -355,7 +355,6 @@ pgbench options d - clients clients @@ -474,10 +473,10 @@ pgbench options d - Because in "prepared" mode pgbench reuses - the parse analysis result for the second and subsequent query - iteration, pgbench runs faster in the - prepared mode than in other modes. + In the prepared mode, pgbench + reuses the parse analysis result starting from the second query + iteration, so pgbench runs faster + than in other modes. The default is simple query protocol. (See @@ -617,6 +616,16 @@ pgbench options d + + scriptname + + + Show the actual code of builtin script scriptname + on stderr, and exit immediately. + + + + transactions transactions @@ -843,7 +852,7 @@ pgbench options d The default built-in transaction script (also invoked with ) issues seven commands per transaction over randomly chosen aid, - tid, bid and balance. + tid, bid and delta. The scenario is inspired by the TPC-B benchmark, but is not actually TPC-B, hence the name. diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml index 82886760f108..9ab9c30b97df 100644 --- a/doc/src/sgml/ref/pgupgrade.sgml +++ b/doc/src/sgml/ref/pgupgrade.sgml @@ -89,6 +89,7 @@ bindir bindir the new PostgreSQL executable directory; + default is the directory where pg_upgrade resides; environment variable PGBINNEW @@ -206,8 +207,7 @@ systems. If it is selected but not supported, the pg_upgrade run will error. At present, it is supported on Linux (kernel 4.5 or later) with Btrfs and XFS (on - file systems created with reflink support, which is not the default - for XFS at this writing), and on macOS with APFS. + file systems created with reflink support), and on macOS with APFS. diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index c638189b9477..865257c2be14 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1376,14 +1376,16 @@ testdb=> In this group of commands, the letters E, i, m, s, t, P, and v - stand for foreign table, index, materialized view, sequence, table, parent table, and view, + stand for foreign table, index, materialized view, + sequence, table, parent table, and view, respectively. You can specify any or all of these letters, in any order, to obtain a listing of objects - of these types. For example, \dit lists indexes - and tables. If + is + of these types. For example, \dti lists + tables and indexes. If + is appended to the command name, each object is listed with its - physical size on disk and its associated description, if any. + persistence status (permanent, temporary, or unlogged), + physical size on disk, and associated description if any. If pattern is specified, only objects whose names match the pattern are listed. By default, only user-created objects are shown; supply a @@ -1876,22 +1878,22 @@ testdb=> \echo text [ ... ] - Prints the arguments to the standard output, separated by one - space and followed by a newline. This can be useful to + Prints the evaluated arguments to standard output, separated by + spaces and followed by a newline. This can be useful to intersperse information in the output of scripts. For example: => \echo `date` Tue Oct 26 21:40:57 CEST 1999 If the first argument is an unquoted -n the trailing - newline is not written. + newline is not written (nor is the first argument). If you use the \o command to redirect your query output you might wish to use \qecho - instead of this command. + instead of this command. See also \warn. @@ -3239,6 +3241,18 @@ testdb=> \setenv LESS -imx4F + + \warn text [ ... ] + + + This command is identical to \echo except + that the output will be written to psql's + standard error channel, rather than standard output. + + + + + \watch [ seconds ] diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml index 6076a829d19c..e60956f15e1f 100644 --- a/doc/src/sgml/ref/reindex.sgml +++ b/doc/src/sgml/ref/reindex.sgml @@ -160,7 +160,7 @@ REINDEX [ ( VERBOSE ) ] { INDEX | TABLE | SCHEMA | DATABASE | SYSTEM } [ CONCURR When this option is used, PostgreSQL will rebuild the index without taking any locks that prevent concurrent inserts, - updates, or deletes on the table; whereas a standard reindex build + updates, or deletes on the table; whereas a standard index rebuild locks out writes (but not reads) on the table until it's done. There are several caveats to be aware of when using this option — see CONCURRENTLY option of REINDEX. When this option is used, PostgreSQL must perform two scans of the table - for each index that needs to be rebuild and in addition it must wait for - all existing transactions that could potentially use the index to - terminate. This method requires more total work than a standard index + for each index that needs to be rebuilt and wait for termination of + all existing transactions that could potentially use the index. + This method requires more total work than a standard index rebuild and takes significantly longer to complete as it needs to wait for unfinished transactions that might modify the index. However, since - it allows normal operations to continue while the index is rebuilt, this + it allows normal operations to continue while the index is being rebuilt, this method is useful for rebuilding indexes in a production environment. Of course, the extra CPU, memory and I/O load imposed by the index rebuild may slow down other operations. @@ -449,8 +449,8 @@ broken_db=> \q - Rebuild a table while authorizing read and write operations on involved - relations when performed: + Rebuild indexes for a table, without blocking read and write operations + on involved relations while reindexing is in progress: REINDEX TABLE CONCURRENTLY my_broken_table; diff --git a/doc/src/sgml/ref/reindexdb.sgml b/doc/src/sgml/ref/reindexdb.sgml index 25b5a7277057..5e21fbcc4e6e 100644 --- a/doc/src/sgml/ref/reindexdb.sgml +++ b/doc/src/sgml/ref/reindexdb.sgml @@ -166,6 +166,29 @@ PostgreSQL documentation + + + + + + Execute the reindex commands in parallel by running + njobs + commands simultaneously. This option reduces the time of the + processing but it also increases the load on the database server. + + + reindexdb will open + njobs connections to the + database, so make sure your + setting is high enough to accommodate all connections. + + + Note that this option is incompatible with the + and options. + + + + diff --git a/doc/src/sgml/release-13.sgml b/doc/src/sgml/release-13.sgml new file mode 100644 index 000000000000..d5530f4130db --- /dev/null +++ b/doc/src/sgml/release-13.sgml @@ -0,0 +1,16 @@ + + + + + Release 13 + + + Release date: + 2020-??-?? + + + + This is just a placeholder for now. + + + diff --git a/src/Makefile.global.in b/src/Makefile.global.in index fa3e41d5f6b7..b35153856253 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -23,7 +23,7 @@ standard_targets = all install installdirs uninstall distprep clean distclean ma # these targets should recurse even into subdirectories not being built: standard_always_targets = distprep clean distclean maintainer-clean -.PHONY: $(standard_targets) install-strip html man installcheck-parallel maintainer-check installcheck-good +.PHONY: $(standard_targets) install-strip html man installcheck-parallel installcheck-good # make `all' the default target all: @@ -582,10 +582,6 @@ host_tuple = @host@ host_os = @host_os@ host_cpu = @host_cpu@ -# This is mainly for use on FreeBSD, where we have both a.out and elf -# systems now. May be applicable to other systems to? -ELF_SYSTEM= @ELF_SYS@ - # Backend stack size limit has to be hard-wired on Windows (it's in bytes) WIN32_STACK_RLIMIT=4194304 diff --git a/src/Makefile.shlib b/src/Makefile.shlib index 373d73caefca..c4893edfc389 100644 --- a/src/Makefile.shlib +++ b/src/Makefile.shlib @@ -139,57 +139,42 @@ ifeq ($(PORTNAME), darwin) endif ifeq ($(PORTNAME), openbsd) - ifdef ELF_SYSTEM - LINK.shared = $(COMPILER) -shared - ifdef soname - LINK.shared += -Wl,-x,-soname,$(soname) - endif - BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ - exports_file = $(SHLIB_EXPORTS:%.txt=%.list) - ifneq (,$(exports_file)) - LINK.shared += -Wl,--version-script=$(exports_file) - endif - SHLIB_LINK += -lc - else - LINK.shared = $(LD) -x -Bshareable -Bforcearchive + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-x,-soname,$(soname) endif + BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ + exports_file = $(SHLIB_EXPORTS:%.txt=%.list) + ifneq (,$(exports_file)) + LINK.shared += -Wl,--version-script=$(exports_file) + endif + SHLIB_LINK += -lc endif ifeq ($(PORTNAME), freebsd) - ifdef ELF_SYSTEM - ifdef SO_MAJOR_VERSION - shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) - endif - LINK.shared = $(COMPILER) -shared - ifdef soname - LINK.shared += -Wl,-x,-soname,$(soname) - endif - BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ - exports_file = $(SHLIB_EXPORTS:%.txt=%.list) - ifneq (,$(exports_file)) - LINK.shared += -Wl,--version-script=$(exports_file) - endif - else - ifdef SO_MAJOR_VERSION - shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) - endif - LINK.shared = $(LD) -x -Bshareable -Bforcearchive + ifdef SO_MAJOR_VERSION + shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) + endif + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-x,-soname,$(soname) + endif + BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ + exports_file = $(SHLIB_EXPORTS:%.txt=%.list) + ifneq (,$(exports_file)) + LINK.shared += -Wl,--version-script=$(exports_file) endif endif ifeq ($(PORTNAME), netbsd) - ifdef ELF_SYSTEM - LINK.shared = $(COMPILER) -shared - ifdef soname - LINK.shared += -Wl,-x,-soname,$(soname) - endif - BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ - exports_file = $(SHLIB_EXPORTS:%.txt=%.list) - ifneq (,$(exports_file)) - LINK.shared += -Wl,--version-script=$(exports_file) - endif - else - LINK.shared = $(LD) -x -Bshareable -Bforcearchive + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-x,-soname,$(soname) + endif + BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ + exports_file = $(SHLIB_EXPORTS:%.txt=%.list) + ifneq (,$(exports_file)) + LINK.shared += -Wl,--version-script=$(exports_file) endif endif diff --git a/src/backend/access/common/bufmask.c b/src/backend/access/common/bufmask.c index ee1c6f234a21..bcd9bd007b70 100644 --- a/src/backend/access/common/bufmask.c +++ b/src/backend/access/common/bufmask.c @@ -20,7 +20,7 @@ #include "access/bufmask.h" /* - * mask_page_lsn + * mask_page_lsn_and_checksum * * In consistency checks, the LSN of the two pages compared will likely be * different because of concurrent operations when the WAL is generated and diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index 6d112e66a122..29fc85309ab9 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -262,14 +262,14 @@ SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, i /* Do we have a non-resjunk tlist item? */ while (tlist_item && ((TargetEntry *) lfirst(tlist_item))->resjunk) - tlist_item = lnext(tlist_item); + tlist_item = lnext(targetlist, tlist_item); if (tlist_item) { TargetEntry *tle = (TargetEntry *) lfirst(tlist_item); resorigtbl = tle->resorigtbl; resorigcol = tle->resorigcol; - tlist_item = lnext(tlist_item); + tlist_item = lnext(targetlist, tlist_item); } else { diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 1a6161ee8974..b77449b88f61 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -76,7 +76,7 @@ * currently executing. * * Fillfactor can be set because it applies only to subsequent changes made to - * data blocks, as documented in heapio.c + * data blocks, as documented in hio.c * * n_distinct options can be set at ShareUpdateExclusiveLock because they * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock, diff --git a/src/backend/access/gin/README b/src/backend/access/gin/README index 30c0867829e6..838fdc0d6306 100644 --- a/src/backend/access/gin/README +++ b/src/backend/access/gin/README @@ -163,7 +163,7 @@ algorithms. * The posting list can be accessed with GinGetPosting(itup) -* If GinITupIsCompressed(itup), the posting list is stored in compressed +* If GinItupIsCompressed(itup), the posting list is stored in compressed format. Otherwise it is just an array of ItemPointers. New tuples are always stored in compressed format, uncompressed items can be present if the database was migrated from 9.3 or earlier version. diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c index 7e2a4b25ff84..c33d10889976 100644 --- a/src/backend/access/gin/gindatapage.c +++ b/src/backend/access/gin/gindatapage.c @@ -92,7 +92,7 @@ typedef struct /* * The following fields represent the items in this segment. If 'items' is - * not NULL, it contains a palloc'd array of the itemsin this segment. If + * not NULL, it contains a palloc'd array of the items in this segment. If * 'seg' is not NULL, it contains the items in an already-compressed * format. It can point to an on-disk page (!modified), or a palloc'd * segment in memory. If both are set, they must represent the same items. @@ -1371,7 +1371,7 @@ disassembleLeaf(Page page) if (GinPageIsCompressed(page)) { /* - * Create a leafSegment entry for each segment. + * Create a leafSegmentInfo entry for each segment. */ seg = GinDataLeafPageGetPostingList(page); segbegin = (Pointer) seg; diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c index 2b3dd1c677f9..439a91b3e613 100644 --- a/src/backend/access/gin/ginfast.c +++ b/src/backend/access/gin/ginfast.c @@ -1013,7 +1013,7 @@ ginInsertCleanup(GinState *ginstate, bool full_clean, /* * As pending list pages can have a high churn rate, it is desirable to - * recycle them immediately to the FreeSpace Map when ordinary backends + * recycle them immediately to the FreeSpaceMap when ordinary backends * clean the list. */ if (fsm_vac && fill_fsm) diff --git a/src/backend/access/gist/README b/src/backend/access/gist/README index 84a4961d0c45..8cbca6929675 100644 --- a/src/backend/access/gist/README +++ b/src/backend/access/gist/README @@ -170,7 +170,7 @@ it splits the page, and constructs the new downlink tuples for the split pages. The caller must then call gistplacetopage() on the parent page to insert the downlink tuples. The parent page that holds the downlink to the child might have migrated as a result of concurrent splits of the -parent, gistfindCorrectParent() is used to find the parent page. +parent, gistFindCorrectParent() is used to find the parent page. Splitting the root page works slightly differently. At root split, gistplacetopage() allocates the new child pages and replaces the old root diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 320461b05714..f926021096da 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -709,14 +709,15 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, continue; } - if (stack->blkno != GIST_ROOT_BLKNO && - stack->parent->lsn < GistPageGetNSN(stack->page)) + if ((stack->blkno != GIST_ROOT_BLKNO && + stack->parent->lsn < GistPageGetNSN(stack->page)) || + GistPageIsDeleted(stack->page)) { /* - * Concurrent split detected. There's no guarantee that the - * downlink for this page is consistent with the tuple we're - * inserting anymore, so go back to parent and rechoose the best - * child. + * Concurrent split or page deletion detected. There's no + * guarantee that the downlink for this page is consistent with + * the tuple we're inserting anymore, so go back to parent and + * rechoose the best child. */ UnlockReleaseBuffer(stack->buffer); xlocked = false; @@ -735,9 +736,6 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTInsertStack *item; OffsetNumber downlinkoffnum; - /* currently, internal pages are never deleted */ - Assert(!GistPageIsDeleted(stack->page)); - downlinkoffnum = gistchoose(state.r, stack->page, itup, giststate); iid = PageGetItemId(stack->page, downlinkoffnum); idxtuple = (IndexTuple) PageGetItem(stack->page, iid); @@ -821,7 +819,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, /* * Leaf page. Insert the new key. We've already updated all the * parents on the way down, but we might have to split the page if - * it doesn't fit. gistinserthere() will take care of that. + * it doesn't fit. gistinserttuple() will take care of that. */ /* @@ -858,12 +856,13 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, * leaf/inner is enough to recognize split for root */ } - else if (GistFollowRight(stack->page) || - stack->parent->lsn < GistPageGetNSN(stack->page)) + else if ((GistFollowRight(stack->page) || + stack->parent->lsn < GistPageGetNSN(stack->page)) && + GistPageIsDeleted(stack->page)) { /* - * The page was split while we momentarily unlocked the - * page. Go back to parent. + * The page was split or deleted while we momentarily + * unlocked the page. Go back to parent. */ UnlockReleaseBuffer(stack->buffer); xlocked = false; @@ -872,18 +871,6 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, } } - /* - * The page might have been deleted after we scanned the parent - * and saw the downlink. - */ - if (GistPageIsDeleted(stack->page)) - { - UnlockReleaseBuffer(stack->buffer); - xlocked = false; - state.stack = stack = stack->parent; - continue; - } - /* now state.stack->(page, buffer and blkno) points to leaf page */ gistinserttuple(&state, stack, giststate, itup, @@ -947,6 +934,9 @@ gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum) break; } + /* currently, internal pages are never deleted */ + Assert(!GistPageIsDeleted(page)); + top->lsn = BufferGetLSNAtomic(buffer); /* @@ -1323,8 +1313,6 @@ static void gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, List *splitinfo, bool unlockbuf) { - ListCell *lc; - List *reversed; GISTPageSplitInfo *right; GISTPageSplitInfo *left; IndexTuple tuples[2]; @@ -1339,14 +1327,6 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack, * left. Finally insert the downlink for the last new page and update the * downlink for the original page as one operation. */ - - /* for convenience, create a copy of the list in reverse order */ - reversed = NIL; - foreach(lc, splitinfo) - { - reversed = lcons(lfirst(lc), reversed); - } - LockBuffer(stack->parent->buffer, GIST_EXCLUSIVE); gistFindCorrectParent(state->r, stack); @@ -1354,10 +1334,10 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack, * insert downlinks for the siblings from right to left, until there are * only two siblings left. */ - while (list_length(reversed) > 2) + for (int pos = list_length(splitinfo) - 1; pos > 1; pos--) { - right = (GISTPageSplitInfo *) linitial(reversed); - left = (GISTPageSplitInfo *) lsecond(reversed); + right = (GISTPageSplitInfo *) list_nth(splitinfo, pos); + left = (GISTPageSplitInfo *) list_nth(splitinfo, pos - 1); if (gistinserttuples(state, stack->parent, giststate, &right->downlink, 1, @@ -1371,11 +1351,10 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack, gistFindCorrectParent(state->r, stack); } /* gistinserttuples() released the lock on right->buf. */ - reversed = list_delete_first(reversed); } - right = (GISTPageSplitInfo *) linitial(reversed); - left = (GISTPageSplitInfo *) lsecond(reversed); + right = (GISTPageSplitInfo *) lsecond(splitinfo); + left = (GISTPageSplitInfo *) linitial(splitinfo); /* * Finally insert downlink for the remaining right page and update the diff --git a/src/backend/access/gist/gistbuildbuffers.c b/src/backend/access/gist/gistbuildbuffers.c index cfeeaa502f41..2740d2f5e41f 100644 --- a/src/backend/access/gist/gistbuildbuffers.c +++ b/src/backend/access/gist/gistbuildbuffers.c @@ -138,6 +138,7 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate, nodeBuffer->pageBlocknum = InvalidBlockNumber; nodeBuffer->pageBuffer = NULL; nodeBuffer->queuedForEmptying = false; + nodeBuffer->isTemp = false; nodeBuffer->level = level; /* @@ -186,8 +187,8 @@ gistAllocateNewPageBuffer(GISTBuildBuffers *gfbb) { GISTNodeBufferPage *pageBuffer; - pageBuffer = (GISTNodeBufferPage *) MemoryContextAlloc(gfbb->context, - BLCKSZ); + pageBuffer = (GISTNodeBufferPage *) MemoryContextAllocZero(gfbb->context, + BLCKSZ); pageBuffer->prev = InvalidBlockNumber; /* Set page free space */ diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index 405d71814000..3fe15caf4799 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -392,6 +392,20 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, MemoryContextSwitchTo(oldcxt); } + /* + * Check if the page was deleted after we saw the downlink. There's + * nothing of interest on a deleted page. Note that we must do this + * after checking the NSN for concurrent splits! It's possible that + * the page originally contained some tuples that are visible to us, + * but was split so that all the visible tuples were moved to another + * page, and then this page was deleted. + */ + if (GistPageIsDeleted(page)) + { + UnlockReleaseBuffer(buffer); + return; + } + so->nPageData = so->curPageData = 0; scan->xs_hitup = NULL; /* might point into pageDataCxt */ if (so->pageDataCxt) @@ -685,7 +699,7 @@ gistgettuple(IndexScanDesc scan, ScanDirection dir) } /* - * Check the last returned tuple and add it to killitems if + * Check the last returned tuple and add it to killedItems if * necessary */ if (scan->kill_prior_tuple diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 1826b51bbb4b..118dd9653fa4 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -1464,26 +1464,12 @@ gist_point_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } -/* - * The inexact GiST distance method for geometric types that store bounding - * boxes. - * - * Compute lossy distance from point to index entries. The result is inexact - * because index entries are bounding boxes, not the exact shapes of the - * indexed geometric types. We use distance from point to MBR of index entry. - * This is a lower bound estimate of distance from point to indexed geometric - * type. - */ static float8 -gist_bbox_distance(GISTENTRY *entry, Datum query, - StrategyNumber strategy, bool *recheck) +gist_bbox_distance(GISTENTRY *entry, Datum query, StrategyNumber strategy) { float8 distance; StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset; - /* Bounding box distance is always inexact. */ - *recheck = true; - switch (strategyGroup) { case PointStrategyNumberGroup: @@ -1499,6 +1485,32 @@ gist_bbox_distance(GISTENTRY *entry, Datum query, return distance; } +Datum +gist_box_distance(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + Datum query = PG_GETARG_DATUM(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + /* Oid subtype = PG_GETARG_OID(3); */ + /* bool *recheck = (bool *) PG_GETARG_POINTER(4); */ + float8 distance; + + distance = gist_bbox_distance(entry, query, strategy); + + PG_RETURN_FLOAT8(distance); +} + +/* + * The inexact GiST distance methods for geometric types that store bounding + * boxes. + * + * Compute lossy distance from point to index entries. The result is inexact + * because index entries are bounding boxes, not the exact shapes of the + * indexed geometric types. We use distance from point to MBR of index entry. + * This is a lower bound estimate of distance from point to indexed geometric + * type. + */ Datum gist_circle_distance(PG_FUNCTION_ARGS) { @@ -1510,7 +1522,8 @@ gist_circle_distance(PG_FUNCTION_ARGS) bool *recheck = (bool *) PG_GETARG_POINTER(4); float8 distance; - distance = gist_bbox_distance(entry, query, strategy, recheck); + distance = gist_bbox_distance(entry, query, strategy); + *recheck = true; PG_RETURN_FLOAT8(distance); } @@ -1526,7 +1539,8 @@ gist_poly_distance(PG_FUNCTION_ARGS) bool *recheck = (bool *) PG_GETARG_POINTER(4); float8 distance; - distance = gist_bbox_distance(entry, query, strategy, recheck); + distance = gist_bbox_distance(entry, query, strategy); + *recheck = true; PG_RETURN_FLOAT8(distance); } diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 9ccde5c89e48..e77fb5e08dfb 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -120,7 +120,7 @@ gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen) } /* - * make plain IndexTupleVector + * make plain IndexTuple vector */ IndexTupleData * @@ -882,9 +882,27 @@ gistNewBuffer(Relation r) bool gistPageRecyclable(Page page) { - return PageIsNew(page) || - (GistPageIsDeleted(page) && - TransactionIdPrecedes(GistPageGetDeleteXid(page), RecentGlobalXmin)); + if (PageIsNew(page)) + return true; + if (GistPageIsDeleted(page)) + { + /* + * The page was deleted, but when? If it was just deleted, a scan + * might have seen the downlink to it, and will read the page later. + * As long as that can happen, we must keep the deleted page around as + * a tombstone. + * + * Compare the deletion XID with RecentGlobalXmin. If deleteXid < + * RecentGlobalXmin, then no scan that's still in progress could have + * seen its downlink, and we can recycle it. + */ + FullTransactionId deletexid_full = GistPageGetDeleteXid(page); + FullTransactionId recentxmin_full = GetFullRecentGlobalXmin(); + + if (FullTransactionIdPrecedes(deletexid_full, recentxmin_full)) + return true; + } + return false; } bytea * diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index 4270226eee29..bf754ea6d0d9 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -595,7 +595,7 @@ gistdeletepage(IndexVacuumInfo *info, GistBulkDeleteResult *stats, ItemId iid; IndexTuple idxtuple; XLogRecPtr recptr; - TransactionId txid; + FullTransactionId txid; /* * Check that the leaf is still empty and deletable. @@ -648,14 +648,13 @@ gistdeletepage(IndexVacuumInfo *info, GistBulkDeleteResult *stats, * currently in progress must have ended. (That's much more conservative * than needed, but let's keep it safe and simple.) */ - txid = ReadNewTransactionId(); + txid = ReadNextFullTransactionId(); START_CRIT_SECTION(); /* mark the page as deleted */ MarkBufferDirty(leafBuffer); - GistPageSetDeleteXid(leafPage, txid); - GistPageSetDeleted(leafPage); + GistPageSetDeleted(leafPage, txid); stats->stats.pages_deleted++; /* remove the downlink from the parent */ diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c index 503db34d8639..3b28f546465d 100644 --- a/src/backend/access/gist/gistxlog.c +++ b/src/backend/access/gist/gistxlog.c @@ -356,8 +356,7 @@ gistRedoPageDelete(XLogReaderState *record) { Page page = (Page) BufferGetPage(leafBuffer); - GistPageSetDeleteXid(page, xldata->deleteXid); - GistPageSetDeleted(page); + GistPageSetDeleted(page, xldata->deleteXid); PageSetLSN(page, lsn); MarkBufferDirty(leafBuffer); @@ -396,8 +395,27 @@ gistRedoPageReuse(XLogReaderState *record) */ if (InHotStandby) { - ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid, - xlrec->node); + FullTransactionId latestRemovedFullXid = xlrec->latestRemovedFullXid; + FullTransactionId nextFullXid = ReadNextFullTransactionId(); + uint64 diff; + + /* + * ResolveRecoveryConflictWithSnapshot operates on 32-bit + * TransactionIds, so truncate the logged FullTransactionId. If the + * logged value is very old, so that XID wrap-around already happened + * on it, there can't be any snapshots that still see it. + */ + nextFullXid = ReadNextFullTransactionId(); + diff = U64FromFullTransactionId(nextFullXid) - + U64FromFullTransactionId(latestRemovedFullXid); + if (diff < MaxTransactionId / 2) + { + TransactionId latestRemovedXid; + + latestRemovedXid = XidFromFullTransactionId(latestRemovedFullXid); + ResolveRecoveryConflictWithSnapshot(latestRemovedXid, + xlrec->node); + } } } @@ -554,7 +572,7 @@ gistXLogSplit(bool page_is_leaf, * downlink from the parent page. */ XLogRecPtr -gistXLogPageDelete(Buffer buffer, TransactionId xid, +gistXLogPageDelete(Buffer buffer, FullTransactionId xid, Buffer parentBuffer, OffsetNumber downlinkOffset) { gistxlogPageDelete xlrec; @@ -578,7 +596,7 @@ gistXLogPageDelete(Buffer buffer, TransactionId xid, * Write XLOG record about reuse of a deleted page. */ void -gistXLogPageReuse(Relation rel, BlockNumber blkno, TransactionId latestRemovedXid) +gistXLogPageReuse(Relation rel, BlockNumber blkno, FullTransactionId latestRemovedXid) { gistxlogPageReuse xlrec_reuse; @@ -591,7 +609,7 @@ gistXLogPageReuse(Relation rel, BlockNumber blkno, TransactionId latestRemovedXi /* XLOG stuff */ xlrec_reuse.node = rel->rd_node; xlrec_reuse.block = blkno; - xlrec_reuse.latestRemovedXid = latestRemovedXid; + xlrec_reuse.latestRemovedFullXid = latestRemovedXid; XLogBeginInsert(); XLogRegisterData((char *) &xlrec_reuse, SizeOfGistxlogPageReuse); diff --git a/src/backend/access/hash/hashovfl.c b/src/backend/access/hash/hashovfl.c index ae0cef58b27c..67ceea0af755 100644 --- a/src/backend/access/hash/hashovfl.c +++ b/src/backend/access/hash/hashovfl.c @@ -793,7 +793,7 @@ _hash_initbitmapbuffer(Buffer buf, uint16 bmsize, bool initpage) * be confused into returning the same tuple more than once or some tuples * not at all by the rearrangement we are performing here. To prevent * any concurrent scan to cross the squeeze scan we use lock chaining - * similar to hasbucketcleanup. Refer comments atop hashbucketcleanup. + * similar to hashbucketcleanup. Refer comments atop hashbucketcleanup. * * We need to retain a pin on the primary bucket to ensure that no concurrent * split can start. diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c index 376ee2a63b52..defdc9b4085c 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -509,7 +509,7 @@ _hash_init_metabuffer(Buffer buf, double num_tuples, RegProcedure procid, * Choose the number of initial bucket pages to match the fill factor * given the estimated number of tuples. We round up the result to the * total number of buckets which has to be allocated before using its - * _hashm_spare element. However always force at least 2 bucket pages. The + * hashm_spares element. However always force at least 2 bucket pages. The * upper limit is determined by considerations explained in * _hash_expandtable(). */ diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index cd9fc05af6e2..c2d17e6d9c9c 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -112,7 +112,7 @@ static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, int *remaining); static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup); -static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified, +static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_changed, bool *copy); diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 89f45d404f21..31d6aeb79a67 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -19,8 +19,6 @@ */ #include "postgres.h" -#include - #include "miscadmin.h" #include "access/genam.h" @@ -38,7 +36,6 @@ #include "commands/progress.h" #include "commands/vacuum.h" #include "executor/executor.h" -#include "optimizer/plancat.h" #include "pgstat.h" #include "storage/bufmgr.h" #include "storage/bufpage.h" @@ -454,7 +451,9 @@ heapam_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot, /* otherwise xmin should not be dirty... */ if (TransactionIdIsValid(SnapshotDirty.xmin)) - elog(ERROR, "t_xmin is uncommitted in tuple to be updated"); + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg_internal("t_xmin is uncommitted in tuple to be updated"))); /* * If tuple is being updated by other transaction then we @@ -2024,26 +2023,6 @@ heapam_scan_get_blocks_done(HeapScanDesc hscan) * ------------------------------------------------------------------------ */ -static uint64 -heapam_relation_size(Relation rel, ForkNumber forkNumber) -{ - uint64 nblocks = 0; - - /* Open it at the smgr level if not already done */ - RelationOpenSmgr(rel); - - /* InvalidForkNumber indicates returning the size for all forks */ - if (forkNumber == InvalidForkNumber) - { - for (int i = 0; i < MAX_FORKNUM; i++) - nblocks += smgrnblocks(rel->rd_smgr, i); - } - else - nblocks = smgrnblocks(rel->rd_smgr, forkNumber); - - return nblocks * BLCKSZ; -} - /* * Check to see whether the table needs a TOAST table. It does only if * (1) there are any toastable attributes, and (2) the maximum length @@ -2100,106 +2079,20 @@ heapam_relation_needs_toast_table(Relation rel) * ------------------------------------------------------------------------ */ +#define HEAP_OVERHEAD_BYTES_PER_TUPLE \ + (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData)) +#define HEAP_USABLE_BYTES_PER_PAGE \ + (BLCKSZ - SizeOfPageHeaderData) + static void heapam_estimate_rel_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac) { - BlockNumber curpages; - BlockNumber relpages; - double reltuples; - BlockNumber relallvisible; - double density; - - /* it has storage, ok to call the smgr */ - curpages = RelationGetNumberOfBlocks(rel); - - /* coerce values in pg_class to more desirable types */ - relpages = (BlockNumber) rel->rd_rel->relpages; - reltuples = (double) rel->rd_rel->reltuples; - relallvisible = (BlockNumber) rel->rd_rel->relallvisible; - - /* - * HACK: if the relation has never yet been vacuumed, use a minimum size - * estimate of 10 pages. The idea here is to avoid assuming a - * newly-created table is really small, even if it currently is, because - * that may not be true once some data gets loaded into it. Once a vacuum - * or analyze cycle has been done on it, it's more reasonable to believe - * the size is somewhat stable. - * - * (Note that this is only an issue if the plan gets cached and used again - * after the table has been filled. What we're trying to avoid is using a - * nestloop-type plan on a table that has grown substantially since the - * plan was made. Normally, autovacuum/autoanalyze will occur once enough - * inserts have happened and cause cached-plan invalidation; but that - * doesn't happen instantaneously, and it won't happen at all for cases - * such as temporary tables.) - * - * We approximate "never vacuumed" by "has relpages = 0", which means this - * will also fire on genuinely empty relations. Not great, but - * fortunately that's a seldom-seen case in the real world, and it - * shouldn't degrade the quality of the plan too much anyway to err in - * this direction. - * - * If the table has inheritance children, we don't apply this heuristic. - * Totally empty parent tables are quite common, so we should be willing - * to believe that they are empty. - */ - if (curpages < 10 && - relpages == 0 && - !rel->rd_rel->relhassubclass) - curpages = 10; - - /* report estimated # pages */ - *pages = curpages; - /* quick exit if rel is clearly empty */ - if (curpages == 0) - { - *tuples = 0; - *allvisfrac = 0; - return; - } - - /* estimate number of tuples from previous tuple density */ - if (relpages > 0) - density = reltuples / (double) relpages; - else - { - /* - * When we have no data because the relation was truncated, estimate - * tuple width from attribute datatypes. We assume here that the - * pages are completely full, which is OK for tables (since they've - * presumably not been VACUUMed yet) but is probably an overestimate - * for indexes. Fortunately get_relation_info() can clamp the - * overestimate to the parent table's size. - * - * Note: this code intentionally disregards alignment considerations, - * because (a) that would be gilding the lily considering how crude - * the estimate is, and (b) it creates platform dependencies in the - * default plans which are kind of a headache for regression testing. - */ - int32 tuple_width; - - tuple_width = get_rel_data_width(rel, attr_widths); - tuple_width += MAXALIGN(SizeofHeapTupleHeader); - tuple_width += sizeof(ItemIdData); - /* note: integer division is intentional here */ - density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width; - } - *tuples = rint(density * (double) curpages); - - /* - * We use relallvisible as-is, rather than scaling it up like we do for - * the pages and tuples counts, on the theory that any pages added since - * the last VACUUM are most likely not marked all-visible. But costsize.c - * wants it converted to a fraction. - */ - if (relallvisible == 0 || curpages <= 0) - *allvisfrac = 0; - else if ((double) relallvisible >= curpages) - *allvisfrac = 1; - else - *allvisfrac = (double) relallvisible / curpages; + table_block_relation_estimate_size(rel, attr_widths, pages, + tuples, allvisfrac, + HEAP_OVERHEAD_BYTES_PER_TUPLE, + HEAP_USABLE_BYTES_PER_PAGE); } @@ -2680,7 +2573,7 @@ static const TableAmRoutine heapam_methods = { .index_build_range_scan = heapam_index_build_range_scan, .index_validate_scan = heapam_index_validate_scan, - .relation_size = heapam_relation_size, + .relation_size = table_block_relation_size, .relation_needs_toast_table = heapam_relation_needs_toast_table, .relation_estimate_size = heapam_estimate_rel_size, diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 31f3d91cc32b..48b5618673ea 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -256,7 +256,7 @@ heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin, MarkBufferDirty(buffer); /* - * Emit a WAL HEAP_CLEAN record showing what we did + * Emit a WAL XLOG_HEAP2_CLEAN record showing what we did */ if (RelationNeedsWAL(relation)) { diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c index 790536c8fbb2..1b79d4bf7f94 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -237,7 +237,7 @@ static void logical_end_heap_rewrite(RewriteState state); * new_heap new, locked heap relation to insert tuples to * oldest_xmin xid used by the caller to determine which tuples are dead * freeze_xid xid before which tuples will be frozen - * min_multi multixact before which multis will be removed + * cutoff_multi multixact before which multis will be removed * use_wal should the inserts to the new heap be WAL-logged? * * Returns an opaque RewriteState, allocated in current memory context, @@ -792,7 +792,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup) * Instead we simply write the mapping files out to disk, *before* the * XLogInsert() is performed. That guarantees that either the XLogInsert() is * inserted after the checkpoint's redo pointer or that the checkpoint (via - * LogicalRewriteHeapCheckpoint()) has flushed the (partial) mapping file to + * CheckPointLogicalRewriteHeap()) has flushed the (partial) mapping file to * disk. That leaves the tail end that has not yet been flushed open to * corruption, which is solved by including the current offset in the * xl_heap_rewrite_mapping records and truncating the mapping file to it @@ -1207,7 +1207,7 @@ heap_xlog_logical_rewrite(XLogReaderState *r) errmsg("could not fsync file \"%s\": %m", path))); pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); @@ -1309,7 +1309,7 @@ CheckPointLogicalRewriteHeap(void) errmsg("could not fsync file \"%s\": %m", path))); pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 1732c8adc70f..e1afa23a4d84 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -2223,10 +2223,12 @@ toast_fetch_datum(struct varlena *attr) * Some checks on the data we've found */ if (residx != nextidx) - elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u in %s", - residx, nextidx, - toast_pointer.va_valueid, - RelationGetRelationName(toastrel)); + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg_internal("unexpected chunk number %d (expected %d) for toast value %u in %s", + residx, nextidx, + toast_pointer.va_valueid, + RelationGetRelationName(toastrel)))); if ((residx == 0) && (chunksize < ressize) && (chunksize != actual_max_chunk_size)) @@ -2251,28 +2253,34 @@ toast_fetch_datum(struct varlena *attr) if (residx < numchunks - 1) { if (chunksize != actual_max_chunk_size) - elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s", - chunksize, (int) actual_max_chunk_size, - residx, numchunks, - toast_pointer.va_valueid, - RelationGetRelationName(toastrel)); + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg_internal("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s", + chunksize, (int) actual_max_chunk_size, + residx, numchunks, + toast_pointer.va_valueid, + RelationGetRelationName(toastrel)))); } else if (residx == numchunks - 1) { if ((residx * actual_max_chunk_size + chunksize) != ressize) - elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s", - chunksize, - (int) (ressize - residx * actual_max_chunk_size), - residx, - toast_pointer.va_valueid, - RelationGetRelationName(toastrel)); + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg_internal("unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s", + chunksize, + (int) (ressize - residx * actual_max_chunk_size), + residx, + toast_pointer.va_valueid, + RelationGetRelationName(toastrel)))); } else - elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s", - residx, - 0, numchunks - 1, - toast_pointer.va_valueid, - RelationGetRelationName(toastrel)); + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg_internal("unexpected chunk number %d (out of range %d..%d) for toast value %u in %s", + residx, + 0, numchunks - 1, + toast_pointer.va_valueid, + RelationGetRelationName(toastrel)))); /* * Copy the data into proper place in our result @@ -2288,10 +2296,12 @@ toast_fetch_datum(struct varlena *attr) * Final checks that we successfully fetched the datum */ if (nextidx != numchunks) - elog(ERROR, "missing chunk number %d for toast value %u in %s", - nextidx, - toast_pointer.va_valueid, - RelationGetRelationName(toastrel)); + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg_internal("missing chunk number %d for toast value %u in %s", + nextidx, + toast_pointer.va_valueid, + RelationGetRelationName(toastrel)))); /* * End scan and close relations diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 42aaa5bad62c..2599b5d3425f 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -557,8 +557,8 @@ systable_endscan(SysScanDesc sysscan) * we could do a heapscan and sort, but the uses are in places that * probably don't need to still work with corrupted catalog indexes.) * For the moment, therefore, these functions are merely the thinnest of - * wrappers around index_beginscan/index_getnext. The main reason for their - * existence is to centralize possible future support of lossy operators + * wrappers around index_beginscan/index_getnext_slot. The main reason for + * their existence is to centralize possible future support of lossy operators * in catalog scans. */ SysScanDesc diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README index c5b0a30e4ebd..3d01b7854df2 100644 --- a/src/backend/access/nbtree/README +++ b/src/backend/access/nbtree/README @@ -457,7 +457,7 @@ right sibling's left-link --- followed by a second WAL entry for the insertion on the parent level (which might itself be a page split, requiring an additional insertion above that, etc). -For a root split, the followon WAL entry is a "new root" entry rather than +For a root split, the follow-on WAL entry is a "new root" entry rather than an "insertion" entry, but details are otherwise much the same. Because splitting involves multiple atomic actions, it's possible that the diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index 225923733720..eea562dcc58f 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -1572,10 +1572,12 @@ _bt_split(Relation rel, BTScanInsert itup_key, Buffer buf, Buffer cbuf, if (sopaque->btpo_prev != origpagenumber) { memset(rightpage, 0, BufferGetPageSize(rbuf)); - elog(ERROR, "right sibling's left-link doesn't match: " - "block %u links to %u instead of expected %u in index \"%s\"", - oopaque->btpo_next, sopaque->btpo_prev, origpagenumber, - RelationGetRelationName(rel)); + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg_internal("right sibling's left-link doesn't match: " + "block %u links to %u instead of expected %u in index \"%s\"", + oopaque->btpo_next, sopaque->btpo_prev, origpagenumber, + RelationGetRelationName(rel)))); } /* @@ -1831,8 +1833,10 @@ _bt_insert_parent(Relation rel, _bt_relbuf(rel, rbuf); if (pbuf == InvalidBuffer) - elog(ERROR, "failed to re-find parent key in index \"%s\" for split pages %u/%u", - RelationGetRelationName(rel), bknum, rbknum); + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg_internal("failed to re-find parent key in index \"%s\" for split pages %u/%u", + RelationGetRelationName(rel), bknum, rbknum))); /* Recursively update the parent */ _bt_insertonpg(rel, NULL, pbuf, buf, stack->bts_parent, diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 03570300d895..9c1f7de60f11 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -33,7 +33,6 @@ #include "storage/predicate.h" #include "utils/snapmgr.h" -static void _bt_cachemetadata(Relation rel, BTMetaPageData *input); static BTMetaPageData *_bt_getmeta(Relation rel, Buffer metabuf); static bool _bt_mark_page_halfdead(Relation rel, Buffer buf, BTStack stack); static bool _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, @@ -109,48 +108,13 @@ _bt_upgrademetapage(Page page) ((char *) metad + sizeof(BTMetaPageData)) - (char *) page; } -/* - * Cache metadata from input meta page to rel->rd_amcache. - */ -static void -_bt_cachemetadata(Relation rel, BTMetaPageData *input) -{ - BTMetaPageData *cached_metad; - - /* We assume rel->rd_amcache was already freed by caller */ - Assert(rel->rd_amcache == NULL); - rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt, - sizeof(BTMetaPageData)); - - /* Meta page should be of supported version */ - Assert(input->btm_version >= BTREE_MIN_VERSION && - input->btm_version <= BTREE_VERSION); - - cached_metad = (BTMetaPageData *) rel->rd_amcache; - if (input->btm_version >= BTREE_NOVAC_VERSION) - { - /* Version with compatible meta-data, no need to upgrade */ - memcpy(cached_metad, input, sizeof(BTMetaPageData)); - } - else - { - /* - * Upgrade meta-data: copy available information from meta-page and - * fill new fields with default values. - * - * Note that we cannot upgrade to version 4+ without a REINDEX, since - * extensive on-disk changes are required. - */ - memcpy(cached_metad, input, offsetof(BTMetaPageData, btm_oldest_btpo_xact)); - cached_metad->btm_version = BTREE_NOVAC_VERSION; - cached_metad->btm_oldest_btpo_xact = InvalidTransactionId; - cached_metad->btm_last_cleanup_num_heap_tuples = -1.0; - } -} - /* * Get metadata from share-locked buffer containing metapage, while performing - * standard sanity checks. Sanity checks here must match _bt_getroot(). + * standard sanity checks. + * + * Callers that cache data returned here in local cache should note that an + * on-the-fly upgrade using _bt_upgrademetapage() can change the version field + * and BTREE_NOVAC_VERSION specific fields without invalidating local cache. */ static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf) @@ -266,9 +230,7 @@ _bt_update_meta_cleanup_info(Relation rel, TransactionId oldestBtpoXact, * * Since the root page can move around the btree file, we have to read * its location from the metadata page, and then read the root page - * itself. If no root page exists yet, we have to create one. The - * standard class of race conditions exists here; I think I covered - * them all in the intricate dance of lock requests below. + * itself. If no root page exists yet, we have to create one. * * The access type parameter (BT_READ or BT_WRITE) controls whether * a new root page will be created or not. If access = BT_READ, @@ -293,8 +255,6 @@ Buffer _bt_getroot(Relation rel, int access) { Buffer metabuf; - Page metapg; - BTPageOpaque metaopaque; Buffer rootbuf; Page rootpage; BTPageOpaque rootopaque; @@ -347,30 +307,13 @@ _bt_getroot(Relation rel, int access) } metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ); - metapg = BufferGetPage(metabuf); - metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg); - metad = BTPageGetMeta(metapg); - - /* sanity-check the metapage */ - if (!P_ISMETA(metaopaque) || - metad->btm_magic != BTREE_MAGIC) - ereport(ERROR, - (errcode(ERRCODE_INDEX_CORRUPTED), - errmsg("index \"%s\" is not a btree", - RelationGetRelationName(rel)))); - - if (metad->btm_version < BTREE_MIN_VERSION || - metad->btm_version > BTREE_VERSION) - ereport(ERROR, - (errcode(ERRCODE_INDEX_CORRUPTED), - errmsg("version mismatch in index \"%s\": file version %d, " - "current version %d, minimal supported version %d", - RelationGetRelationName(rel), - metad->btm_version, BTREE_VERSION, BTREE_MIN_VERSION))); + metad = _bt_getmeta(rel, metabuf); /* if no root page initialized yet, do it */ if (metad->btm_root == P_NONE) { + Page metapg; + /* If access = BT_READ, caller doesn't want us to create root yet */ if (access == BT_READ) { @@ -412,6 +355,8 @@ _bt_getroot(Relation rel, int access) rootopaque->btpo_flags = (BTP_LEAF | BTP_ROOT); rootopaque->btpo.level = 0; rootopaque->btpo_cycleid = 0; + /* Get raw page pointer for metapage */ + metapg = BufferGetPage(metabuf); /* NO ELOG(ERROR) till meta is updated */ START_CRIT_SECTION(); @@ -473,7 +418,7 @@ _bt_getroot(Relation rel, int access) LockBuffer(rootbuf, BUFFER_LOCK_UNLOCK); LockBuffer(rootbuf, BT_READ); - /* okay, metadata is correct, release lock on it */ + /* okay, metadata is correct, release lock on it without caching */ _bt_relbuf(rel, metabuf); } else @@ -485,7 +430,9 @@ _bt_getroot(Relation rel, int access) /* * Cache the metapage data for next time */ - _bt_cachemetadata(rel, metad); + rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt, + sizeof(BTMetaPageData)); + memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData)); /* * We are done with the metapage; arrange to release it via first @@ -659,16 +606,19 @@ _bt_getrootheight(Relation rel) /* * Cache the metapage data for next time */ - _bt_cachemetadata(rel, metad); - /* We shouldn't have cached it if any of these fail */ - Assert(metad->btm_magic == BTREE_MAGIC); - Assert(metad->btm_version >= BTREE_NOVAC_VERSION); - Assert(metad->btm_fastroot != P_NONE); + rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt, + sizeof(BTMetaPageData)); + memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData)); _bt_relbuf(rel, metabuf); } /* Get cached page */ metad = (BTMetaPageData *) rel->rd_amcache; + /* We shouldn't have cached it if any of these fail */ + Assert(metad->btm_magic == BTREE_MAGIC); + Assert(metad->btm_version >= BTREE_MIN_VERSION); + Assert(metad->btm_version <= BTREE_VERSION); + Assert(metad->btm_fastroot != P_NONE); return metad->btm_fastlevel; } @@ -709,17 +659,26 @@ _bt_heapkeyspace(Relation rel) /* * Cache the metapage data for next time + * + * An on-the-fly version upgrade performed by _bt_upgrademetapage() + * can change the nbtree version for an index without invalidating any + * local cache. This is okay because it can only happen when moving + * from version 2 to version 3, both of which are !heapkeyspace + * versions. */ - _bt_cachemetadata(rel, metad); - /* We shouldn't have cached it if any of these fail */ - Assert(metad->btm_magic == BTREE_MAGIC); - Assert(metad->btm_version >= BTREE_NOVAC_VERSION); - Assert(metad->btm_fastroot != P_NONE); + rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt, + sizeof(BTMetaPageData)); + memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData)); _bt_relbuf(rel, metabuf); } /* Get cached page */ metad = (BTMetaPageData *) rel->rd_amcache; + /* We shouldn't have cached it if any of these fail */ + Assert(metad->btm_magic == BTREE_MAGIC); + Assert(metad->btm_version >= BTREE_MIN_VERSION); + Assert(metad->btm_version <= BTREE_VERSION); + Assert(metad->btm_fastroot != P_NONE); return metad->btm_version > BTREE_NOVAC_VERSION; } @@ -1233,8 +1192,10 @@ _bt_lock_branch_parent(Relation rel, BlockNumber child, BTStack stack, stack->bts_btentry = child; pbuf = _bt_getstackbuf(rel, stack); if (pbuf == InvalidBuffer) - elog(ERROR, "failed to re-find parent key in index \"%s\" for deletion target page %u", - RelationGetRelationName(rel), child); + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg_internal("failed to re-find parent key in index \"%s\" for deletion target page %u", + RelationGetRelationName(rel), child))); parent = stack->bts_blkno; poffset = stack->bts_offset; @@ -1652,9 +1613,11 @@ _bt_mark_page_halfdead(Relation rel, Buffer leafbuf, BTStack stack) itemid = PageGetItemId(page, nextoffset); itup = (IndexTuple) PageGetItem(page, itemid); if (BTreeInnerTupleGetDownLink(itup) != rightsib) - elog(ERROR, "right sibling %u of block %u is not next child %u of block %u in index \"%s\"", - rightsib, target, BTreeInnerTupleGetDownLink(itup), - BufferGetBlockNumber(topparent), RelationGetRelationName(rel)); + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg_internal("right sibling %u of block %u is not next child %u of block %u in index \"%s\"", + rightsib, target, BTreeInnerTupleGetDownLink(itup), + BufferGetBlockNumber(topparent), RelationGetRelationName(rel)))); /* * Any insert which would have gone on the leaf block will now go to its @@ -1919,8 +1882,10 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty) target, RelationGetRelationName(rel)); } if (opaque->btpo_prev != leftsib) - elog(ERROR, "left link changed unexpectedly in block %u of index \"%s\"", - target, RelationGetRelationName(rel)); + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg_internal("left link changed unexpectedly in block %u of index \"%s\"", + target, RelationGetRelationName(rel)))); if (target == leafblkno) { @@ -1952,10 +1917,12 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty) page = BufferGetPage(rbuf); opaque = (BTPageOpaque) PageGetSpecialPointer(page); if (opaque->btpo_prev != target) - elog(ERROR, "right sibling's left-link doesn't match: " - "block %u links to %u instead of expected %u in index \"%s\"", - rightsib, opaque->btpo_prev, target, - RelationGetRelationName(rel)); + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg_internal("right sibling's left-link doesn't match: " + "block %u links to %u instead of expected %u in index \"%s\"", + rightsib, opaque->btpo_prev, target, + RelationGetRelationName(rel)))); rightsib_is_rightmost = P_RIGHTMOST(opaque); *rightsib_empty = (P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page)); diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c index c655dadb963e..19735bf73303 100644 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@ -2113,8 +2113,10 @@ _bt_get_endpoint(Relation rel, uint32 level, bool rightmost, if (opaque->btpo.level == level) break; if (opaque->btpo.level < level) - elog(ERROR, "btree level %u not found in index \"%s\"", - level, RelationGetRelationName(rel)); + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg_internal("btree level %u not found in index \"%s\"", + level, RelationGetRelationName(rel)))); /* Descend to leftmost or rightmost child page */ if (rightmost) diff --git a/src/backend/access/nbtree/nbtsplitloc.c b/src/backend/access/nbtree/nbtsplitloc.c index f43ec6774b44..a7882fd8742e 100644 --- a/src/backend/access/nbtree/nbtsplitloc.c +++ b/src/backend/access/nbtree/nbtsplitloc.c @@ -74,7 +74,7 @@ static bool _bt_afternewitemoff(FindSplitData *state, OffsetNumber maxoff, int leaffillfactor, bool *usemult); static bool _bt_adjacenthtid(ItemPointer lowhtid, ItemPointer highhtid); static OffsetNumber _bt_bestsplitloc(FindSplitData *state, int perfectpenalty, - bool *newitemonleft); + bool *newitemonleft, FindSplitStrat strategy); static int _bt_strategy(FindSplitData *state, SplitPoint *leftpage, SplitPoint *rightpage, FindSplitStrat *strategy); static void _bt_interval_edges(FindSplitData *state, @@ -214,7 +214,7 @@ _bt_findsplitloc(Relation rel, * split can be newitem. Record a split after the previous data item * from original page, but before the current data item from original * page. (_bt_recsplitloc() will reject the split when there are no - * previous data items, which we rely on.) + * previous items, which we rely on.) */ if (offnum < newitemoff) _bt_recsplitloc(&state, offnum, false, olddataitemstoleft, itemsz); @@ -361,10 +361,11 @@ _bt_findsplitloc(Relation rel, * fillfactormult, in order to deal with pages with a large number of * duplicates gracefully. * - * Pass low and high splits for the entire page (including even newitem). - * These are used when the initial split interval encloses split points - * that are full of duplicates, and we need to consider if it's even - * possible to avoid appending a heap TID. + * Pass low and high splits for the entire page (actually, they're for an + * imaginary version of the page that includes newitem). These are used + * when the initial split interval encloses split points that are full of + * duplicates, and we need to consider if it's even possible to avoid + * appending a heap TID. */ perfectpenalty = _bt_strategy(&state, &leftpage, &rightpage, &strategy); @@ -381,12 +382,15 @@ _bt_findsplitloc(Relation rel, * appended, but the page isn't completely full of logical duplicates. * * The split interval is widened to include all legal candidate split - * points. There may be a few as two distinct values in the whole-page - * split interval. Many duplicates strategy has no hard requirements for - * space utilization, though it still keeps the use of space balanced as a - * non-binding secondary goal (perfect penalty is set so that the - * first/lowest delta split points that avoids appending a heap TID is - * used). + * points. There might be a few as two distinct values in the whole-page + * split interval, though it's also possible that most of the values on + * the page are unique. The final split point will either be to the + * immediate left or to the immediate right of the group of duplicate + * tuples that enclose the first/delta-optimal split point (perfect + * penalty was set so that the lowest delta split point that avoids + * appending a heap TID will be chosen). Maximizing the number of + * attributes that can be truncated away is not a goal of the many + * duplicates strategy. * * Single value strategy is used when it is impossible to avoid appending * a heap TID. It arranges to leave the left page very full. This @@ -399,6 +403,9 @@ _bt_findsplitloc(Relation rel, else if (strategy == SPLIT_MANY_DUPLICATES) { Assert(state.is_leaf); + /* Shouldn't try to truncate away extra user attributes */ + Assert(perfectpenalty == + IndexRelationGetNumberOfKeyAttributes(state.rel)); /* No need to resort splits -- no change in fillfactormult/deltas */ state.interval = state.nsplits; } @@ -419,7 +426,8 @@ _bt_findsplitloc(Relation rel, * the entry that has the lowest penalty, and is therefore expected to * maximize fan-out. Sets *newitemonleft for us. */ - foundfirstright = _bt_bestsplitloc(&state, perfectpenalty, newitemonleft); + foundfirstright = _bt_bestsplitloc(&state, perfectpenalty, newitemonleft, + strategy); pfree(state.splits); return foundfirstright; @@ -753,11 +761,13 @@ _bt_adjacenthtid(ItemPointer lowhtid, ItemPointer highhtid) * page, plus a boolean indicating if new item is on left of split point. */ static OffsetNumber -_bt_bestsplitloc(FindSplitData *state, int perfectpenalty, bool *newitemonleft) +_bt_bestsplitloc(FindSplitData *state, int perfectpenalty, + bool *newitemonleft, FindSplitStrat strategy) { int bestpenalty, lowsplit; int highsplit = Min(state->interval, state->nsplits); + SplitPoint *final; bestpenalty = INT_MAX; lowsplit = 0; @@ -781,8 +791,37 @@ _bt_bestsplitloc(FindSplitData *state, int perfectpenalty, bool *newitemonleft) } } - *newitemonleft = state->splits[lowsplit].newitemonleft; - return state->splits[lowsplit].firstoldonright; + final = &state->splits[lowsplit]; + + /* + * There is a risk that the "many duplicates" strategy will repeatedly do + * the wrong thing when there are monotonically decreasing insertions to + * the right of a large group of duplicates. Repeated splits could leave + * a succession of right half pages with free space that can never be + * used. This must be avoided. + * + * Consider the example of the leftmost page in a single integer attribute + * NULLS FIRST index which is almost filled with NULLs. Monotonically + * decreasing integer insertions might cause the same leftmost page to + * split repeatedly at the same point. Each split derives its new high + * key from the lowest current value to the immediate right of the large + * group of NULLs, which will always be higher than all future integer + * insertions, directing all future integer insertions to the same + * leftmost page. + */ + if (strategy == SPLIT_MANY_DUPLICATES && !state->is_rightmost && + !final->newitemonleft && final->firstoldonright >= state->newitemoff && + final->firstoldonright < state->newitemoff + MAX_LEAF_INTERVAL) + { + /* + * Avoid the problem by peforming a 50:50 split when the new item is + * just to the right of the would-be "many duplicates" split point. + */ + final = &state->splits[0]; + } + + *newitemonleft = final->newitemonleft; + return final->firstoldonright; } /* @@ -859,17 +898,16 @@ _bt_strategy(FindSplitData *state, SplitPoint *leftpage, *strategy = SPLIT_MANY_DUPLICATES; /* - * Caller should choose the lowest delta split that avoids appending a - * heap TID. Maximizing the number of attributes that can be - * truncated away (returning perfectpenalty when it happens to be less - * than the number of key attributes in index) can result in continual - * unbalanced page splits. + * Many duplicates strategy should split at either side the group of + * duplicates that enclose the delta-optimal split point. Return + * indnkeyatts rather than the true perfect penalty to make that + * happen. (If perfectpenalty was returned here then low cardinality + * composite indexes could have continual unbalanced splits.) * - * Just avoiding appending a heap TID can still make splits very - * unbalanced, but this is self-limiting. When final split has a very - * high delta, one side of the split will likely consist of a single - * value. If that page is split once again, then that split will - * likely use the single value strategy. + * Note that caller won't go through with a many duplicates split in + * rare cases where it looks like there are ever-decreasing insertions + * to the immediate right of the split point. This must happen just + * before a final decision is made, within _bt_bestsplitloc(). */ return indnkeyatts; } diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c index 6fdaaaa767d5..74a5228980a8 100644 --- a/src/backend/access/nbtree/nbtxlog.c +++ b/src/backend/access/nbtree/nbtxlog.c @@ -109,6 +109,8 @@ _bt_restore_meta(XLogReaderState *record, uint8 block_id) md->btm_level = xlrec->level; md->btm_fastroot = xlrec->fastroot; md->btm_fastlevel = xlrec->fastlevel; + /* Cannot log BTREE_MIN_VERSION index metapage without upgrade */ + Assert(md->btm_version >= BTREE_NOVAC_VERSION); md->btm_oldest_btpo_xact = xlrec->oldest_btpo_xact; md->btm_last_cleanup_num_heap_tuples = xlrec->last_cleanup_num_heap_tuples; diff --git a/src/backend/access/rmgrdesc/gistdesc.c b/src/backend/access/rmgrdesc/gistdesc.c index 767864b58e67..eccb6fd94286 100644 --- a/src/backend/access/rmgrdesc/gistdesc.c +++ b/src/backend/access/rmgrdesc/gistdesc.c @@ -26,10 +26,11 @@ out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec) static void out_gistxlogPageReuse(StringInfo buf, gistxlogPageReuse *xlrec) { - appendStringInfo(buf, "rel %u/%u/%u; blk %u; latestRemovedXid %u", + appendStringInfo(buf, "rel %u/%u/%u; blk %u; latestRemovedXid %u:%u", xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode, xlrec->block, - xlrec->latestRemovedXid); + EpochFromFullTransactionId(xlrec->latestRemovedFullXid), + XidFromFullTransactionId(xlrec->latestRemovedFullXid)); } static void @@ -50,8 +51,10 @@ out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec) static void out_gistxlogPageDelete(StringInfo buf, gistxlogPageDelete *xlrec) { - appendStringInfo(buf, "deleteXid %u; downlink %u", - xlrec->deleteXid, xlrec->downlinkOffset); + appendStringInfo(buf, "deleteXid %u:%u; downlink %u", + EpochFromFullTransactionId(xlrec->deleteXid), + XidFromFullTransactionId(xlrec->deleteXid), + xlrec->downlinkOffset); } void diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c index 935a4cd3a36c..4bddc3ffc229 100644 --- a/src/backend/access/rmgrdesc/heapdesc.c +++ b/src/backend/access/rmgrdesc/heapdesc.c @@ -86,9 +86,9 @@ heap_desc(StringInfo buf, XLogReaderState *record) int i; if (xlrec->flags & XLH_TRUNCATE_CASCADE) - appendStringInfo(buf, "cascade "); + appendStringInfoString(buf, "cascade "); if (xlrec->flags & XLH_TRUNCATE_RESTART_SEQS) - appendStringInfo(buf, "restart_seqs "); + appendStringInfoString(buf, "restart_seqs "); appendStringInfo(buf, "nrelids %u relids", xlrec->nrelids); for (i = 0; i < xlrec->nrelids; i++) appendStringInfo(buf, " %u", xlrec->relids[i]); diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c index 307321854152..77f492a80389 100644 --- a/src/backend/access/spgist/spgscan.c +++ b/src/backend/access/spgist/spgscan.c @@ -146,11 +146,6 @@ resetSpGistScanOpaque(SpGistScanOpaque so) { MemoryContext oldCtx; - /* - * clear traversal context before proceeding to the next scan; this must - * not happen before the freeScanStack above, else we get double-free - * crashes. - */ MemoryContextReset(so->traversalCxt); oldCtx = MemoryContextSwitchTo(so->traversalCxt); @@ -648,7 +643,7 @@ spgInnerTest(SpGistScanOpaque so, SpGistSearchItem *item, continue; /* - * Use infinity distances if innerConsistent() failed to return + * Use infinity distances if innerConsistentFn() failed to return * them or if is a NULL item (their distances are really unused). */ distances = out.distances ? out.distances[i] : so->infDistances; diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c index 2b1662a267dc..478d4c0d612e 100644 --- a/src/backend/access/spgist/spgvacuum.c +++ b/src/backend/access/spgist/spgvacuum.c @@ -842,7 +842,7 @@ spgvacuumscan(spgBulkDeleteState *bds) } } - /* Propagate local lastUsedPage cache to metablock */ + /* Propagate local lastUsedPages cache to metablock */ SpGistUpdateMetaPage(index); /* diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c index 921e50e630c7..62c367f0f321 100644 --- a/src/backend/access/table/tableam.c +++ b/src/backend/access/table/tableam.c @@ -19,11 +19,15 @@ */ #include "postgres.h" +#include + #include "access/heapam.h" /* for ss_* */ #include "access/tableam.h" #include "access/xact.h" +#include "optimizer/plancat.h" #include "storage/bufmgr.h" #include "storage/shmem.h" +#include "storage/smgr.h" /* GUC variables */ @@ -494,3 +498,160 @@ table_block_parallelscan_nextpage(Relation rel, ParallelBlockTableScanDesc pbsca return page; } + +/* ---------------------------------------------------------------------------- + * Helper functions to implement relation sizing for block oriented AMs. + * ---------------------------------------------------------------------------- + */ + +/* + * table_block_relation_size + * + * If a table AM uses the various relation forks as the sole place where data + * is stored, and if it uses them in the expected manner (e.g. the actual data + * is in the main fork rather than some other), it can use this implementation + * of the relation_size callback rather than implementing its own. + */ +uint64 +table_block_relation_size(Relation rel, ForkNumber forkNumber) +{ + uint64 nblocks = 0; + + /* Open it at the smgr level if not already done */ + RelationOpenSmgr(rel); + + /* InvalidForkNumber indicates returning the size for all forks */ + if (forkNumber == InvalidForkNumber) + { + for (int i = 0; i < MAX_FORKNUM; i++) + nblocks += smgrnblocks(rel->rd_smgr, i); + } + else + nblocks = smgrnblocks(rel->rd_smgr, forkNumber); + + return nblocks * BLCKSZ; +} + +/* + * table_block_relation_estimate_size + * + * This function can't be directly used as the implementation of the + * relation_estimate_size callback, because it has a few additional parameters. + * Instead, it is intended to be used as a helper function; the caller can + * pass through the arguments to its relation_estimate_size function plus the + * additional values required here. + * + * overhead_bytes_per_tuple should contain the approximate number of bytes + * of storage required to store a tuple above and beyond what is required for + * the tuple data proper. Typically, this would include things like the + * size of the tuple header and item pointer. This is only used for query + * planning, so a table AM where the value is not constant could choose to + * pass a "best guess". + * + * usable_bytes_per_page should contain the approximate number of bytes per + * page usable for tuple data, excluding the page header and any anticipated + * special space. + */ +void +table_block_relation_estimate_size(Relation rel, int32 *attr_widths, + BlockNumber *pages, double *tuples, + double *allvisfrac, + Size overhead_bytes_per_tuple, + Size usable_bytes_per_page) +{ + BlockNumber curpages; + BlockNumber relpages; + double reltuples; + BlockNumber relallvisible; + double density; + + /* it should have storage, so we can call the smgr */ + curpages = RelationGetNumberOfBlocks(rel); + + /* coerce values in pg_class to more desirable types */ + relpages = (BlockNumber) rel->rd_rel->relpages; + reltuples = (double) rel->rd_rel->reltuples; + relallvisible = (BlockNumber) rel->rd_rel->relallvisible; + + /* + * HACK: if the relation has never yet been vacuumed, use a minimum size + * estimate of 10 pages. The idea here is to avoid assuming a + * newly-created table is really small, even if it currently is, because + * that may not be true once some data gets loaded into it. Once a vacuum + * or analyze cycle has been done on it, it's more reasonable to believe + * the size is somewhat stable. + * + * (Note that this is only an issue if the plan gets cached and used again + * after the table has been filled. What we're trying to avoid is using a + * nestloop-type plan on a table that has grown substantially since the + * plan was made. Normally, autovacuum/autoanalyze will occur once enough + * inserts have happened and cause cached-plan invalidation; but that + * doesn't happen instantaneously, and it won't happen at all for cases + * such as temporary tables.) + * + * We approximate "never vacuumed" by "has relpages = 0", which means this + * will also fire on genuinely empty relations. Not great, but + * fortunately that's a seldom-seen case in the real world, and it + * shouldn't degrade the quality of the plan too much anyway to err in + * this direction. + * + * If the table has inheritance children, we don't apply this heuristic. + * Totally empty parent tables are quite common, so we should be willing + * to believe that they are empty. + */ + if (curpages < 10 && + relpages == 0 && + !rel->rd_rel->relhassubclass) + curpages = 10; + + /* report estimated # pages */ + *pages = curpages; + /* quick exit if rel is clearly empty */ + if (curpages == 0) + { + *tuples = 0; + *allvisfrac = 0; + return; + } + + /* estimate number of tuples from previous tuple density */ + if (relpages > 0) + density = reltuples / (double) relpages; + else + { + /* + * When we have no data because the relation was truncated, estimate + * tuple width from attribute datatypes. We assume here that the + * pages are completely full, which is OK for tables (since they've + * presumably not been VACUUMed yet) but is probably an overestimate + * for indexes. Fortunately get_relation_info() can clamp the + * overestimate to the parent table's size. + * + * Note: this code intentionally disregards alignment considerations, + * because (a) that would be gilding the lily considering how crude + * the estimate is, (b) it creates platform dependencies in the + * default plans which are kind of a headache for regression testing, + * and (c) different table AMs might use different padding schemes. + */ + int32 tuple_width; + + tuple_width = get_rel_data_width(rel, attr_widths); + tuple_width += overhead_bytes_per_tuple; + /* note: integer division is intentional here */ + density = usable_bytes_per_page / tuple_width; + } + *tuples = rint(density * (double) curpages); + + /* + * We use relallvisible as-is, rather than scaling it up like we do for + * the pages and tuples counts, on the theory that any pages added since + * the last VACUUM are most likely not marked all-visible. But costsize.c + * wants it converted to a fraction. + */ + if (relallvisible == 0 || curpages <= 0) + *allvisfrac = 0; + else if ((double) relallvisible >= curpages) + *allvisfrac = 1; + else + *allvisfrac = (double) relallvisible / curpages; +} diff --git a/src/backend/access/table/tableamapi.c b/src/backend/access/table/tableamapi.c index b2f587681071..eff1085a6df7 100644 --- a/src/backend/access/table/tableamapi.c +++ b/src/backend/access/table/tableamapi.c @@ -51,6 +51,7 @@ GetTableAmRoutine(Oid amhandler) Assert(routine->scan_begin != NULL); Assert(routine->scan_end != NULL); Assert(routine->scan_rescan != NULL); + Assert(routine->scan_getnextslot != NULL); Assert(routine->parallelscan_estimate != NULL); Assert(routine->parallelscan_initialize != NULL); @@ -62,7 +63,10 @@ GetTableAmRoutine(Oid amhandler) Assert(routine->index_fetch_tuple != NULL); Assert(routine->tuple_fetch_row_version != NULL); + Assert(routine->tuple_tid_valid != NULL); + Assert(routine->tuple_get_latest_tid != NULL); Assert(routine->tuple_satisfies_snapshot != NULL); + Assert(routine->compute_xid_horizon_for_tuples != NULL); Assert(routine->tuple_insert != NULL); @@ -89,6 +93,7 @@ GetTableAmRoutine(Oid amhandler) Assert(routine->index_validate_scan != NULL); Assert(routine->relation_size != NULL); + Assert(routine->relation_needs_toast_table != NULL); Assert(routine->relation_estimate_size != NULL); diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c index 70a4831c3721..9e16ab156aa8 100644 --- a/src/backend/access/transam/clog.c +++ b/src/backend/access/transam/clog.c @@ -155,7 +155,7 @@ static void TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids, * NB: this is a low-level routine and is NOT the preferred entry point * for most uses; functions in transam.c are the intended callers. * - * XXX Think about issuing FADVISE_WILLNEED on pages that we will need, + * XXX Think about issuing POSIX_FADV_WILLNEED on pages that we will need, * but aren't yet in cache, as well as hinting pages not to fall out of * cache yet. */ @@ -1001,7 +1001,7 @@ ExtendCLOG(TransactionId newestXact) * Remove all CLOG segments before the one holding the passed transaction ID * * Before removing any CLOG data, we must flush XLOG to disk, to ensure - * that any recently-emitted HEAP_FREEZE records have reached disk; otherwise + * that any recently-emitted FREEZE_PAGE records have reached disk; otherwise * a crash and restart might leave us with some unfrozen tuples referencing * removed CLOG data. We choose to emit a special TRUNCATE XLOG record too. * Replaying the deletion from XLOG is not critical, since the files could diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 4c83d3d0441e..f43fdda709b1 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -366,8 +366,10 @@ static bool SetOffsetVacuumLimit(bool is_startup); static bool find_multixact_start(MultiXactId multi, MultiXactOffset *result); static void WriteMZeroPageXlogRec(int pageno, uint8 info); static void WriteMTruncateXlogRec(Oid oldestMultiDB, - MultiXactId startOff, MultiXactId endOff, - MultiXactOffset startMemb, MultiXactOffset endMemb); + MultiXactId startTruncOff, + MultiXactId endTruncOff, + MultiXactOffset startTruncMemb, + MultiXactOffset endTruncMemb); /* @@ -2784,7 +2786,7 @@ ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members) /* * Multixact members can be removed once the multixacts that refer to them - * are older than every datminxmid. autovacuum_multixact_freeze_max_age and + * are older than every datminmxid. autovacuum_multixact_freeze_max_age and * vacuum_multixact_freeze_table_age work together to make sure we never have * too many multixacts; we hope that, at least under normal circumstances, * this will also be sufficient to keep us from using too many offsets. diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c index 7802d945dd6b..ab48b3d55539 100644 --- a/src/backend/access/transam/slru.c +++ b/src/backend/access/transam/slru.c @@ -621,7 +621,7 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno) result = endpos >= (off_t) (offset + BLCKSZ); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) { slru_errcause = SLRU_CLOSE_FAILED; slru_errno = errno; @@ -697,7 +697,7 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno) } pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) { slru_errcause = SLRU_CLOSE_FAILED; slru_errno = errno; @@ -869,7 +869,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruFlush fdata) if (!fdata) { pgstat_report_wait_start(WAIT_EVENT_SLRU_SYNC); - if (ctl->do_fsync && pg_fsync(fd)) + if (ctl->do_fsync && pg_fsync(fd) != 0) { pgstat_report_wait_end(); slru_errcause = SLRU_FSYNC_FAILED; @@ -879,7 +879,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruFlush fdata) } pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) { slru_errcause = SLRU_CLOSE_FAILED; slru_errno = errno; @@ -1146,7 +1146,7 @@ SimpleLruFlush(SlruCtl ctl, bool allow_redirtied) for (i = 0; i < fdata.num_files; i++) { pgstat_report_wait_start(WAIT_EVENT_SLRU_FLUSH_SYNC); - if (ctl->do_fsync && pg_fsync(fdata.fd[i])) + if (ctl->do_fsync && pg_fsync(fdata.fd[i]) != 0) { slru_errcause = SLRU_FSYNC_FAILED; slru_errno = errno; @@ -1155,7 +1155,7 @@ SimpleLruFlush(SlruCtl ctl, bool allow_redirtied) } pgstat_report_wait_end(); - if (CloseTransientFile(fdata.fd[i])) + if (CloseTransientFile(fdata.fd[i]) != 0) { slru_errcause = SLRU_CLOSE_FAILED; slru_errno = errno; diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c index cbd9b2cee19b..c2ba480c7037 100644 --- a/src/backend/access/transam/timeline.c +++ b/src/backend/access/transam/timeline.c @@ -371,7 +371,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI, pgstat_report_wait_end(); } - if (CloseTransientFile(srcfd)) + if (CloseTransientFile(srcfd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); @@ -415,7 +415,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI, errmsg("could not fsync file \"%s\": %m", tmppath))); pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tmppath))); @@ -493,7 +493,7 @@ writeTimeLineHistoryFile(TimeLineID tli, char *content, int size) errmsg("could not fsync file \"%s\": %m", tmppath))); pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tmppath))); diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 0b26d777ba1f..da8a81a248f2 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -1339,7 +1339,7 @@ ReadTwoPhaseFile(TransactionId xid, bool missing_ok) pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index a2dd8454822f..60eafe905e6d 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -2628,7 +2628,7 @@ StartTransaction(void) /* * Advertise it in the proc array. We assume assignment of - * LocalTransactionID is atomic, and the backendId should be set already. + * localTransactionId is atomic, and the backendId should be set already. */ Assert(MyProc->backendId == vxid.backendId); MyProc->lxid = vxid.localTransactionId; diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 599347ccc968..6a1172b2a4d5 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1838,11 +1838,11 @@ WaitXLogInsertionsToFinish(XLogRecPtr upto) do { /* - * See if this insertion is in progress. LWLockWait will wait for - * the lock to be released, or for the 'value' to be set by a - * LWLockUpdateVar call. When a lock is initially acquired, its - * value is 0 (InvalidXLogRecPtr), which means that we don't know - * where it's inserting yet. We will have to wait for it. If + * See if this insertion is in progress. LWLockWaitForVar will + * wait for the lock to be released, or for the 'value' to be set + * by a LWLockUpdateVar call. When a lock is initially acquired, + * its value is 0 (InvalidXLogRecPtr), which means that we don't + * know where it's inserting yet. We will have to wait for it. If * it's a small insertion, the record will most likely fit on the * same page and the inserter will release the lock without ever * calling LWLockUpdateVar. But if it has to sleep, it will @@ -3375,7 +3375,7 @@ XLogFileInit(XLogSegNo logsegno, bool *use_existent, bool use_lock) } pgstat_report_wait_end(); - if (close(fd)) + if (close(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tmppath))); @@ -3543,12 +3543,12 @@ XLogFileCopy(XLogSegNo destsegno, TimeLineID srcTLI, XLogSegNo srcsegno, errmsg("could not fsync file \"%s\": %m", tmppath))); pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tmppath))); - if (CloseTransientFile(srcfd)) + if (CloseTransientFile(srcfd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); @@ -3845,7 +3845,7 @@ XLogFileClose(void) (void) posix_fadvise(openLogFile, 0, 0, POSIX_FADV_DONTNEED); #endif - if (close(openLogFile)) + if (close(openLogFile) != 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", @@ -4633,7 +4633,7 @@ WriteControlFile(void) XLOG_CONTROL_FILE))); pgstat_report_wait_end(); - if (close(fd)) + if (close(fd) != 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", @@ -5298,7 +5298,7 @@ BootStrapXLOG(void) errmsg("could not fsync bootstrap write-ahead log file: %m"))); pgstat_report_wait_end(); - if (close(openLogFile)) + if (close(openLogFile) != 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not close bootstrap write-ahead log file: %m"))); @@ -5601,7 +5601,7 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog) fd = XLogFileInit(startLogSegNo, &use_existent, true); - if (close(fd)) + if (close(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", @@ -6111,7 +6111,10 @@ recoveryApplyDelay(XLogReaderState *record) TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime, &secs, µsecs); - /* NB: We're ignoring waits below min_apply_delay's resolution. */ + /* + * NB: We're ignoring waits below recovery_min_apply_delay's + * resolution. + */ if (secs <= 0 && microsecs / 1000 <= 0) break; @@ -9729,7 +9732,7 @@ CreateRestartPoint(int flags) /* * Update pg_control, using current time. Check that it still shows - * IN_ARCHIVE_RECOVERY state and an older checkpoint, else do nothing; + * DB_IN_ARCHIVE_RECOVERY state and an older checkpoint, else do nothing; * this is a quick hack to make sure nothing really bad happens if somehow * we get here after the end-of-recovery checkpoint. */ diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c index ed973b3c8c3e..2dfe135a07eb 100644 --- a/src/backend/access/transam/xlogreader.c +++ b/src/backend/access/transam/xlogreader.c @@ -31,6 +31,7 @@ #endif #ifndef FRONTEND +#include "miscadmin.h" #include "utils/memutils.h" #endif @@ -829,20 +830,10 @@ XLogReaderValidatePageHeader(XLogReaderState *state, XLogRecPtr recptr, if (state->system_identifier && longhdr->xlp_sysid != state->system_identifier) { - char fhdrident_str[32]; - char sysident_str[32]; - - /* - * Format sysids separately to keep platform-dependent format code - * out of the translatable message string. - */ - snprintf(fhdrident_str, sizeof(fhdrident_str), UINT64_FORMAT, - longhdr->xlp_sysid); - snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT, - state->system_identifier); report_invalid_record(state, - "WAL file is from different database system: WAL file database system identifier is %s, pg_control database system identifier is %s", - fhdrident_str, sysident_str); + "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu", + (unsigned long long) longhdr->xlp_sysid, + (unsigned long long) state->system_identifier); return false; } else if (longhdr->xlp_seg_size != state->wal_segment_size) @@ -1569,3 +1560,37 @@ zstd_decompress_backupblock(const char *source, int32 slen, char *dest, "binary not compiled with ZSTD support"); return false; } + +#ifndef FRONTEND + +/* + * Extract the FullTransactionId from a WAL record. + */ +FullTransactionId +XLogRecGetFullXid(XLogReaderState *record) +{ + TransactionId xid, + next_xid; + uint32 epoch; + + /* + * This function is only safe during replay, because it depends on the + * replay state. See AdvanceNextFullTransactionIdPastXid() for more. + */ + Assert(AmStartupProcess() || !IsUnderPostmaster); + + xid = XLogRecGetXid(record); + next_xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); + epoch = EpochFromFullTransactionId(ShmemVariableCache->nextFullXid); + + /* + * If xid is numerically greater than next_xid, it has to be from the + * last epoch. + */ + if (unlikely(xid > next_xid)) + --epoch; + + return FullTransactionIdFromEpochAndXid(epoch, xid); +} + +#endif diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 3a684f7f7b89..a0b21df60442 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -724,7 +724,7 @@ DefineAttr(char *name, char *type, int attnum, int nullness) namestrcpy(&attrtypes[attnum]->attname, name); elog(DEBUG4, "column %s %s", NameStr(attrtypes[attnum]->attname), type); - attrtypes[attnum]->attnum = attnum + 1; /* fillatt */ + attrtypes[attnum]->attnum = attnum + 1; typeoid = gettype(type); diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 7b25fbc516ed..d0a3b4c21372 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -1770,7 +1770,7 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, /* * This processes attributes, but expects to be called from - * ExecGrant_Relation, not directly from ExecGrantStmt. + * ExecGrant_Relation, not directly from ExecuteGrantStmt. */ static void ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index d1ba6b14210f..86c763ad24d2 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -568,6 +568,7 @@ findDependentObjects(const ObjectAddress *object, ObjectIdGetDatum(object->objectId)); if (object->objectSubId != 0) { + /* Consider only dependencies of this sub-object */ ScanKeyInit(&key[2], Anum_pg_depend_objsubid, BTEqualStrategyNumber, F_INT4EQ, @@ -575,7 +576,10 @@ findDependentObjects(const ObjectAddress *object, nkeys = 3; } else + { + /* Consider dependencies of this object and any sub-objects it has */ nkeys = 2; + } scan = systable_beginscan(*depRel, DependDependerIndexId, true, NULL, nkeys, key); @@ -592,6 +596,18 @@ findDependentObjects(const ObjectAddress *object, otherObject.objectId = foundDep->refobjid; otherObject.objectSubId = foundDep->refobjsubid; + /* + * When scanning dependencies of a whole object, we may find rows + * linking sub-objects of the object to the object itself. (Normally, + * such a dependency is implicit, but we must make explicit ones in + * some cases involving partitioning.) We must ignore such rows to + * avoid infinite recursion. + */ + if (otherObject.classId == object->classId && + otherObject.objectId == object->objectId && + object->objectSubId == 0) + continue; + switch (foundDep->deptype) { case DEPENDENCY_NORMAL: @@ -879,6 +895,16 @@ findDependentObjects(const ObjectAddress *object, otherObject.objectId = foundDep->objid; otherObject.objectSubId = foundDep->objsubid; + /* + * If what we found is a sub-object of the current object, just ignore + * it. (Normally, such a dependency is implicit, but we must make + * explicit ones in some cases involving partitioning.) + */ + if (otherObject.classId == object->classId && + otherObject.objectId == object->objectId && + object->objectSubId == 0) + continue; + /* * Must lock the dependent object before recursing to it. */ @@ -1635,8 +1661,10 @@ recordDependencyOnExpr(const ObjectAddress *depender, * As above, but only one relation is expected to be referenced (with * varno = 1 and varlevelsup = 0). Pass the relation OID instead of a * range table. An additional frammish is that dependencies on that - * relation (or its component columns) will be marked with 'self_behavior', - * whereas 'behavior' is used for everything else. + * relation's component columns will be marked with 'self_behavior', + * whereas 'behavior' is used for everything else; also, if 'reverse_self' + * is true, those dependencies are reversed so that the columns are made + * to depend on the table not vice versa. * * NOTE: the caller should ensure that a whole-table dependency on the * specified relation is created separately, if one is needed. In particular, @@ -1649,7 +1677,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, - bool ignore_self) + bool reverse_self) { find_expr_references_context context; RangeTblEntry rte; @@ -1673,7 +1701,8 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, eliminate_duplicate_dependencies(context.addrs); /* Separate self-dependencies if necessary */ - if (behavior != self_behavior && context.addrs->numrefs > 0) + if ((behavior != self_behavior || reverse_self) && + context.addrs->numrefs > 0) { ObjectAddresses *self_addrs; ObjectAddress *outobj; @@ -1704,11 +1733,23 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, } context.addrs->numrefs = outrefs; - /* Record the self-dependencies */ - if (!ignore_self) + /* Record the self-dependencies with the appropriate direction */ + if (!reverse_self) recordMultipleDependencies(depender, self_addrs->refs, self_addrs->numrefs, self_behavior); + else + { + /* Can't use recordMultipleDependencies, so do it the hard way */ + int selfref; + + for (selfref = 0; selfref < self_addrs->numrefs; selfref++) + { + ObjectAddress *thisobj = self_addrs->refs + selfref; + + recordDependencyOn(thisobj, depender, self_behavior); + } + } free_object_addresses(self_addrs); } diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index faa14585a417..7f13125f4900 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -151,7 +151,6 @@ static void SetRelationNumChecks(Relation rel, int numchecks); static Node *cookConstraint(ParseState *pstate, Node *raw_constraint, char *relname); -static List *insert_ordered_unique_oid(List *list, Oid datum); /* ---------------------------------------------------------------- @@ -707,7 +706,7 @@ CheckAttributeType(const char *attname, errmsg("composite type %s cannot be made a member of itself", format_type_be(atttypid)))); - containing_rowtypes = lcons_oid(atttypid, containing_rowtypes); + containing_rowtypes = lappend_oid(containing_rowtypes, atttypid); relation = relation_open(get_typ_typrelid(atttypid), AccessShareLock); @@ -727,7 +726,7 @@ CheckAttributeType(const char *attname, relation_close(relation, AccessShareLock); - containing_rowtypes = list_delete_first(containing_rowtypes); + containing_rowtypes = list_delete_last(containing_rowtypes); } else if (OidIsValid((att_typelem = get_element_type(atttypid)))) { @@ -3918,55 +3917,19 @@ heap_truncate_find_FKs(List *relationIds) if (!list_member_oid(relationIds, con->confrelid)) continue; - /* Add referencer unless already in input or result list */ + /* Add referencer to result, unless present in input list */ if (!list_member_oid(relationIds, con->conrelid)) - result = insert_ordered_unique_oid(result, con->conrelid); + result = lappend_oid(result, con->conrelid); } systable_endscan(fkeyScan); table_close(fkeyRel, AccessShareLock); - return result; -} - -/* - * insert_ordered_unique_oid - * Insert a new Oid into a sorted list of Oids, preserving ordering, - * and eliminating duplicates - * - * Building the ordered list this way is O(N^2), but with a pretty small - * constant, so for the number of entries we expect it will probably be - * faster than trying to apply qsort(). It seems unlikely someone would be - * trying to truncate a table with thousands of dependent tables ... - */ -static List * -insert_ordered_unique_oid(List *list, Oid datum) -{ - ListCell *prev; - - /* Does the datum belong at the front? */ - if (list == NIL || datum < linitial_oid(list)) - return lcons_oid(datum, list); - /* Does it match the first entry? */ - if (datum == linitial_oid(list)) - return list; /* duplicate, so don't insert */ - /* No, so find the entry it belongs after */ - prev = list_head(list); - for (;;) - { - ListCell *curr = lnext(prev); - - if (curr == NULL || datum < lfirst_oid(curr)) - break; /* it belongs after 'prev', before 'curr' */ - - if (datum == lfirst_oid(curr)) - return list; /* duplicate, so don't insert */ + /* Now sort and de-duplicate the result list */ + list_sort(result, list_oid_cmp); + list_deduplicate_oid(result); - prev = curr; - } - /* Insert datum into list after 'prev' */ - lappend_cell_oid(list, prev, datum); - return list; + return result; } /* @@ -4062,16 +4025,36 @@ StorePartitionKey(Relation rel, } /* - * Anything mentioned in the expressions. We must ignore the column - * references, which will depend on the table itself; there is no separate - * partition key object. + * The partitioning columns are made internally dependent on the table, + * because we cannot drop any of them without dropping the whole table. + * (ATExecDropColumn independently enforces that, but it's not bulletproof + * so we need the dependencies too.) + */ + for (i = 0; i < partnatts; i++) + { + if (partattrs[i] == 0) + continue; /* ignore expressions here */ + + referenced.classId = RelationRelationId; + referenced.objectId = RelationGetRelid(rel); + referenced.objectSubId = partattrs[i]; + + recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); + } + + /* + * Also consider anything mentioned in partition expressions. External + * references (e.g. functions) get NORMAL dependencies. Table columns + * mentioned in the expressions are handled the same as plain partitioning + * columns, i.e. they become internally dependent on the whole table. */ if (partexprs) recordDependencyOnSingleRelExpr(&myself, (Node *) partexprs, RelationGetRelid(rel), DEPENDENCY_NORMAL, - DEPENDENCY_AUTO, true); + DEPENDENCY_INTERNAL, + true /* reverse the self-deps */ ); /* * We must invalidate the relcache so that the next diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index fff19580c63b..fa5b1bbedb58 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -397,7 +397,7 @@ ConstructTupleDescriptor(Relation heapRelation, if (indexpr_item == NULL) /* shouldn't happen */ elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(indexInfo->ii_Expressions, indexpr_item); /* * Lookup the expression type in pg_type for the type length etc. @@ -447,7 +447,7 @@ ConstructTupleDescriptor(Relation heapRelation, if (colnames_item == NULL) /* shouldn't happen */ elog(ERROR, "too few entries in colnames list"); namestrcpy(&to->attname, (const char *) lfirst(colnames_item)); - colnames_item = lnext(colnames_item); + colnames_item = lnext(indexColNames, colnames_item); /* * Check the opclass and index AM to see if either provides a keytype @@ -1273,7 +1273,8 @@ Oid index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char *newName) { Relation indexRelation; - IndexInfo *indexInfo; + IndexInfo *oldInfo, + *newInfo; Oid newIndexId = InvalidOid; HeapTuple indexTuple, classTuple; @@ -1284,11 +1285,22 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char int2vector *indcoloptions; bool isnull; List *indexColNames = NIL; + List *indexExprs = NIL; + List *indexPreds = NIL; indexRelation = index_open(oldIndexId, RowExclusiveLock); - /* New index uses the same index information as old index */ - indexInfo = BuildIndexInfo(indexRelation); + /* The new index needs some information from the old index */ + oldInfo = BuildIndexInfo(indexRelation); + + /* + * Concurrent build of an index with exclusion constraints is not + * supported. + */ + if (oldInfo->ii_ExclusionOps != NULL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("concurrent index creation for exclusion constraints is not supported"))); /* Get the array of class and column options IDs from index info */ indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldIndexId)); @@ -1312,14 +1324,64 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char Anum_pg_class_reloptions, &isnull); /* - * Extract the list of column names to be used for the index creation. + * Fetch the list of expressions and predicates directly from the + * catalogs. This cannot rely on the information from IndexInfo of the + * old index as these have been flattened for the planner. */ - for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + if (oldInfo->ii_Expressions != NIL) + { + Datum exprDatum; + char *exprString; + + exprDatum = SysCacheGetAttr(INDEXRELID, indexTuple, + Anum_pg_index_indexprs, &isnull); + Assert(!isnull); + exprString = TextDatumGetCString(exprDatum); + indexExprs = (List *) stringToNode(exprString); + pfree(exprString); + } + if (oldInfo->ii_Predicate != NIL) + { + Datum predDatum; + char *predString; + + predDatum = SysCacheGetAttr(INDEXRELID, indexTuple, + Anum_pg_index_indpred, &isnull); + Assert(!isnull); + predString = TextDatumGetCString(predDatum); + indexPreds = (List *) stringToNode(predString); + + /* Also convert to implicit-AND format */ + indexPreds = make_ands_implicit((Expr *) indexPreds); + pfree(predString); + } + + /* + * Build the index information for the new index. Note that rebuild of + * indexes with exclusion constraints is not supported, hence there is no + * need to fill all the ii_Exclusion* fields. + */ + newInfo = makeIndexInfo(oldInfo->ii_NumIndexAttrs, + oldInfo->ii_NumIndexKeyAttrs, + oldInfo->ii_Am, + indexExprs, + indexPreds, + oldInfo->ii_Unique, + false, /* not ready for inserts */ + true); + + /* + * Extract the list of column names and the column numbers for the new + * index information. All this information will be used for the index + * creation. + */ + for (int i = 0; i < oldInfo->ii_NumIndexAttrs; i++) { TupleDesc indexTupDesc = RelationGetDescr(indexRelation); Form_pg_attribute att = TupleDescAttr(indexTupDesc, i); indexColNames = lappend(indexColNames, NameStr(att->attname)); + newInfo->ii_IndexAttrNumbers[i] = oldInfo->ii_IndexAttrNumbers[i]; } /* @@ -1335,7 +1397,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char InvalidOid, /* parentIndexRelid */ InvalidOid, /* parentConstraintId */ InvalidOid, /* relFileNode */ - indexInfo, + newInfo, indexColNames, indexRelation->rd_rel->relam, indexRelation->rd_rel->reltablespace, @@ -2547,7 +2609,7 @@ FormIndexDatum(struct IndexInfo *indexInfo, iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexpr_item), GetPerTupleExprContext(estate), &isNull); - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(indexInfo->ii_ExpressionsState, indexpr_item); } values[i] = iDatum; isnull[i] = isNull; diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 361090a76b3a..90ef5a5eed3d 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -517,9 +517,9 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation) * permission on the target namespace, this function will instead signal * an ERROR. * - * If non-NULL, *existing_oid is set to the OID of any existing relation with - * the same name which already exists in that namespace, or to InvalidOid if - * no such relation exists. + * If non-NULL, *existing_relation_id is set to the OID of any existing relation + * with the same name which already exists in that namespace, or to InvalidOid + * if no such relation exists. * * If lockmode != NoLock, the specified lock mode is acquired on the existing * relation, if any, provided that the current user owns the target relation. @@ -2422,7 +2422,7 @@ TSParserIsVisible(Oid prsId) /* * get_ts_dict_oid - find a TS dictionary by possibly qualified name * - * If not found, returns InvalidOid if failOK, else throws error + * If not found, returns InvalidOid if missing_ok, else throws error */ Oid get_ts_dict_oid(List *names, bool missing_ok) @@ -3489,7 +3489,7 @@ OverrideSearchPathMatchesCurrent(OverrideSearchPath *path) if (path->addTemp) { if (lc && lfirst_oid(lc) == myTempNamespace) - lc = lnext(lc); + lc = lnext(activeSearchPath, lc); else return false; } @@ -3497,7 +3497,7 @@ OverrideSearchPathMatchesCurrent(OverrideSearchPath *path) if (path->addCatalog) { if (lc && lfirst_oid(lc) == PG_CATALOG_NAMESPACE) - lc = lnext(lc); + lc = lnext(activeSearchPath, lc); else return false; } @@ -3508,7 +3508,7 @@ OverrideSearchPathMatchesCurrent(OverrideSearchPath *path) foreach(lcp, path->schemas) { if (lc && lfirst_oid(lc) == lfirst_oid(lcp)) - lc = lnext(lc); + lc = lnext(activeSearchPath, lc); else return false; } diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index e96620e4018f..07b82233486e 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -275,7 +275,7 @@ has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr) /* Find all attributes referenced */ pull_varattnos(expr, 1, &expr_attrs); - partexprs_item = lnext(partexprs_item); + partexprs_item = lnext(partexprs, partexprs_item); if (bms_overlap(attnums, expr_attrs)) { diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 809ebd338e0d..0d5af3732275 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -737,7 +737,7 @@ AggregateCreate(const char *aggName, * Create dependencies for the aggregate (above and beyond those already * made by ProcedureCreate). Note: we don't need an explicit dependency * on aggTransType since we depend on it indirectly through transfn. - * Likewise for aggmTransType using the mtransfunc, if it exists. + * Likewise for aggmTransType using the mtransfn, if it exists. * * If we're replacing an existing definition, ProcedureCreate deleted all * our existing dependencies, so we have to do the same things here either diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 628b517c7a44..6f3ffe614c42 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -769,10 +769,11 @@ sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId) /* * Collect a list of OIDs of all sequences owned by the specified relation, - * and column if specified. + * and column if specified. If deptype is not zero, then only find sequences + * with the specified dependency type. */ -List * -getOwnedSequences(Oid relid, AttrNumber attnum) +static List * +getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype) { List *result = NIL; Relation depRel; @@ -814,7 +815,8 @@ getOwnedSequences(Oid relid, AttrNumber attnum) (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) && get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE) { - result = lappend_oid(result, deprec->objid); + if (!deptype || deprec->deptype == deptype) + result = lappend_oid(result, deprec->objid); } } @@ -826,17 +828,32 @@ getOwnedSequences(Oid relid, AttrNumber attnum) } /* - * Get owned sequence, error if not exactly one. + * Collect a list of OIDs of all sequences owned (identity or serial) by the + * specified relation. + */ +List * +getOwnedSequences(Oid relid) +{ + return getOwnedSequences_internal(relid, 0, 0); +} + +/* + * Get owned identity sequence, error if not exactly one. */ Oid -getOwnedSequence(Oid relid, AttrNumber attnum) +getIdentitySequence(Oid relid, AttrNumber attnum, bool missing_ok) { - List *seqlist = getOwnedSequences(relid, attnum); + List *seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL); if (list_length(seqlist) > 1) elog(ERROR, "more than one owned sequence found"); else if (list_length(seqlist) < 1) - elog(ERROR, "no owned sequence found"); + { + if (missing_ok) + return InvalidOid; + else + elog(ERROR, "no owned sequence found"); + } return linitial_oid(seqlist); } diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c index 56ddd84c5bc9..d8966f238684 100644 --- a/src/backend/catalog/pg_inherits.c +++ b/src/backend/catalog/pg_inherits.c @@ -38,7 +38,7 @@ typedef struct SeenRelsEntry { Oid rel_id; /* relation oid */ - ListCell *numparents_cell; /* corresponding list cell */ + int list_index; /* its position in output list(s) */ } SeenRelsEntry; /* @@ -215,7 +215,9 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents) * indirect children. We can use a single list as both the record of * already-found rels and the agenda of rels yet to be scanned for more * children. This is a bit tricky but works because the foreach() macro - * doesn't fetch the next list element until the bottom of the loop. + * doesn't fetch the next list element until the bottom of the loop. Note + * that we can't keep pointers into the output lists; but an index is + * sufficient. */ rels_list = list_make1_oid(parentrelId); rel_numparents = list_make1_int(0); @@ -246,14 +248,18 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents) if (found) { /* if the rel is already there, bump number-of-parents counter */ - lfirst_int(hash_entry->numparents_cell)++; + ListCell *numparents_cell; + + numparents_cell = list_nth_cell(rel_numparents, + hash_entry->list_index); + lfirst_int(numparents_cell)++; } else { /* if it's not there, add it. expect 1 parent, initially. */ + hash_entry->list_index = list_length(rels_list); rels_list = lappend_oid(rels_list, child_oid); rel_numparents = lappend_int(rel_numparents, 1); - hash_entry->numparents_cell = rel_numparents->tail; } } } diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index a14ba5e79f55..bdb2730f7037 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -553,11 +553,9 @@ ProcedureCreate(const char *procedureName, Assert(list_length(oldDefaults) == oldproc->pronargdefaults); /* new list can have more defaults than old, advance over 'em */ - newlc = list_head(parameterDefaults); - for (i = list_length(parameterDefaults) - oldproc->pronargdefaults; - i > 0; - i--) - newlc = lnext(newlc); + newlc = list_nth_cell(parameterDefaults, + list_length(parameterDefaults) - + oldproc->pronargdefaults); foreach(oldlc, oldDefaults) { @@ -572,7 +570,7 @@ ProcedureCreate(const char *procedureName, errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); - newlc = lnext(newlc); + newlc = lnext(parameterDefaults, newlc); } } diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c index dceade68392e..a7a380cd2ccd 100644 --- a/src/backend/catalog/pg_publication.c +++ b/src/backend/catalog/pg_publication.c @@ -484,7 +484,6 @@ pg_get_publication_tables(PG_FUNCTION_ARGS) char *pubname = text_to_cstring(PG_GETARG_TEXT_PP(0)); Publication *publication; List *tables; - ListCell **lcp; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) @@ -502,22 +501,19 @@ pg_get_publication_tables(PG_FUNCTION_ARGS) tables = GetAllTablesPublicationRelations(); else tables = GetPublicationRelations(publication->oid); - lcp = (ListCell **) palloc(sizeof(ListCell *)); - *lcp = list_head(tables); - funcctx->user_fctx = (void *) lcp; + funcctx->user_fctx = (void *) tables; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); - lcp = (ListCell **) funcctx->user_fctx; + tables = (List *) funcctx->user_fctx; - while (*lcp != NULL) + if (funcctx->call_cntr < list_length(tables)) { - Oid relid = lfirst_oid(*lcp); + Oid relid = list_nth_oid(tables, funcctx->call_cntr); - *lcp = lnext(*lcp); SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid)); } diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index b763bb2458f7..8693bf539697 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -622,7 +622,8 @@ do_analyze_rel(Relation onerel, VacuumParams *params, if (indexpr_item == NULL) /* shouldn't happen */ elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(indexInfo->ii_Expressions, + indexpr_item); thisdata->vacattrstats[tcnt] = examine_attribute(Irel[ind], i + 1, indexkey, elevel); if (thisdata->vacattrstats[tcnt] != NULL) diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 10badd75e00a..bc1311b5a8e7 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -690,36 +690,22 @@ Datum pg_listening_channels(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; - ListCell **lcp; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { - MemoryContext oldcontext; - /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); - - /* switch to memory context appropriate for multiple function calls */ - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - - /* allocate memory for user context */ - lcp = (ListCell **) palloc(sizeof(ListCell *)); - *lcp = list_head(listenChannels); - funcctx->user_fctx = (void *) lcp; - - MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); - lcp = (ListCell **) funcctx->user_fctx; - while (*lcp != NULL) + if (funcctx->call_cntr < list_length(listenChannels)) { - char *channel = (char *) lfirst(*lcp); + char *channel = (char *) list_nth(listenChannels, + funcctx->call_cntr); - *lcp = lnext(*lcp); SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(channel)); } @@ -1036,23 +1022,20 @@ static void Exec_UnlistenCommit(const char *channel) { ListCell *q; - ListCell *prev; if (Trace_notify) elog(DEBUG1, "Exec_UnlistenCommit(%s,%d)", channel, MyProcPid); - prev = NULL; foreach(q, listenChannels) { char *lchan = (char *) lfirst(q); if (strcmp(lchan, channel) == 0) { - listenChannels = list_delete_cell(listenChannels, q, prev); + listenChannels = foreach_delete_current(listenChannels, q); pfree(lchan); break; } - prev = q; } /* @@ -1312,9 +1295,9 @@ asyncQueueNotificationToEntry(Notification *n, AsyncQueueEntry *qe) * database OID in order to fill the page. So every page is always used up to * the last byte which simplifies reading the page later. * - * We are passed the list cell containing the next notification to write - * and return the first still-unwritten cell back. Eventually we will return - * NULL indicating all is done. + * We are passed the list cell (in pendingNotifies) containing the next + * notification to write and return the first still-unwritten cell back. + * Eventually we will return NULL indicating all is done. * * We are holding AsyncQueueLock already from the caller and grab AsyncCtlLock * locally in this function. @@ -1363,7 +1346,7 @@ asyncQueueAddEntries(ListCell *nextNotify) if (offset + qe.length <= QUEUE_PAGESIZE) { /* OK, so advance nextNotify past this item */ - nextNotify = lnext(nextNotify); + nextNotify = lnext(pendingNotifies, nextNotify); } else { diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 084b753f88a3..dfcdbeba4bdb 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -1853,7 +1853,7 @@ get_tables_to_cluster(MemoryContext cluster_context) rvtc = (RelToCluster *) palloc(sizeof(RelToCluster)); rvtc->tableOid = index->indrelid; rvtc->indexOid = index->indexrelid; - rvs = lcons(rvtc, rvs); + rvs = lappend(rvs, rvtc); MemoryContextSwitchTo(old_context); } diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index eabeff3c564b..538fd855ddaf 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -76,6 +76,18 @@ CreateConversionCommand(CreateConversionStmt *stmt) errmsg("destination encoding \"%s\" does not exist", to_encoding_name))); + /* + * We consider conversions to or from SQL_ASCII to be meaningless. (If + * you wish to change this, note that pg_do_encoding_conversion() and its + * sister functions have hard-wired fast paths for any conversion in which + * the source or target encoding is SQL_ASCII, so that an encoding + * conversion function declared for such a case will never be used.) + */ + if (from_encoding == PG_SQL_ASCII || to_encoding == PG_SQL_ASCII) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("encoding conversion to or from \"SQL_ASCII\" is not supported"))); + /* * Check the existence of the conversion function. Function name could be * a qualified name. diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 0427c89374ea..6db8515840f1 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -3604,7 +3604,8 @@ CopyMultiInsertBufferFlush(CopyMultiInsertInfo *miinfo, * The buffer must be flushed before cleanup. */ static inline void -CopyMultiInsertBufferCleanup(CopyMultiInsertBuffer *buffer) +CopyMultiInsertBufferCleanup(CopyMultiInsertInfo *miinfo, + CopyMultiInsertBuffer *buffer) { int i; @@ -3620,6 +3621,9 @@ CopyMultiInsertBufferCleanup(CopyMultiInsertBuffer *buffer) for (i = 0; i < MAX_BUFFERED_TUPLES && buffer->slots[i] != NULL; i++) ExecDropSingleTupleTableSlot(buffer->slots[i]); + table_finish_bulk_insert(buffer->resultRelInfo->ri_RelationDesc, + miinfo->ti_options); + pfree(buffer); } @@ -3671,7 +3675,7 @@ CopyMultiInsertInfoFlush(CopyMultiInsertInfo *miinfo, ResultRelInfo *curr_rri) buffer = (CopyMultiInsertBuffer *) linitial(miinfo->multiInsertBuffers); } - CopyMultiInsertBufferCleanup(buffer); + CopyMultiInsertBufferCleanup(miinfo, buffer); miinfo->multiInsertBuffers = list_delete_first(miinfo->multiInsertBuffers); } } @@ -3685,7 +3689,7 @@ CopyMultiInsertInfoCleanup(CopyMultiInsertInfo *miinfo) ListCell *lc; foreach(lc, miinfo->multiInsertBuffers) - CopyMultiInsertBufferCleanup(lfirst(lc)); + CopyMultiInsertBufferCleanup(miinfo, lfirst(lc)); list_free(miinfo->multiInsertBuffers); } @@ -4651,9 +4655,6 @@ CopyFrom(CopyState cstate) { if (!CopyMultiInsertInfoIsEmpty(&multiInsertInfo)) CopyMultiInsertInfoFlush(&multiInsertInfo, NULL); - - /* Tear down the multi-insert buffer data */ - CopyMultiInsertInfoCleanup(&multiInsertInfo); } /* Done, clean up */ @@ -4745,6 +4746,10 @@ CopyFrom(CopyState cstate) target_resultRelInfo->ri_FdwRoutine->EndForeignInsert(estate, target_resultRelInfo); + /* Tear down the multi-insert buffer data */ + if (insertMethod != CIM_SINGLE) + CopyMultiInsertInfoCleanup(&multiInsertInfo); + ExecCloseIndices(target_resultRelInfo); /* Close all the partitioned tables, leaf partitions, and their indices */ @@ -4758,8 +4763,6 @@ CopyFrom(CopyState cstate) FreeExecutorState(estate); - table_finish_bulk_insert(cstate->rel, ti_options); - return processed; } diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index adee9be411a3..c39b9ce5d69f 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -245,7 +245,7 @@ create_ctas_nodata(List *tlist, IntoClause *into, QueryDesc *queryDesc) if (lc) { colname = strVal(lfirst(lc)); - lc = lnext(lc); + lc = lnext(into->colNames, lc); } else colname = tle->resname; @@ -605,7 +605,7 @@ intorel_initplan(struct QueryDesc *queryDesc, int eflags) if (lc) { colname = strVal(lfirst(lc)); - lc = lnext(lc); + lc = lnext(into->colNames, lc); } else colname = NameStr(attribute->attname); diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index bfb9c8dc46eb..1d996555848d 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -150,6 +150,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) DefElem *downer = NULL; DefElem *dtemplate = NULL; DefElem *dencoding = NULL; + DefElem *dlocale = NULL; DefElem *dcollate = NULL; DefElem *dctype = NULL; DefElem *distemplate = NULL; @@ -211,6 +212,15 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) parser_errposition(pstate, defel->location))); dencoding = defel; } + else if (strcmp(defel->defname, "locale") == 0) + { + if (dlocale) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + dlocale = defel; + } else if (strcmp(defel->defname, "lc_collate") == 0) { if (dcollate) @@ -271,6 +281,12 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) parser_errposition(pstate, defel->location))); } + if (dlocale && (dcollate || dctype)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + errdetail("LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE."))); + if (downer && downer->arg) dbowner = defGetString(downer); if (dtemplate && dtemplate->arg) @@ -310,6 +326,11 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) errmsg("server encoding 'SQL_ASCII' is not supported"))); } } + if (dlocale && dlocale->arg) + { + dbcollate = defGetString(dlocale); + dbctype = defGetString(dlocale); + } if (dcollate && dcollate->arg) dbcollate = defGetString(dcollate); if (dctype && dctype->arg) @@ -2034,7 +2055,7 @@ get_db_info(const char *name, LOCKMODE lockmode, /* limit of frozen XIDs */ if (dbFrozenXidP) *dbFrozenXidP = dbform->datfrozenxid; - /* minimum MultixactId */ + /* minimum MultiXactId */ if (dbMinMultiP) *dbMinMultiP = dbform->datminmxid; /* default tablespace for this database */ diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index d40449e1dea4..525592a8ca9d 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -156,8 +156,7 @@ static void AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId); static event_trigger_command_tag_check_result check_ddl_tag(const char *tag); -static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag( - const char *tag); +static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(const char *tag); static void error_duplicate_filter_variable(const char *defname); static Datum filter_list_to_array(List *filterlist); static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname, diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 2c018e2c34d0..df252431377d 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -296,7 +296,7 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString, queryString, params, queryEnv); /* Separate plans with an appropriate separator */ - if (lnext(l) != NULL) + if (lnext(rewritten, l) != NULL) ExplainSeparatePlans(es); } } @@ -1123,7 +1123,7 @@ ExplainPrintJIT(ExplainState *es, int jit_flags, if (for_workers) appendStringInfo(es->str, "JIT for worker %u:\n", worker_num); else - appendStringInfo(es->str, "JIT:\n"); + appendStringInfoString(es->str, "JIT:\n"); es->indent += 1; ExplainPropertyInteger("Functions", NULL, ji->created_functions, es); diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 9377e1426708..0968fa200b0d 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -723,9 +723,21 @@ execute_sql_string(const char *sql) foreach(lc1, raw_parsetree_list) { RawStmt *parsetree = lfirst_node(RawStmt, lc1); + MemoryContext per_parsetree_context, + oldcontext; List *stmt_list; ListCell *lc2; + /* + * We do the work for each parsetree in a short-lived context, to + * limit the memory used when there are many commands in the string. + */ + per_parsetree_context = + AllocSetContextCreate(CurrentMemoryContext, + "execute_sql_string per-statement context", + ALLOCSET_DEFAULT_SIZES); + oldcontext = MemoryContextSwitchTo(per_parsetree_context); + /* Be sure parser can see any DDL done so far */ CommandCounterIncrement(); @@ -778,6 +790,10 @@ execute_sql_string(const char *sql) PopActiveSnapshot(); } + + /* Clean up per-parsetree context. */ + MemoryContextSwitchTo(oldcontext); + MemoryContextDelete(per_parsetree_context); } /* Be sure to advance the command counter after the last script command */ diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index 910d8ba5a513..992d683c176a 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -126,11 +126,10 @@ transformGenericOptions(Oid catalogId, { DefElem *od = lfirst(optcell); ListCell *cell; - ListCell *prev = NULL; /* * Find the element in resultOptions. We need this for validation in - * all cases. Also identify the previous element. + * all cases. */ foreach(cell, resultOptions) { @@ -138,8 +137,6 @@ transformGenericOptions(Oid catalogId, if (strcmp(def->defname, od->defname) == 0) break; - else - prev = cell; } /* @@ -156,7 +153,7 @@ transformGenericOptions(Oid catalogId, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("option \"%s\" not found", od->defname))); - resultOptions = list_delete_cell(resultOptions, cell, prev); + resultOptions = list_delete_cell(resultOptions, cell); break; case DEFELEM_SET: diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index f51f78bddcf2..8de45868791c 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -313,18 +313,8 @@ CheckIndexCompatible(Oid oldId, * contains only key attributes, thus we're filling ii_NumIndexAttrs and * ii_NumIndexKeyAttrs with same value. */ - indexInfo = makeNode(IndexInfo); - indexInfo->ii_NumIndexAttrs = numberOfAttributes; - indexInfo->ii_NumIndexKeyAttrs = numberOfAttributes; - indexInfo->ii_Expressions = NIL; - indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NULL; - indexInfo->ii_ExclusionOps = NULL; - indexInfo->ii_ExclusionProcs = NULL; - indexInfo->ii_ExclusionStrats = NULL; - indexInfo->ii_Am = accessMethodId; - indexInfo->ii_AmCache = NULL; - indexInfo->ii_Context = CurrentMemoryContext; + indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes, + accessMethodId, NIL, NIL, false, false, false); typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); @@ -980,27 +970,17 @@ DefineIndex(Oid relationId, /* * Prepare arguments for index_create, primarily an IndexInfo structure. - * Note that ii_Predicate must be in implicit-AND format. + * Note that predicates must be in implicit-AND format. In a concurrent + * build, mark it not-ready-for-inserts. */ - indexInfo = makeNode(IndexInfo); - indexInfo->ii_NumIndexAttrs = numberOfAttributes; - indexInfo->ii_NumIndexKeyAttrs = numberOfKeyAttributes; - indexInfo->ii_Expressions = NIL; /* for now */ - indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NULL; - indexInfo->ii_ExclusionOps = NULL; - indexInfo->ii_ExclusionProcs = NULL; - indexInfo->ii_ExclusionStrats = NULL; - indexInfo->ii_Unique = stmt->unique; - /* In a concurrent build, mark it not-ready-for-inserts */ - indexInfo->ii_ReadyForInserts = !stmt->concurrent; - indexInfo->ii_Concurrent = stmt->concurrent; - indexInfo->ii_BrokenHotChain = false; - indexInfo->ii_ParallelWorkers = 0; - indexInfo->ii_Am = accessMethodId; - indexInfo->ii_AmCache = NULL; - indexInfo->ii_Context = CurrentMemoryContext; + indexInfo = makeIndexInfo(numberOfAttributes, + numberOfKeyAttributes, + accessMethodId, + NIL, /* expressions, NIL for now */ + make_ands_implicit((Expr *) stmt->whereClause), + stmt->unique, + !stmt->concurrent, + stmt->concurrent); typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); @@ -2123,7 +2103,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo, indexInfo->ii_ExclusionOps[attn] = opid; indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid); indexInfo->ii_ExclusionStrats[attn] = strat; - nextExclOp = lnext(nextExclOp); + nextExclOp = lnext(exclusionOpNames, nextExclOp); } /* diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index 19199fe659d3..94cc35a24473 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -320,11 +320,11 @@ LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views context.nowait = nowait; context.viewowner = view->rd_rel->relowner; context.viewoid = reloid; - context.ancestor_views = lcons_oid(reloid, ancestor_views); + context.ancestor_views = lappend_oid(ancestor_views, reloid); LockViewRecurse_walker((Node *) viewquery, &context); - ancestor_views = list_delete_oid(ancestor_views, reloid); + (void) list_delete_last(context.ancestor_views); table_close(view, NoLock); } diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 2fc6c0fce0d0..d6df895baad9 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -27,9 +27,6 @@ * "create operator": * operators * - * Most of the parse-tree manipulation routines are defined in - * commands/manip.c. - * *------------------------------------------------------------------------- */ #include "postgres.h" diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index dd05c0437346..0bd5bc067c20 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -723,7 +723,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es, /* No need for CommandCounterIncrement, as ExplainOnePlan did it */ /* Separate plans with an appropriate separator */ - if (lnext(p) != NULL) + if (lnext(plan_list, p) != NULL) ExplainSeparatePlans(es); } diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 473d76a46ebb..5ede3450e15a 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -237,6 +237,14 @@ CreatePublication(CreatePublicationStmt *stmt) InvokeObjectPostCreateHook(PublicationRelationId, puboid, 0); + if (wal_level != WAL_LEVEL_LOGICAL) + { + ereport(WARNING, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("wal_level is insufficient to publish logical changes"), + errhint("Set wal_level to logical before creating subscriptions."))); + } + if (Gp_role == GP_ROLE_DISPATCH) { CdbDispatchUtilityStatement((Node *) stmt, diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index 9db82280287c..63219ad589f7 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -58,7 +58,7 @@ ExecSecLabelStmt(SecLabelStmt *stmt) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("no security label providers have been loaded"))); - if (lnext(list_head(label_provider_list)) != NULL) + if (list_length(label_provider_list) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("must specify provider when multiple security label providers have been loaded"))); diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index c1d94693c392..b7c526493cb6 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -489,6 +489,10 @@ RemoveStatisticsById(Oid statsOid) * * For MCV lists that's not the case, as those statistics store the datums * internally. In this case we simply reset the statistics value to NULL. + * + * Note that "type change" includes collation change, which means we can rely + * on the MCV list being consistent with the collation info in pg_attribute + * during estimation. */ void UpdateStatisticsForTypeChange(Oid statsOid, Oid relationOid, int attnum, diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 45ba2548ac57..ee03b0058594 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -2163,7 +2163,7 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, foreach(cell, rels) { Relation rel = (Relation) lfirst(cell); - List *seqlist = getOwnedSequences(RelationGetRelid(rel), 0); + List *seqlist = getOwnedSequences(RelationGetRelid(rel)); ListCell *seqcell; foreach(seqcell, seqlist) @@ -2552,13 +2552,13 @@ List * MergeAttributes(List *schema, List *supers, char relpersistence, bool is_partition, List **supconstr, bool gp_style_alter_part) { - ListCell *entry; List *inhSchema = NIL; List *constraints = NIL; bool have_bogus_defaults = false; int child_attno; static Node bogus_marker = {0}; /* marks conflicting defaults */ List *saved_schema = NIL; + ListCell *entry; /* * Check for and reject tables with too many columns. We perform this @@ -2583,12 +2583,17 @@ MergeAttributes(List *schema, List *supers, char relpersistence, * Although we might consider merging such entries in the same way that we * handle name conflicts for inherited attributes, it seems to make more * sense to assume such conflicts are errors. + * + * We don't use foreach() here because we have two nested loops over the + * schema list, with possible element deletions in the inner one. If we + * used foreach_delete_current() it could only fix up the state of one of + * the loops, so it seems cleaner to use looping over list indexes for + * both loops. Note that any deletion will happen beyond where the outer + * loop is, so its index never needs adjustment. */ - foreach(entry, schema) + for (int coldefpos = 0; coldefpos < list_length(schema); coldefpos++) { - ColumnDef *coldef = lfirst(entry); - ListCell *rest = lnext(entry); - ListCell *prev = entry; + ColumnDef *coldef = list_nth_node(ColumnDef, schema, coldefpos); if (!is_partition && coldef->typeName == NULL) { @@ -2604,11 +2609,10 @@ MergeAttributes(List *schema, List *supers, char relpersistence, coldef->colname))); } - while (rest != NULL) + /* restpos scans all entries beyond coldef; incr is in loop body */ + for (int restpos = coldefpos + 1; restpos < list_length(schema);) { - ColumnDef *restdef = lfirst(rest); - ListCell *next = lnext(rest); /* need to save it in case we - * delete it */ + ColumnDef *restdef = list_nth_node(ColumnDef, schema, restpos); if (strcmp(coldef->colname, restdef->colname) == 0) { @@ -2622,13 +2626,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, coldef->cooked_default = restdef->cooked_default; coldef->constraints = restdef->constraints; coldef->is_from_type = false; - schema = list_delete_cell(schema, rest, prev); - - /* - * As two elements are merged and one is removed, we - * should never finish with an empty list. - */ - Assert(schema != NIL); + schema = list_delete_nth_cell(schema, restpos); } else ereport(ERROR, @@ -2636,8 +2634,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence, errmsg("column \"%s\" specified more than once", coldef->colname))); } - prev = rest; - rest = next; + else + restpos++; } } @@ -4684,16 +4682,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, break; case AT_AddIdentity: ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); + /* This command never recurses */ pass = AT_PASS_ADD_CONSTR; break; - case AT_DropIdentity: - ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); - pass = AT_PASS_DROP; - break; case AT_SetIdentity: ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); + /* This command never recurses */ pass = AT_PASS_COL_ATTRS; break; + case AT_DropIdentity: + ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); + /* This command never recurses */ + pass = AT_PASS_DROP; + break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATPrepDropNotNull(rel, recurse, recursing); @@ -4768,7 +4769,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_DropConstraint: /* DROP CONSTRAINT */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATCheckPartitionsNotInUse(rel, lockmode); - /* Recursion occurs during execution phase */ + /* Other recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) cmd->subtype = AT_DropConstraintRecurse; @@ -6783,8 +6784,9 @@ ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode) { /* - * Propagate to children if desired. Only plain tables and foreign tables - * have children, so no need to search for other relkinds. + * Propagate to children if desired. Only plain tables, foreign tables + * and partitioned tables have children, so no need to search for other + * relkinds. */ if (recurse && (rel->rd_rel->relkind == RELKIND_RELATION || @@ -6851,7 +6853,7 @@ ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode) inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL); /* first element is the parent rel; must ignore it */ - for_each_cell(cell, lnext(list_head(inh))) + for_each_cell(cell, inh, list_second_cell(inh)) { Relation childrel; @@ -8470,7 +8472,7 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE table_close(attrelation, RowExclusiveLock); /* drop the internal sequence */ - seqid = getOwnedSequence(RelationGetRelid(rel), attnum); + seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false); deleteDependencyRecordsForClass(RelationRelationId, seqid, RelationRelationId, DEPENDENCY_INTERNAL); CommandCounterIncrement(); @@ -8887,27 +8889,28 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, errmsg("cannot drop system column \"%s\"", colName))); - /* Don't drop inherited columns */ + /* + * Don't drop inherited columns, unless recursing (presumably from a drop + * of the parent column) + */ if (targetatt->attinhcount > 0 && !recursing) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("cannot drop inherited column \"%s\"", colName))); - /* Don't drop columns used in the partition key */ + /* + * Don't drop columns used in the partition key, either. (If we let this + * go through, the key column's dependencies would cause a cascaded drop + * of the whole table, which is surely not what the user expected.) + */ if (has_partition_attrs(rel, bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber), &is_expr)) - { - if (!is_expr) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot drop column named in partition key"))); - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot drop column referenced in partition key expression"))); - } + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"", + colName, RelationGetRelationName(rel)))); ReleaseSysCache(tuple); @@ -9804,7 +9807,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, * assess ppeqop or ffeqop, which RI_Initial_Check() does not use. */ old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item)); - old_pfeqop_item = lnext(old_pfeqop_item); + old_pfeqop_item = lnext(fkconstraint->old_conpfeqop, + old_pfeqop_item); } if (old_check_ok) { @@ -12239,16 +12243,10 @@ ATPrepAlterColumnType(List **wqueue, if (has_partition_attrs(rel, bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber), &is_expr)) - { - if (!is_expr) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot alter type of column named in partition key"))); - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot alter type of column referenced in partition key expression"))); - } + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"", + colName, RelationGetRelationName(rel)))); /* Look up the target type */ typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod); @@ -13613,7 +13611,7 @@ TryReuseForeignKey(Oid oldId, Constraint *con) /* stash a List of the operator Oids in our Constraint node */ for (i = 0; i < numkeys; i++) - con->old_conpfeqop = lcons_oid(rawarr[i], con->old_conpfeqop); + con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]); ReleaseSysCache(tup); } @@ -18522,6 +18520,11 @@ register_on_commit_action(Oid relid, OnCommitAction action) oc->creating_subid = GetCurrentSubTransactionId(); oc->deleting_subid = InvalidSubTransactionId; + /* + * We use lcons() here so that ON COMMIT actions are processed in reverse + * order of registration. That might not be essential but it seems + * reasonable. + */ on_commits = lcons(oc, on_commits); MemoryContextSwitchTo(oldcxt); @@ -18670,12 +18673,8 @@ void AtEOXact_on_commit_actions(bool isCommit) { ListCell *cur_item; - ListCell *prev_item; - - prev_item = NULL; - cur_item = list_head(on_commits); - while (cur_item != NULL) + foreach(cur_item, on_commits) { OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item); @@ -18683,20 +18682,14 @@ AtEOXact_on_commit_actions(bool isCommit) oc->creating_subid != InvalidSubTransactionId) { /* cur_item must be removed */ - on_commits = list_delete_cell(on_commits, cur_item, prev_item); + on_commits = foreach_delete_current(on_commits, cur_item); pfree(oc); - if (prev_item) - cur_item = lnext(prev_item); - else - cur_item = list_head(on_commits); } else { /* cur_item must be preserved */ oc->creating_subid = InvalidSubTransactionId; oc->deleting_subid = InvalidSubTransactionId; - prev_item = cur_item; - cur_item = lnext(prev_item); } } } @@ -18713,24 +18706,16 @@ AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid) { ListCell *cur_item; - ListCell *prev_item; - - prev_item = NULL; - cur_item = list_head(on_commits); - while (cur_item != NULL) + foreach(cur_item, on_commits) { OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item); if (!isCommit && oc->creating_subid == mySubid) { /* cur_item must be removed */ - on_commits = list_delete_cell(on_commits, cur_item, prev_item); + on_commits = foreach_delete_current(on_commits, cur_item); pfree(oc); - if (prev_item) - cur_item = lnext(prev_item); - else - cur_item = list_head(on_commits); } else { @@ -18739,8 +18724,6 @@ AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, oc->creating_subid = parentSubid; if (oc->deleting_subid == mySubid) oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId; - prev_item = cur_item; - cur_item = lnext(prev_item); } } } diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 690f060a8201..0593141f646c 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -803,7 +803,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, NULL, true, /* islocal */ 0, /* inhcount */ - true, /* isnoinherit */ + true, /* noinherit */ isInternal); /* is_internal */ } @@ -1238,7 +1238,6 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, */ childStmt = (CreateTrigStmt *) copyObject(stmt); childStmt->funcname = NIL; - childStmt->args = NIL; childStmt->whenClause = NULL; /* If there is a WHEN clause, create a modified copy of it */ diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index 7feb76fb7e7f..9a49642d0faa 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -614,22 +614,16 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) { DefElem *defel = (DefElem *) lfirst(pl); ListCell *cell; - ListCell *prev; - ListCell *next; /* * Remove any matches ... */ - prev = NULL; - for (cell = list_head(dictoptions); cell; cell = next) + foreach(cell, dictoptions) { DefElem *oldel = (DefElem *) lfirst(cell); - next = lnext(cell); if (strcmp(oldel->defname, defel->defname) == 0) - dictoptions = list_delete_cell(dictoptions, cell, prev); - else - prev = cell; + dictoptions = foreach_delete_current(dictoptions, cell); } /* @@ -1647,7 +1641,7 @@ serialize_deflist(List *deflist) appendStringInfoChar(&buf, ch); } appendStringInfoChar(&buf, '\''); - if (lnext(l) != NULL) + if (lnext(deflist, l) != NULL) appendStringInfoString(&buf, ", "); } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 78aa10cd799c..9dcaf95d6d75 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -3108,7 +3108,7 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode) rtc->rel = rel; rtc->natts = 0; rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel)); - result = lcons(rtc, result); + result = lappend(result, rtc); } /* diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index e571f470eb7b..ab5e5f8fb130 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -570,7 +570,7 @@ DefineView(ViewStmt *stmt, const char *queryString, if (te->resjunk) continue; te->resname = pstrdup(strVal(lfirst(alist_item))); - alist_item = lnext(alist_item); + alist_item = lnext(stmt->aliases, alist_item); if (alist_item == NULL) break; /* done assigning aliases */ } diff --git a/src/backend/executor/README b/src/backend/executor/README index 05f197bc75b6..18b2ac186595 100644 --- a/src/backend/executor/README +++ b/src/backend/executor/README @@ -186,9 +186,9 @@ Expression Evaluation To allow for different methods of expression evaluation, and for better branch/jump target prediction, expressions are evaluated by -calling ExprState->evalfunc (via ExprEvalExpr() and friends). +calling ExprState->evalfunc (via ExecEvalExpr() and friends). -ExprReadyExpr() can choose the method of interpretation by setting +ExecReadyExpr() can choose the method of interpretation by setting evalfunc to an appropriate function. The default execution function, ExecInterpExpr, is implemented in execExprInterp.c; see its header comment for details. Special-case evalfuncs are used for certain diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index cf1542b08209..469d14737b16 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -797,7 +797,7 @@ ExecInitExprRec(Expr *node, ExprState *state, { AggState *aggstate = (AggState *) state->parent; - aggstate->aggs = lcons(astate, aggstate->aggs); + aggstate->aggs = lappend(aggstate->aggs, astate); aggstate->numaggs++; } else @@ -903,7 +903,7 @@ ExecInitExprRec(Expr *node, ExprState *state, WindowAggState *winstate = (WindowAggState *) state->parent; int nfuncs; - winstate->funcs = lcons(wfstate, winstate->funcs); + winstate->funcs = lappend(winstate->funcs, wfstate); nfuncs = ++winstate->numfuncs; if (wfunc->winagg) winstate->numaggs++; diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c index 79fcc8dee2b1..897ff52e0341 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -173,7 +173,7 @@ ExecInitJunkFilterConversion(List *targetList, { TargetEntry *tle = lfirst(t); - t = lnext(t); + t = lnext(targetList, t); if (!tle->resjunk) { cleanMap[i] = tle->resno; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 23e1416baaf2..f2ac08a45eb0 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -3380,7 +3380,7 @@ ExecBuildSlotValueDescription(Oid reloid, /* truncate if needed */ vallen = strlen(val); if (vallen <= maxfieldlen) - appendStringInfoString(&buf, val); + appendBinaryStringInfo(&buf, val, vallen); else { vallen = pg_mbcliplen(val, vallen, maxfieldlen); @@ -3399,7 +3399,7 @@ ExecBuildSlotValueDescription(Oid reloid, if (!table_perm) { appendStringInfoString(&collist, ") = "); - appendStringInfoString(&collist, buf.data); + appendBinaryStringInfo(&collist, buf.data, buf.len); return collist.data; } @@ -3883,6 +3883,7 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree) estate->es_range_table_array = parentestate->es_range_table_array; estate->es_range_table_size = parentestate->es_range_table_size; estate->es_relations = parentestate->es_relations; + estate->es_queryEnv = parentestate->es_queryEnv; estate->es_rowmarks = parentestate->es_rowmarks; estate->es_plannedstmt = parentestate->es_plannedstmt; estate->es_junkFilter = parentestate->es_junkFilter; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index b42311887967..c746ce0b10da 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -1227,7 +1227,7 @@ FormPartitionKeyDatum(PartitionDispatch pd, datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item), GetPerTupleExprContext(estate), &isNull); - partexpr_item = lnext(partexpr_item); + partexpr_item = lnext(pd->keystate, partexpr_item); } values[i] = datum; isnull[i] = isNull; @@ -1420,7 +1420,7 @@ ExecBuildSlotPartitionKeyDescription(Relation rel, /* truncate if needed */ vallen = strlen(val); if (vallen <= maxfieldlen) - appendStringInfoString(&buf, val); + appendBinaryStringInfo(&buf, val, vallen); else { vallen = pg_mbcliplen(val, vallen, maxfieldlen); diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 61d187b3682b..4d65b098163c 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -102,8 +102,7 @@ ExecScanFetch(ScanState *node, * ExecScan * * Scans the relation using the 'access method' indicated and - * returns the next qualifying tuple in the direction specified - * in the global variable ExecDirection. + * returns the next qualifying tuple. * The access method returns the next tuple and ExecScan() is * responsible for checking the tuple returned against the qual-clause. * diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 615b4c7ad6f4..7cf1c186f7c2 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1161,7 +1161,7 @@ ExecAllocTableSlot(List **tupleTable, TupleDesc desc, * This releases any resources (buffer pins, tupdesc refcounts) * held by the tuple table, and optionally releases the memory * occupied by the tuple table data structure. - * It is expected that this routine be called by EndPlan(). + * It is expected that this routine be called by ExecEndPlan(). * -------------------------------- */ void diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index d7f3366241e5..0002822a0340 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -681,7 +681,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc var->vartypmod != -1)) return false; /* type mismatch */ - tlist_item = lnext(tlist_item); + tlist_item = lnext(tlist, tlist_item); } if (tlist_item) diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 7421cf2497d4..f9946380e978 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1318,7 +1318,7 @@ PG_TRY(); es = es->next; while (!es) { - eslc = lnext(eslc); + eslc = lnext(eslist, eslc); if (!eslc) break; /* end of function */ diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 2bda17162bed..ecc69876eb0b 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1050,7 +1050,7 @@ process_ordered_aggregate_multi(AggState *aggstate, * This function handles only one grouping set (already set in * aggstate->current_set). * - * The finalfunction will be run, and the result delivered, in the + * The finalfn will be run, and the result delivered, in the * output-tuple context; caller's CurrentMemoryContext does not matter. * * The finalfn uses the state as set in the transno. This also might be @@ -4247,8 +4247,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, numTransArgs = pertrans->numTransInputs + 1; /* - * Set up infrastructure for calling the transfn. Note that invtrans - * is not needed here. + * Set up infrastructure for calling the transfn. Note that + * invtransfn is not needed here. */ build_aggregate_transfn_expr(inputTypes, numArguments, diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 3cdebb23f417..6bce1fd480c3 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -107,7 +107,6 @@ ExecInitAppend(Append *node, EState *estate, int eflags) int firstvalid; int i, j; - ListCell *lc; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); @@ -214,24 +213,20 @@ ExecInitAppend(Append *node, EState *estate, int eflags) * * While at it, find out the first valid partial plan. */ - j = i = 0; + j = 0; firstvalid = nplans; - foreach(lc, node->appendplans) + i = -1; + while ((i = bms_next_member(validsubplans, i)) >= 0) { - if (bms_is_member(i, validsubplans)) - { - Plan *initNode = (Plan *) lfirst(lc); + Plan *initNode = (Plan *) list_nth(node->appendplans, i); - /* - * Record the lowest appendplans index which is a valid partial - * plan. - */ - if (i >= node->first_partial_plan && j < firstvalid) - firstvalid = j; + /* + * Record the lowest appendplans index which is a valid partial plan. + */ + if (i >= node->first_partial_plan && j < firstvalid) + firstvalid = j; - appendplanstates[j++] = ExecInitNode(initNode, estate, eflags); - } - i++; + appendplanstates[j++] = ExecInitNode(initNode, estate, eflags); } appendstate->as_first_partial_plan = firstvalid; diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index e4a0c8d5086a..4ed816bbee88 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -63,15 +63,13 @@ #include "cdb/cdbvars.h" /* gp_select_invisible */ static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node); -static inline void BitmapDoneInitializingSharedState( - ParallelBitmapHeapState *pstate); +static inline void BitmapDoneInitializingSharedState(ParallelBitmapHeapState *pstate); static inline void BitmapAdjustPrefetchIterator(BitmapHeapScanState *node, TBMIterateResult *tbmres); static inline void BitmapAdjustPrefetchTarget(BitmapHeapScanState *node); static inline void BitmapPrefetch(BitmapHeapScanState *node, TableScanDesc scan); -static bool BitmapShouldInitializeSharedState( - ParallelBitmapHeapState *pstate); +static bool BitmapShouldInitializeSharedState(ParallelBitmapHeapState *pstate); static void ExecEagerFreeBitmapHeapScan(BitmapHeapScanState *node); diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index 08c9e4f171d0..357ec1dcd025 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -70,7 +70,6 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) int nplans; int i, j; - ListCell *lc; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -180,16 +179,13 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) * call ExecInitNode on each of the valid plans to be executed and save * the results into the mergeplanstates array. */ - j = i = 0; - foreach(lc, node->mergeplans) + j = 0; + i = -1; + while ((i = bms_next_member(validsubplans, i)) >= 0) { - if (bms_is_member(i, validsubplans)) - { - Plan *initNode = (Plan *) lfirst(lc); + Plan *initNode = (Plan *) list_nth(node->mergeplans, i); - mergeplanstates[j++] = ExecInitNode(initNode, estate, eflags); - } - i++; + mergeplanstates[j++] = ExecInitNode(initNode, estate, eflags); } mergestate->ps.ps_ProjInfo = NULL; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index cfa82653548b..a037f006fbe7 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2944,11 +2944,16 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) table_slot_create(resultRelInfo->ri_RelationDesc, &mtstate->ps.state->es_tupleTable); - /* create the tuple slot for the UPDATE SET projection */ + /* + * Create the tuple slot for the UPDATE SET projection. We want a slot + * of the table's type here, because the slot will be used to insert + * into the table, and for RETURNING processing - which may access + * system attributes. + */ tupDesc = ExecTypeFromTL((List *) node->onConflictSet); resultRelInfo->ri_onConflict->oc_ProjSlot = ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc, - &TTSOpsVirtual); + table_slot_callbacks(resultRelInfo->ri_RelationDesc)); /* build UPDATE SET projection state */ resultRelInfo->ri_onConflict->oc_ProjInfo = diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c index 277d27837111..facdb4232e67 100644 --- a/src/backend/executor/nodeProjectSet.c +++ b/src/backend/executor/nodeProjectSet.c @@ -196,8 +196,8 @@ ExecProjectSRF(ProjectSetState *node, bool continuing) Assert(hassrf); /* - * If all the SRFs returned EndResult, we consider that as no row being - * produced. + * If all the SRFs returned ExprEndResult, we consider that as no row + * being produced. */ if (hasresult) { diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c index 705fc56b75f2..373941bdee44 100644 --- a/src/backend/executor/nodeRecursiveunion.c +++ b/src/backend/executor/nodeRecursiveunion.c @@ -173,7 +173,7 @@ ExecRecursiveUnion(PlanState *pstate) } /* ---------------------------------------------------------------- - * ExecInitRecursiveUnionScan + * ExecInitRecursiveUnion * ---------------------------------------------------------------- */ RecursiveUnionState * @@ -283,7 +283,7 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags) } /* ---------------------------------------------------------------- - * ExecEndRecursiveUnionScan + * ExecEndRecursiveUnion * * frees any storage allocated through C routines. * ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c index 45d5f3c4244b..d264337899a4 100644 --- a/src/backend/executor/nodeTableFuncscan.c +++ b/src/backend/executor/nodeTableFuncscan.c @@ -514,7 +514,7 @@ tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext) /* advance list of default expressions */ if (cell != NULL) - cell = lnext(cell); + cell = lnext(tstate->coldefexprs, cell); } tuplestore_putvalues(tstate->tupstore, tupdesc, values, nulls); diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 90dec8ff74a2..3f778de4b60e 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -1936,8 +1936,9 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo) slist_push_head(&_SPI_current->tuptables, &tuptable->next); /* set up initial allocations */ - tuptable->alloced = tuptable->free = 128; + tuptable->alloced = 128; tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple)); + tuptable->numvals = 0; tuptable->tupdesc = CreateTupleDescCopy(typeinfo); MemoryContextSwitchTo(oldcxt); @@ -1963,13 +1964,14 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self) oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt); - if (tuptable->free == 0) + if (tuptable->numvals >= tuptable->alloced) { /* Double the size of the pointer array */ - tuptable->free = tuptable->alloced; - tuptable->alloced += tuptable->free; + uint64 newalloced = tuptable->alloced * 2; + tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals, - tuptable->alloced * sizeof(HeapTuple)); + newalloced * sizeof(HeapTuple)); + tuptable->alloced = newalloced; } /* @@ -1979,9 +1981,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self) * Suggested fix: In SPITupleTable, change TupleDesc tupdesc to a slot, and * access everything through slot_XXX intreface. */ - tuptable->vals[tuptable->alloced - tuptable->free] = - ExecCopySlotHeapTuple(slot); - (tuptable->free)--; + tuptable->vals[tuptable->numvals] = ExecCopySlotHeapTuple(slot); + (tuptable->numvals)++; MemoryContextSwitchTo(oldcxt); @@ -2433,8 +2434,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, /* Update "processed" if stmt returned tuples */ if (_SPI_current->tuptable) - _SPI_current->processed = _SPI_current->tuptable->alloced - - _SPI_current->tuptable->free; + _SPI_current->processed = _SPI_current->tuptable->numvals; res = SPI_OK_UTILITY; @@ -2478,7 +2478,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, /* * The last canSetTag query sets the status values returned to the * caller. Be careful to free any tuptables not returned, to - * avoid intratransaction memory leak. + * avoid intra-transaction memory leak. */ if (canSetTag) { @@ -2927,7 +2927,7 @@ _SPI_checktuples(void) if (tuptable == NULL) /* spi_dest_startup was not called */ failed = true; - else if (processed != (tuptable->alloced - tuptable->free)) + else if (processed != tuptable->numvals) failed = true; return failed; diff --git a/src/backend/lib/dshash.c b/src/backend/lib/dshash.c index aa3f57c75949..27cd0a938ee6 100644 --- a/src/backend/lib/dshash.c +++ b/src/backend/lib/dshash.c @@ -409,7 +409,7 @@ dshash_find(dshash_table *hash_table, const void *key, bool exclusive) } else { - /* The caller will free the lock by calling dshash_release. */ + /* The caller will free the lock by calling dshash_release_lock. */ hash_table->find_locked = true; hash_table->find_exclusively_locked = exclusive; return ENTRY_FROM_ITEM(item); diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 6b60abe1ddc4..aa918839fb9f 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -510,9 +510,11 @@ scram_verify_plain_password(const char *username, const char *password, return false; } - salt = palloc(pg_b64_dec_len(strlen(encoded_salt))); - saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt); - if (saltlen == -1) + saltlen = pg_b64_dec_len(strlen(encoded_salt)); + salt = palloc(saltlen); + saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt, + saltlen); + if (saltlen < 0) { ereport(LOG, (errmsg("invalid SCRAM verifier for user \"%s\"", username))); @@ -596,9 +598,10 @@ parse_scram_verifier(const char *verifier, int *iterations, char **salt, * Verify that the salt is in Base64-encoded format, by decoding it, * although we return the encoded version to the caller. */ - decoded_salt_buf = palloc(pg_b64_dec_len(strlen(salt_str))); + decoded_len = pg_b64_dec_len(strlen(salt_str)); + decoded_salt_buf = palloc(decoded_len); decoded_len = pg_b64_decode(salt_str, strlen(salt_str), - decoded_salt_buf); + decoded_salt_buf, decoded_len); if (decoded_len < 0) goto invalid_verifier; *salt = pstrdup(salt_str); @@ -606,16 +609,18 @@ parse_scram_verifier(const char *verifier, int *iterations, char **salt, /* * Decode StoredKey and ServerKey. */ - decoded_stored_buf = palloc(pg_b64_dec_len(strlen(storedkey_str))); + decoded_len = pg_b64_dec_len(strlen(storedkey_str)); + decoded_stored_buf = palloc(decoded_len); decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str), - decoded_stored_buf); + decoded_stored_buf, decoded_len); if (decoded_len != SCRAM_KEY_LEN) goto invalid_verifier; memcpy(stored_key, decoded_stored_buf, SCRAM_KEY_LEN); - decoded_server_buf = palloc(pg_b64_dec_len(strlen(serverkey_str))); + decoded_len = pg_b64_dec_len(strlen(serverkey_str)); + decoded_server_buf = palloc(decoded_len); decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str), - decoded_server_buf); + decoded_server_buf, decoded_len); if (decoded_len != SCRAM_KEY_LEN) goto invalid_verifier; memcpy(server_key, decoded_server_buf, SCRAM_KEY_LEN); @@ -649,8 +654,20 @@ mock_scram_verifier(const char *username, int *iterations, char **salt, /* Generate deterministic salt */ raw_salt = scram_mock_salt(username); - encoded_salt = (char *) palloc(pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN) + 1); - encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt); + encoded_len = pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN); + /* don't forget the zero-terminator */ + encoded_salt = (char *) palloc(encoded_len + 1); + encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt, + encoded_len); + + /* + * Note that we cannot reveal any information to an attacker here so the + * error message needs to remain generic. This should never fail anyway + * as the salt generated for mock authentication uses the cluster's nonce + * value. + */ + if (encoded_len < 0) + elog(ERROR, "could not encode salt"); encoded_salt[encoded_len] = '\0'; *salt = encoded_salt; @@ -1144,8 +1161,15 @@ build_server_first_message(scram_state *state) (errcode(ERRCODE_INTERNAL_ERROR), errmsg("could not generate random nonce"))); - state->server_nonce = palloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1); - encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->server_nonce); + encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN); + /* don't forget the zero-terminator */ + state->server_nonce = palloc(encoded_len + 1); + encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, + state->server_nonce, encoded_len); + if (encoded_len < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not encode random nonce"))); state->server_nonce[encoded_len] = '\0'; state->server_first_message = @@ -1170,6 +1194,7 @@ read_client_final_message(scram_state *state, const char *input) *proof; char *p; char *client_proof; + int client_proof_len; begin = p = pstrdup(input); @@ -1234,9 +1259,13 @@ read_client_final_message(scram_state *state, const char *input) snprintf(cbind_input, cbind_input_len, "p=tls-server-end-point,,"); memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len); - b64_message = palloc(pg_b64_enc_len(cbind_input_len) + 1); + b64_message_len = pg_b64_enc_len(cbind_input_len); + /* don't forget the zero-terminator */ + b64_message = palloc(b64_message_len + 1); b64_message_len = pg_b64_encode(cbind_input, cbind_input_len, - b64_message); + b64_message, b64_message_len); + if (b64_message_len < 0) + elog(ERROR, "could not encode channel binding data"); b64_message[b64_message_len] = '\0'; /* @@ -1276,8 +1305,10 @@ read_client_final_message(scram_state *state, const char *input) value = read_any_attr(&p, &attr); } while (attr != 'p'); - client_proof = palloc(pg_b64_dec_len(strlen(value))); - if (pg_b64_decode(value, strlen(value), client_proof) != SCRAM_KEY_LEN) + client_proof_len = pg_b64_dec_len(strlen(value)); + client_proof = palloc(client_proof_len); + if (pg_b64_decode(value, strlen(value), client_proof, + client_proof_len) != SCRAM_KEY_LEN) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), @@ -1322,9 +1353,14 @@ build_server_final_message(scram_state *state) strlen(state->client_final_message_without_proof)); scram_HMAC_final(ServerSignature, &ctx); - server_signature_base64 = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1); + siglen = pg_b64_enc_len(SCRAM_KEY_LEN); + /* don't forget the zero-terminator */ + server_signature_base64 = palloc(siglen + 1); siglen = pg_b64_encode((const char *) ServerSignature, - SCRAM_KEY_LEN, server_signature_base64); + SCRAM_KEY_LEN, server_signature_base64, + siglen); + if (siglen < 0) + elog(ERROR, "could not encode server signature"); server_signature_base64[siglen] = '\0'; /*------ diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 634435a9d67b..28843fd3949f 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -2166,14 +2166,9 @@ interpret_ident_response(const char *ident_response, /* - * Talk to the ident server on host "remote_ip_addr" and find out who - * owns the tcp connection from his port "remote_port" to port - * "local_port_addr" on host "local_ip_addr". Return the user name the - * ident server gives as "*ident_user". - * - * IP addresses and port numbers are in network byte order. - * - * But iff we're unable to get the information from ident, return false. + * Talk to the ident server on "remote_addr" and find out who + * owns the tcp connection to "local_addr" + * It the username successfully retrieved, check the usermap. * * XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if the * latch was set would improve the responsiveness to timeouts/cancellations. @@ -3406,11 +3401,11 @@ CheckRADIUSAuth(Port *port) * don't and will then reuse the correct value. */ if (list_length(port->hba->radiussecrets) > 1) - secrets = lnext(secrets); + secrets = lnext(port->hba->radiussecrets, secrets); if (list_length(port->hba->radiusports) > 1) - radiusports = lnext(radiusports); + radiusports = lnext(port->hba->radiusports, radiusports); if (list_length(port->hba->radiusidentifiers) > 1) - identifiers = lnext(identifiers); + identifiers = lnext(port->hba->radiusidentifiers, identifiers); } /* No servers left to try, so give up */ diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c index 6199f36e40b8..8204c129bde1 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -500,7 +500,7 @@ lo_import_internal(text *filename, Oid lobjOid) inv_close(lobj); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", @@ -573,7 +573,7 @@ be_lo_export(PG_FUNCTION_ARGS) fnamebuf))); } - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c index 877226d3776f..4abbef5bf190 100644 --- a/src/backend/libpq/be-secure-common.c +++ b/src/backend/libpq/be-secure-common.c @@ -112,9 +112,10 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, goto error; } - /* strip trailing newline */ + /* strip trailing newline, including \r in case we're on Windows */ len = strlen(buf); - if (len > 0 && buf[len - 1] == '\n') + while (len > 0 && (buf[len - 1] == '\n' || + buf[len - 1] == '\r')) buf[--len] = '\0'; error: diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 77bb67b8a42f..f7f2b2af6995 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -1060,7 +1060,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) } /* Get the databases. */ - field = lnext(field); + field = lnext(tok_line->fields, field); if (!field) { ereport(elevel, @@ -1080,7 +1080,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) } /* Get the roles. */ - field = lnext(field); + field = lnext(tok_line->fields, field); if (!field) { ereport(elevel, @@ -1102,7 +1102,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) if (parsedline->conntype != ctLocal) { /* Read the IP address field. (with or without CIDR netmask) */ - field = lnext(field); + field = lnext(tok_line->fields, field); if (!field) { ereport(elevel, @@ -1222,7 +1222,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) { /* Read the mask field. */ pfree(str); - field = lnext(field); + field = lnext(tok_line->fields, field); if (!field) { ereport(elevel, @@ -1283,7 +1283,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) } /* != ctLocal */ /* Get the authentication method */ - field = lnext(field); + field = lnext(tok_line->fields, field); if (!field) { ereport(elevel, @@ -1488,7 +1488,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) } /* Parse remaining arguments */ - while ((field = lnext(field)) != NULL) + while ((field = lnext(tok_line->fields, field)) != NULL) { tokens = lfirst(field); foreach(tokencell, tokens) @@ -2744,7 +2744,7 @@ parse_ident_line(TokenizedLine *tok_line) parsedline->usermap = pstrdup(token->string); /* Get the ident user token */ - field = lnext(field); + field = lnext(tok_line->fields, field); IDENT_FIELD_ABSENT(field); tokens = lfirst(field); IDENT_MULTI_VALUE(tokens); @@ -2752,7 +2752,7 @@ parse_ident_line(TokenizedLine *tok_line) parsedline->ident_user = pstrdup(token->string); /* Get the PG rolename token */ - field = lnext(field); + field = lnext(tok_line->fields, field); IDENT_FIELD_ABSENT(field); tokens = lfirst(field); IDENT_MULTI_VALUE(tokens); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 8a9e903c5ca4..681bd11bc512 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -5650,48 +5650,6 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt *from) return newnode; } -/* **************************************************************** - * pg_list.h copy functions - * **************************************************************** - */ - -/* - * Perform a deep copy of the specified list, using copyObject(). The - * list MUST be of type T_List; T_IntList and T_OidList nodes don't - * need deep copies, so they should be copied via list_copy() - */ -#define COPY_NODE_CELL(new, old) \ - (new) = (ListCell *) palloc(sizeof(ListCell)); \ - lfirst(new) = copyObjectImpl(lfirst(old)); - -static List * -_copyList(const List *from) -{ - List *new; - ListCell *curr_old; - ListCell *prev_new; - - Assert(list_length(from) >= 1); - - new = makeNode(List); - new->length = from->length; - - COPY_NODE_CELL(new->head, from->head); - prev_new = new->head; - curr_old = lnext(from->head); - - while (curr_old) - { - COPY_NODE_CELL(prev_new->next, curr_old); - prev_new = prev_new->next; - curr_old = curr_old->next; - } - prev_new->next = NULL; - new->tail = prev_new; - - return new; -} - /* **************************************************************** * extensible.h copy functions * **************************************************************** @@ -6216,7 +6174,7 @@ copyObjectImpl(const void *from) * LIST NODES */ case T_List: - retval = _copyList(from); + retval = list_copy_deep(from); break; /* diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c index c4ea9bfa5aa3..474c61058063 100644 --- a/src/backend/nodes/list.c +++ b/src/backend/nodes/list.c @@ -1,7 +1,9 @@ /*------------------------------------------------------------------------- * * list.c - * implementation for PostgreSQL generic linked list package + * implementation for PostgreSQL generic list package + * + * See comments in pg_list.h. * * * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group @@ -16,10 +18,35 @@ #include "postgres.h" #include "nodes/pg_list.h" +#include "utils/memutils.h" + + +/* + * The previous List implementation, since it used a separate palloc chunk + * for each cons cell, had the property that adding or deleting list cells + * did not move the storage of other existing cells in the list. Quite a + * bit of existing code depended on that, by retaining ListCell pointers + * across such operations on a list. There is no such guarantee in this + * implementation, so instead we have debugging support that is meant to + * help flush out now-broken assumptions. Defining DEBUG_LIST_MEMORY_USAGE + * while building this file causes the List operations to forcibly move + * all cells in a list whenever a cell is added or deleted. In combination + * with MEMORY_CONTEXT_CHECKING and/or Valgrind, this can usually expose + * broken code. It's a bit expensive though, as there's many more palloc + * cycles and a lot more data-copying than in a default build. + * + * By default, we enable this when building for Valgrind. + */ +#ifdef USE_VALGRIND +#define DEBUG_LIST_MEMORY_USAGE +#endif +/* Overhead for the fixed part of a List header, measured in ListCells */ +#define LIST_HEADER_OVERHEAD \ + ((int) ((offsetof(List, initial_elements) - 1) / sizeof(ListCell) + 1)) /* - * Routines to simplify writing assertions about the type of a list; a + * Macros to simplify writing assertions about the type of a list; a * NIL list is considered to be an empty list of any type. */ #define IsPointerList(l) ((l) == NIL || IsA((l), List)) @@ -37,50 +64,221 @@ check_list_invariants(const List *list) return; Assert(list->length > 0); - Assert(list->head != NULL); - Assert(list->tail != NULL); + Assert(list->length <= list->max_length); + Assert(list->elements != NULL); Assert(list->type == T_List || list->type == T_IntList || list->type == T_OidList); - - if (list->length == 1) - Assert(list->head == list->tail); - if (list->length == 2) - Assert(list->head->next == list->tail); - Assert(list->tail->next == NULL); } #else -#define check_list_invariants(l) +#define check_list_invariants(l) ((void) 0) #endif /* USE_ASSERT_CHECKING */ /* - * Return a freshly allocated List. Since empty non-NIL lists are - * invalid, new_list() also allocates the head cell of the new list: - * the caller should be sure to fill in that cell's data. + * Return a freshly allocated List with room for at least min_size cells. + * + * Since empty non-NIL lists are invalid, new_list() sets the initial length + * to min_size, effectively marking that number of cells as valid; the caller + * is responsible for filling in their data. */ static List * -new_list(NodeTag type) +new_list(NodeTag type, int min_size) +{ + List *newlist; + int max_size; + + Assert(min_size > 0); + + /* + * We allocate all the requested cells, and possibly some more, as part of + * the same palloc request as the List header. This is a big win for the + * typical case of short fixed-length lists. It can lose if we allocate a + * moderately long list and then it gets extended; we'll be wasting more + * initial_elements[] space than if we'd made the header small. However, + * rounding up the request as we do in the normal code path provides some + * defense against small extensions. + */ + +#ifndef DEBUG_LIST_MEMORY_USAGE + + /* + * Normally, we set up a list with some extra cells, to allow it to grow + * without a repalloc. Prefer cell counts chosen to make the total + * allocation a power-of-2, since palloc would round it up to that anyway. + * (That stops being true for very large allocations, but very long lists + * are infrequent, so it doesn't seem worth special logic for such cases.) + * + * The minimum allocation is 8 ListCell units, providing either 4 or 5 + * available ListCells depending on the machine's word width. Counting + * palloc's overhead, this uses the same amount of space as a one-cell + * list did in the old implementation, and less space for any longer list. + * + * We needn't worry about integer overflow; no caller passes min_size + * that's more than twice the size of an existing list, so the size limits + * within palloc will ensure that we don't overflow here. + */ + max_size = 8; /* semi-arbitrary small power of 2 */ + while (max_size < min_size + LIST_HEADER_OVERHEAD) + max_size *= 2; + max_size -= LIST_HEADER_OVERHEAD; +#else + + /* + * For debugging, don't allow any extra space. This forces any cell + * addition to go through enlarge_list() and thus move the existing data. + */ + max_size = min_size; +#endif + + newlist = (List *) palloc(offsetof(List, initial_elements) + + max_size * sizeof(ListCell)); + newlist->type = type; + newlist->length = min_size; + newlist->max_length = max_size; + newlist->elements = newlist->initial_elements; + + return newlist; +} + +/* + * Enlarge an existing non-NIL List to have room for at least min_size cells. + * + * This does *not* update list->length, as some callers would find that + * inconvenient. (list->length had better be the correct number of existing + * valid cells, though.) + */ +static void +enlarge_list(List *list, int min_size) +{ + int new_max_len; + + Assert(min_size > list->max_length); /* else we shouldn't be here */ + +#ifndef DEBUG_LIST_MEMORY_USAGE + + /* + * As above, we prefer power-of-two total allocations; but here we need + * not account for list header overhead. The existing max length might + * not be a power of 2, so don't rely on that. + */ + new_max_len = 16; /* semi-arbitrary small power of 2 */ + while (new_max_len < min_size) + new_max_len *= 2; +#else + /* As above, don't allocate anything extra */ + new_max_len = min_size; +#endif + + if (list->elements == list->initial_elements) + { + List *newlist PG_USED_FOR_ASSERTS_ONLY; + + /* + * Replace original in-line allocation with a separate palloc block. + * Ensure it is in the same memory context as the List header. (The + * previous List implementation did not offer any guarantees about + * keeping all list cells in the same context, but it seems reasonable + * to create such a guarantee now.) + */ + list->elements = (ListCell *) + MemoryContextAlloc(GetMemoryChunkContext(list), + new_max_len * sizeof(ListCell)); + memcpy(list->elements, list->initial_elements, + list->length * sizeof(ListCell)); + + /* + * Currently, asking aset.c to reduce the allocated size of the List + * header is pointless in terms of reclaiming space, unless the list + * is very long. However, it seems worth doing anyway to cause the + * no-longer-needed initial_elements[] space to be cleared in + * debugging builds. + */ + newlist = (List *) repalloc(list, offsetof(List, initial_elements)); + + /* That better not have failed, nor moved the list header */ + Assert(newlist == list); + } + else + { +#ifndef DEBUG_LIST_MEMORY_USAGE + /* Normally, let repalloc deal with enlargement */ + list->elements = (ListCell *) repalloc(list->elements, + new_max_len * sizeof(ListCell)); +#else + /* + * repalloc() might enlarge the space in-place, which we don't want + * for debugging purposes, so forcibly move the data somewhere else. + */ + ListCell *newelements; + + newelements = (ListCell *) + MemoryContextAlloc(GetMemoryChunkContext(list), + new_max_len * sizeof(ListCell)); + memcpy(newelements, list->elements, + list->length * sizeof(ListCell)); + pfree(list->elements); + list->elements = newelements; +#endif + } + + list->max_length = new_max_len; +} + +/* + * Convenience functions to construct short Lists from given values. + * (These are normally invoked via the list_makeN macros.) + */ +List * +list_make1_impl(NodeTag t, ListCell datum1) +{ + List *list = new_list(t, 1); + + list->elements[0] = datum1; + check_list_invariants(list); + return list; +} + +List * +list_make2_impl(NodeTag t, ListCell datum1, ListCell datum2) +{ + List *list = new_list(t, 2); + + list->elements[0] = datum1; + list->elements[1] = datum2; + check_list_invariants(list); + return list; +} + +List * +list_make3_impl(NodeTag t, ListCell datum1, ListCell datum2, + ListCell datum3) { - List *new_list; - ListCell *new_head; + List *list = new_list(t, 3); - new_head = (ListCell *) palloc(sizeof(*new_head)); - new_head->next = NULL; - /* new_head->data is left undefined! */ + list->elements[0] = datum1; + list->elements[1] = datum2; + list->elements[2] = datum3; + check_list_invariants(list); + return list; +} - new_list = (List *) palloc(sizeof(*new_list)); - new_list->type = type; - new_list->length = 1; - new_list->head = new_head; - new_list->tail = new_head; +List * +list_make4_impl(NodeTag t, ListCell datum1, ListCell datum2, + ListCell datum3, ListCell datum4) +{ + List *list = new_list(t, 4); - return new_list; + list->elements[0] = datum1; + list->elements[1] = datum2; + list->elements[2] = datum3; + list->elements[3] = datum4; + check_list_invariants(list); + return list; } /* - * Allocate a new cell and make it the head of the specified - * list. Assumes the list it is passed is non-NIL. + * Make room for a new head cell in the given (non-NIL) list. * * The data in the new head cell is undefined; the caller should be * sure to fill it in @@ -88,18 +286,17 @@ new_list(NodeTag type) static void new_head_cell(List *list) { - ListCell *new_head; - - new_head = (ListCell *) palloc(sizeof(*new_head)); - new_head->next = list->head; - - list->head = new_head; + /* Enlarge array if necessary */ + if (list->length >= list->max_length) + enlarge_list(list, list->length + 1); + /* Now shove the existing data over */ + memmove(&list->elements[1], &list->elements[0], + list->length * sizeof(ListCell)); list->length++; } /* - * Allocate a new cell and make it the tail of the specified - * list. Assumes the list it is passed is non-NIL. + * Make room for a new tail cell in the given (non-NIL) list. * * The data in the new tail cell is undefined; the caller should be * sure to fill it in @@ -107,13 +304,9 @@ new_head_cell(List *list) static void new_tail_cell(List *list) { - ListCell *new_tail; - - new_tail = (ListCell *) palloc(sizeof(*new_tail)); - new_tail->next = NULL; - - list->tail->next = new_tail; - list->tail = new_tail; + /* Enlarge array if necessary */ + if (list->length >= list->max_length) + enlarge_list(list, list->length + 1); list->length++; } @@ -130,11 +323,11 @@ lappend(List *list, void *datum) Assert(IsPointerList(list)); if (list == NIL) - list = new_list(T_List); + list = new_list(T_List, 1); else new_tail_cell(list); - lfirst(list->tail) = datum; + lfirst(list_tail(list)) = datum; check_list_invariants(list); return list; } @@ -148,11 +341,11 @@ lappend_int(List *list, int datum) Assert(IsIntegerList(list)); if (list == NIL) - list = new_list(T_IntList); + list = new_list(T_IntList, 1); else new_tail_cell(list); - lfirst_int(list->tail) = datum; + lfirst_int(list_tail(list)) = datum; check_list_invariants(list); return list; } @@ -166,82 +359,83 @@ lappend_oid(List *list, Oid datum) Assert(IsOidList(list)); if (list == NIL) - list = new_list(T_OidList); + list = new_list(T_OidList, 1); else new_tail_cell(list); - lfirst_oid(list->tail) = datum; + lfirst_oid(list_tail(list)) = datum; check_list_invariants(list); return list; } /* - * Add a new cell to the list, in the position after 'prev_cell'. The - * data in the cell is left undefined, and must be filled in by the - * caller. 'list' is assumed to be non-NIL, and 'prev_cell' is assumed - * to be non-NULL and a member of 'list'. + * Make room for a new cell at position 'pos' (measured from 0). + * The data in the cell is left undefined, and must be filled in by the + * caller. 'list' is assumed to be non-NIL, and 'pos' must be a valid + * list position, ie, 0 <= pos <= list's length. + * Returns address of the new cell. */ static ListCell * -add_new_cell(List *list, ListCell *prev_cell) +insert_new_cell(List *list, int pos) { - ListCell *new_cell; - - new_cell = (ListCell *) palloc(sizeof(*new_cell)); - /* new_cell->data is left undefined! */ - new_cell->next = prev_cell->next; - prev_cell->next = new_cell; - - if (list->tail == prev_cell) - list->tail = new_cell; - + Assert(pos >= 0 && pos <= list->length); + + /* Enlarge array if necessary */ + if (list->length >= list->max_length) + enlarge_list(list, list->length + 1); + /* Now shove the existing data over */ + if (pos < list->length) + memmove(&list->elements[pos + 1], &list->elements[pos], + (list->length - pos) * sizeof(ListCell)); list->length++; - return new_cell; + return &list->elements[pos]; } /* - * Add a new cell to the specified list (which must be non-NIL); - * it will be placed after the list cell 'prev' (which must be - * non-NULL and a member of 'list'). The data placed in the new cell - * is 'datum'. The newly-constructed cell is returned. + * Insert the given datum at position 'pos' (measured from 0) in the list. + * 'pos' must be valid, ie, 0 <= pos <= list's length. */ -ListCell * -lappend_cell(List *list, ListCell *prev, void *datum) +List * +list_insert_nth(List *list, int pos, void *datum) { - ListCell *new_cell; - + if (list == NIL) + { + Assert(pos == 0); + return list_make1(datum); + } Assert(IsPointerList(list)); - - new_cell = add_new_cell(list, prev); - lfirst(new_cell) = datum; + lfirst(insert_new_cell(list, pos)) = datum; check_list_invariants(list); - return new_cell; + return list; } -ListCell * -lappend_cell_int(List *list, ListCell *prev, int datum) +List * +list_insert_nth_int(List *list, int pos, int datum) { - ListCell *new_cell; - + if (list == NIL) + { + Assert(pos == 0); + return list_make1_int(datum); + } Assert(IsIntegerList(list)); - - new_cell = add_new_cell(list, prev); - lfirst_int(new_cell) = datum; + lfirst_int(insert_new_cell(list, pos)) = datum; check_list_invariants(list); - return new_cell; + return list; } -ListCell * -lappend_cell_oid(List *list, ListCell *prev, Oid datum) +List * +list_insert_nth_oid(List *list, int pos, Oid datum) { - ListCell *new_cell; - + if (list == NIL) + { + Assert(pos == 0); + return list_make1_oid(datum); + } Assert(IsOidList(list)); - - new_cell = add_new_cell(list, prev); - lfirst_oid(new_cell) = datum; + lfirst_oid(insert_new_cell(list, pos)) = datum; check_list_invariants(list); - return new_cell; + return list; } /* @@ -261,11 +455,11 @@ lcons(void *datum, List *list) Assert(IsPointerList(list)); if (list == NIL) - list = new_list(T_List); + list = new_list(T_List, 1); else new_head_cell(list); - lfirst(list->head) = datum; + lfirst(list_head(list)) = datum; check_list_invariants(list); return list; } @@ -279,11 +473,11 @@ lcons_int(int datum, List *list) Assert(IsIntegerList(list)); if (list == NIL) - list = new_list(T_IntList); + list = new_list(T_IntList, 1); else new_head_cell(list); - lfirst_int(list->head) = datum; + lfirst_int(list_head(list)) = datum; check_list_invariants(list); return list; } @@ -297,41 +491,44 @@ lcons_oid(Oid datum, List *list) Assert(IsOidList(list)); if (list == NIL) - list = new_list(T_OidList); + list = new_list(T_OidList, 1); else new_head_cell(list); - lfirst_oid(list->head) = datum; + lfirst_oid(list_head(list)) = datum; check_list_invariants(list); return list; } /* * Concatenate list2 to the end of list1, and return list1. list1 is - * destructively changed. Callers should be sure to use the return - * value as the new pointer to the concatenated list: the 'list1' - * input pointer may or may not be the same as the returned pointer. - * - * The nodes in list2 are merely appended to the end of list1 in-place - * (i.e. they aren't copied; the two lists will share some of the same - * storage). Therefore, invoking list_free() on list2 will also - * invalidate a portion of list1. + * destructively changed, list2 is not. (However, in the case of pointer + * lists, list1 and list2 will point to the same structures.) Callers + * should be sure to use the return value as the new pointer to the + * concatenated list: the 'list1' input pointer may or may not be the + * same as the returned pointer. */ List * -list_concat(List *list1, List *list2) +list_concat(List *list1, const List *list2) { + int new_len; + if (list1 == NIL) - return list2; + return list_copy(list2); if (list2 == NIL) return list1; - if (list1 == list2) - elog(ERROR, "cannot list_concat() a list to itself"); Assert(list1->type == list2->type); - list1->length += list2->length; - list1->tail->next = list2->head; - list1->tail = list2->tail; + new_len = list1->length + list2->length; + /* Enlarge array if necessary */ + if (new_len > list1->max_length) + enlarge_list(list1, new_len); + + /* Even if list1 == list2, using memcpy should be safe here */ + memcpy(&list1->elements[list1->length], &list2->elements[0], + list2->length * sizeof(ListCell)); + list1->length = new_len; check_list_invariants(list1); return list1; @@ -349,60 +546,27 @@ list_concat(List *list1, List *list2) List * list_truncate(List *list, int new_size) { - ListCell *cell; - int n; - if (new_size <= 0) return NIL; /* truncate to zero length */ /* If asked to effectively extend the list, do nothing */ - if (new_size >= list_length(list)) - return list; + if (new_size < list_length(list)) + list->length = new_size; - n = 1; - foreach(cell, list) - { - if (n == new_size) - { - cell->next = NULL; - list->tail = cell; - list->length = new_size; - check_list_invariants(list); - return list; - } - n++; - } + /* + * Note: unlike the individual-list-cell deletion functions, we don't move + * the list cells to new storage, even in DEBUG_LIST_MEMORY_USAGE mode. + * This is because none of them can move in this operation, so just like + * in the old cons-cell-based implementation, this function doesn't + * invalidate any pointers to cells of the list. This is also the reason + * for not wiping the memory of the deleted cells: the old code didn't + * free them either. Perhaps later we'll tighten this up. + */ - /* keep the compiler quiet; never reached */ - Assert(false); return list; } /* - * Locate the n'th cell (counting from 0) of the list. It is an assertion - * failure if there is no such cell. - */ -ListCell * -list_nth_cell(const List *list, int n) -{ - ListCell *match; - - Assert(list != NIL); - Assert(n >= 0); - Assert(n < list->length); - check_list_invariants(list); - - /* Does the caller actually mean to fetch the tail? */ - if (n == list->length - 1) - return list->tail; - - for (match = list->head; n-- > 0; match = match->next) - ; - - return match; -} - -/** * Replace the n-th data pointer in the list with newvalue. * Returns oldvalue. Assumes that n is a valid offset. */ @@ -412,44 +576,11 @@ list_nth_replace(List *list, int n, void *new_data) ListCell *lc = NULL; lc = list_nth_cell(list, n); Assert(lc); - void *old_data = lc->data.ptr_value; - lc->data.ptr_value = new_data; + void *old_data = lc->ptr_value; + lc->ptr_value = new_data; return old_data; } -/* - * Return the data value contained in the n'th element of the - * specified list. (List elements begin at 0.) - */ -void * -list_nth(const List *list, int n) -{ - Assert(IsPointerList(list)); - return lfirst(list_nth_cell(list, n)); -} - -/* - * Return the integer value contained in the n'th element of the - * specified list. - */ -int -list_nth_int(const List *list, int n) -{ - Assert(IsIntegerList(list)); - return lfirst_int(list_nth_cell(list, n)); -} - -/* - * Return the OID value contained in the n'th element of the specified - * list. - */ -Oid -list_nth_oid(const List *list, int n) -{ - Assert(IsOidList(list)); - return lfirst_oid(list_nth_cell(list, n)); -} - /* * Return true iff 'datum' is a member of the list. Equality is * determined via equal(), so callers should ensure that they pass a @@ -534,16 +665,16 @@ list_member_oid(const List *list, Oid datum) } /* - * Delete 'cell' from 'list'; 'prev' is the previous element to 'cell' - * in 'list', if any (i.e. prev == NULL iff list->head == cell) + * Delete the n'th cell (counting from 0) in list. * - * The cell is pfree'd, as is the List header if this was the last member. + * The List is pfree'd if this was the last member. */ List * -list_delete_cell(List *list, ListCell *cell, ListCell *prev) +list_delete_nth_cell(List *list, int n) { check_list_invariants(list); - Assert(prev != NULL ? lnext(prev) == cell : list_head(list) == cell); + + Assert(n >= 0 && n < list->length); /* * If we're about to delete the last node from the list, free the whole @@ -557,23 +688,61 @@ list_delete_cell(List *list, ListCell *cell, ListCell *prev) } /* - * Otherwise, adjust the necessary list links, deallocate the particular - * node we have just removed, and return the list we were given. + * Otherwise, we normally just collapse out the removed element. But for + * debugging purposes, move the whole list contents someplace else. + * + * (Note that we *must* keep the contents in the same memory context.) */ +#ifndef DEBUG_LIST_MEMORY_USAGE + memmove(&list->elements[n], &list->elements[n + 1], + (list->length - 1 - n) * sizeof(ListCell)); list->length--; +#else + { + ListCell *newelems; + int newmaxlen = list->length - 1; + + newelems = (ListCell *) + MemoryContextAlloc(GetMemoryChunkContext(list), + newmaxlen * sizeof(ListCell)); + memcpy(newelems, list->elements, n * sizeof(ListCell)); + memcpy(&newelems[n], &list->elements[n + 1], + (list->length - 1 - n) * sizeof(ListCell)); + if (list->elements != list->initial_elements) + pfree(list->elements); + else + { + /* + * As in enlarge_list(), tell palloc code we're not using the + * initial_elements space anymore. + */ + List *newlist PG_USED_FOR_ASSERTS_ONLY; + + newlist = (List *) repalloc(list, offsetof(List, initial_elements)); + Assert(newlist == list); + } + list->elements = newelems; + list->max_length = newmaxlen; + list->length--; + check_list_invariants(list); + } +#endif - if (prev) - prev->next = cell->next; - else - list->head = cell->next; - - if (list->tail == cell) - list->tail = prev; - - pfree(cell); return list; } +/* + * Delete 'cell' from 'list'. + * + * The List is pfree'd if this was the last member. However, we do not + * touch any data the cell might've been pointing to. + */ +List * +list_delete_cell(List *list, ListCell *cell) +{ + return list_delete_nth_cell(list, cell - list->elements); +} + /* * Delete the first cell in list that matches datum, if any. * Equality is determined via equal(). @@ -582,18 +751,14 @@ List * list_delete(List *list, void *datum) { ListCell *cell; - ListCell *prev; Assert(IsPointerList(list)); check_list_invariants(list); - prev = NULL; foreach(cell, list) { if (equal(lfirst(cell), datum)) - return list_delete_cell(list, cell, prev); - - prev = cell; + return list_delete_cell(list, cell); } /* Didn't find a match: return the list unmodified */ @@ -605,18 +770,14 @@ List * list_delete_ptr(List *list, void *datum) { ListCell *cell; - ListCell *prev; Assert(IsPointerList(list)); check_list_invariants(list); - prev = NULL; foreach(cell, list) { if (lfirst(cell) == datum) - return list_delete_cell(list, cell, prev); - - prev = cell; + return list_delete_cell(list, cell); } /* Didn't find a match: return the list unmodified */ @@ -628,18 +789,14 @@ List * list_delete_int(List *list, int datum) { ListCell *cell; - ListCell *prev; Assert(IsIntegerList(list)); check_list_invariants(list); - prev = NULL; foreach(cell, list) { if (lfirst_int(cell) == datum) - return list_delete_cell(list, cell, prev); - - prev = cell; + return list_delete_cell(list, cell); } /* Didn't find a match: return the list unmodified */ @@ -651,18 +808,14 @@ List * list_delete_oid(List *list, Oid datum) { ListCell *cell; - ListCell *prev; Assert(IsOidList(list)); check_list_invariants(list); - prev = NULL; foreach(cell, list) { if (lfirst_oid(cell) == datum) - return list_delete_cell(list, cell, prev); - - prev = cell; + return list_delete_cell(list, cell); } /* Didn't find a match: return the list unmodified */ @@ -674,8 +827,8 @@ list_delete_oid(List *list, Oid datum) * * This is useful to replace the Lisp-y code "list = lnext(list);" in cases * where the intent is to alter the list rather than just traverse it. - * Beware that the removed cell is freed, whereas the lnext() coding leaves - * the original list head intact if there's another pointer to it. + * Beware that the list is modified, whereas the Lisp-y coding leaves + * the original list head intact in case there's another pointer to it. */ List * list_delete_first(List *list) @@ -685,7 +838,31 @@ list_delete_first(List *list) if (list == NIL) return NIL; /* would an error be better? */ - return list_delete_cell(list, list_head(list), NULL); + return list_delete_nth_cell(list, 0); +} + +/* + * Delete the last element of the list. + * + * This is the opposite of list_delete_first(), but is noticeably cheaper + * with a long list, since no data need be moved. + */ +List * +list_delete_last(List *list) +{ + check_list_invariants(list); + + if (list == NIL) + return NIL; /* would an error be better? */ + + /* list_truncate won't free list if it goes to empty, but this should */ + if (list_length(list) <= 1) + { + list_free(list); + return NIL; + } + + return list_truncate(list, list_length(list) - 1); } /* @@ -703,7 +880,7 @@ list_delete_first(List *list) * in list1 (so it only performs a "union" if list1 is known unique to * start with). Also, if you are about to write "x = list_union(x, y)" * you probably want to use list_concat_unique() instead to avoid wasting - * the list cells of the old x list. + * the storage of the old x list. * * This function could probably be implemented a lot faster if it is a * performance bottleneck. @@ -1028,12 +1205,10 @@ list_append_unique_oid(List *list, Oid datum) * This is almost the same functionality as list_union(), but list1 is * modified in-place rather than being copied. However, callers of this * function may have strict ordering expectations -- i.e. that the relative - * order of those list2 elements that are not duplicates is preserved. Note - * also that list2's cells are not inserted in list1, so the analogy to - * list_concat() isn't perfect. + * order of those list2 elements that are not duplicates is preserved. */ List * -list_concat_unique(List *list1, List *list2) +list_concat_unique(List *list1, const List *list2) { ListCell *cell; @@ -1055,7 +1230,7 @@ list_concat_unique(List *list1, List *list2) * simple pointer equality. */ List * -list_concat_unique_ptr(List *list1, List *list2) +list_concat_unique_ptr(List *list1, const List *list2) { ListCell *cell; @@ -1076,7 +1251,7 @@ list_concat_unique_ptr(List *list1, List *list2) * This variant of list_concat_unique() operates upon lists of integers. */ List * -list_concat_unique_int(List *list1, List *list2) +list_concat_unique_int(List *list1, const List *list2) { ListCell *cell; @@ -1097,7 +1272,7 @@ list_concat_unique_int(List *list1, List *list2) * This variant of list_concat_unique() operates upon lists of OIDs. */ List * -list_concat_unique_oid(List *list1, List *list2) +list_concat_unique_oid(List *list1, const List *list2) { ListCell *cell; @@ -1114,29 +1289,53 @@ list_concat_unique_oid(List *list1, List *list2) return list1; } +/* + * Remove adjacent duplicates in a list of OIDs. + * + * It is caller's responsibility to have sorted the list to bring duplicates + * together, perhaps via list_sort(list, list_oid_cmp). + */ +void +list_deduplicate_oid(List *list) +{ + int len; + + Assert(IsOidList(list)); + len = list_length(list); + if (len > 1) + { + ListCell *elements = list->elements; + int i = 0; + + for (int j = 1; j < len; j++) + { + if (elements[i].oid_value != elements[j].oid_value) + elements[++i].oid_value = elements[j].oid_value; + } + list->length = i + 1; + } + check_list_invariants(list); +} + /* * Free all storage in a list, and optionally the pointed-to elements */ static void list_free_private(List *list, bool deep) { - ListCell *cell; + if (list == NIL) + return; /* nothing to do */ check_list_invariants(list); - cell = list_head(list); - while (cell != NULL) + if (deep) { - ListCell *tmp = cell; - - cell = lnext(cell); - if (deep) - pfree(lfirst(tmp)); - pfree(tmp); + for (int i = 0; i < list->length; i++) + pfree(lfirst(&list->elements[i])); } - - if (list) - pfree(list); + if (list->elements != list->initial_elements) + pfree(list->elements); + pfree(list); } /* @@ -1178,37 +1377,13 @@ List * list_copy(const List *oldlist) { List *newlist; - ListCell *newlist_prev; - ListCell *oldlist_cur; if (oldlist == NIL) return NIL; - newlist = new_list(oldlist->type); - newlist->length = oldlist->length; - - /* - * Copy over the data in the first cell; new_list() has already allocated - * the head cell itself - */ - newlist->head->data = oldlist->head->data; - - newlist_prev = newlist->head; - oldlist_cur = oldlist->head->next; - while (oldlist_cur) - { - ListCell *newlist_cur; - - newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur)); - newlist_cur->data = oldlist_cur->data; - newlist_prev->next = newlist_cur; - - newlist_prev = newlist_cur; - oldlist_cur = oldlist_cur->next; - } - - newlist_prev->next = NULL; - newlist->tail = newlist_prev; + newlist = new_list(oldlist->type, oldlist->length); + memcpy(newlist->elements, oldlist->elements, + newlist->length * sizeof(ListCell)); check_list_invariants(newlist); return newlist; @@ -1221,8 +1396,6 @@ List * list_copy_tail(const List *oldlist, int nskip) { List *newlist; - ListCell *newlist_prev; - ListCell *oldlist_cur; if (nskip < 0) nskip = 0; /* would it be better to elog? */ @@ -1230,125 +1403,80 @@ list_copy_tail(const List *oldlist, int nskip) if (oldlist == NIL || nskip >= oldlist->length) return NIL; - newlist = new_list(oldlist->type); - newlist->length = oldlist->length - nskip; - - /* - * Skip over the unwanted elements. - */ - oldlist_cur = oldlist->head; - while (nskip-- > 0) - oldlist_cur = oldlist_cur->next; - - /* - * Copy over the data in the first remaining cell; new_list() has already - * allocated the head cell itself - */ - newlist->head->data = oldlist_cur->data; - - newlist_prev = newlist->head; - oldlist_cur = oldlist_cur->next; - while (oldlist_cur) - { - ListCell *newlist_cur; - - newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur)); - newlist_cur->data = oldlist_cur->data; - newlist_prev->next = newlist_cur; - - newlist_prev = newlist_cur; - oldlist_cur = oldlist_cur->next; - } - - newlist_prev->next = NULL; - newlist->tail = newlist_prev; + newlist = new_list(oldlist->type, oldlist->length - nskip); + memcpy(newlist->elements, &oldlist->elements[nskip], + newlist->length * sizeof(ListCell)); check_list_invariants(newlist); return newlist; } /* - * Sort a list as though by qsort. + * Return a deep copy of the specified list. * - * A new list is built and returned. Like list_copy, this doesn't make - * fresh copies of any pointed-to data. - * - * The comparator function receives arguments of type ListCell **. + * The list elements are copied via copyObject(), so that this function's + * idea of a "deep" copy is considerably deeper than what list_free_deep() + * means by the same word. */ List * -list_qsort(const List *list, list_qsort_comparator cmp) +list_copy_deep(const List *oldlist) { - int len = list_length(list); - ListCell **list_arr; List *newlist; - ListCell *newlist_prev; - ListCell *cell; - int i; - /* Empty list is easy */ - if (len == 0) + if (oldlist == NIL) return NIL; - /* Flatten list cells into an array, so we can use qsort */ - list_arr = (ListCell **) palloc(sizeof(ListCell *) * len); - i = 0; - foreach(cell, list) - list_arr[i++] = cell; - - qsort(list_arr, len, sizeof(ListCell *), cmp); - - /* Construct new list (this code is much like list_copy) */ - newlist = new_list(list->type); - newlist->length = len; - - /* - * Copy over the data in the first cell; new_list() has already allocated - * the head cell itself - */ - newlist->head->data = list_arr[0]->data; - - newlist_prev = newlist->head; - for (i = 1; i < len; i++) - { - ListCell *newlist_cur; - - newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur)); - newlist_cur->data = list_arr[i]->data; - newlist_prev->next = newlist_cur; - - newlist_prev = newlist_cur; - } - - newlist_prev->next = NULL; - newlist->tail = newlist_prev; + /* This is only sensible for pointer Lists */ + Assert(IsA(oldlist, List)); - /* Might as well free the workspace array */ - pfree(list_arr); + newlist = new_list(oldlist->type, oldlist->length); + for (int i = 0; i < newlist->length; i++) + lfirst(&newlist->elements[i]) = + copyObjectImpl(lfirst(&oldlist->elements[i])); check_list_invariants(newlist); return newlist; } /* - * Temporary compatibility functions + * Sort a list according to the specified comparator function. + * + * The list is sorted in-place. + * + * The comparator function is declared to receive arguments of type + * const ListCell *; this allows it to use lfirst() and variants + * without casting its arguments. Otherwise it behaves the same as + * the comparator function for standard qsort(). * - * In order to avoid warnings for these function definitions, we need - * to include a prototype here as well as in pg_list.h. That's because - * we don't enable list API compatibility in list.c, so we - * don't see the prototypes for these functions. + * Like qsort(), this provides no guarantees about sort stability + * for equal keys. */ +void +list_sort(List *list, list_sort_comparator cmp) +{ + typedef int (*qsort_comparator) (const void *a, const void *b); + int len; + + check_list_invariants(list); + + /* Nothing to do if there's less than two elements */ + len = list_length(list); + if (len > 1) + qsort(list->elements, len, sizeof(ListCell), (qsort_comparator) cmp); +} /* - * Given a list, return its length. This is merely defined for the - * sake of backward compatibility: we can't afford to define a macro - * called "length", so it must be a function. New code should use the - * list_length() macro in order to avoid the overhead of a function - * call. + * list_sort comparator for sorting a list into ascending OID order. */ -int length(const List *list); - int -length(const List *list) +list_oid_cmp(const ListCell *p1, const ListCell *p2) { - return list_length(list); + Oid v1 = lfirst_oid(p1); + Oid v2 = lfirst_oid(p2); + + if (v1 < v2) + return -1; + if (v1 > v2) + return 1; + return 0; } diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 18e0070cb1ca..bf754b358da0 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -1,8 +1,8 @@ /*------------------------------------------------------------------------- * * makefuncs.c - * creator functions for primitive nodes. The functions here are for - * the most frequently created nodes. + * creator functions for various nodes. The functions here are for the + * most frequently created nodes. * * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -735,6 +735,54 @@ make_ands_implicit(Expr *clause) return list_make1(clause); } +/* + * makeIndexInfo + * create an IndexInfo node + */ +IndexInfo * +makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, + List *predicates, bool unique, bool isready, bool concurrent) +{ + IndexInfo *n = makeNode(IndexInfo); + + n->ii_NumIndexAttrs = numattrs; + n->ii_NumIndexKeyAttrs = numkeyattrs; + Assert(n->ii_NumIndexKeyAttrs != 0); + Assert(n->ii_NumIndexKeyAttrs <= n->ii_NumIndexAttrs); + n->ii_Unique = unique; + n->ii_ReadyForInserts = isready; + n->ii_Concurrent = concurrent; + + /* expressions */ + n->ii_Expressions = expressions; + n->ii_ExpressionsState = NIL; + + /* predicates */ + n->ii_Predicate = predicates; + n->ii_PredicateState = NULL; + + /* exclusion constraints */ + n->ii_ExclusionOps = NULL; + n->ii_ExclusionProcs = NULL; + n->ii_ExclusionStrats = NULL; + + /* speculative inserts */ + n->ii_UniqueOps = NULL; + n->ii_UniqueProcs = NULL; + n->ii_UniqueStrats = NULL; + + /* initialize index-build state to default */ + n->ii_BrokenHotChain = false; + n->ii_ParallelWorkers = 0; + + /* set up for possible use by index AM */ + n->ii_Am = amoid; + n->ii_AmCache = NULL; + n->ii_Context = CurrentMemoryContext; + + return n; +} + /* * makeGroupingSet * diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 23ceac10e77e..6c8179dfe3c9 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -461,7 +461,7 @@ exprTypmod(const Node *expr) typmod = exprTypmod((Node *) linitial(cexpr->args)); if (typmod < 0) return -1; /* no point in trying harder */ - for_each_cell(arg, lnext(list_head(cexpr->args))) + for_each_cell(arg, cexpr->args, list_second_cell(cexpr->args)) { Node *e = (Node *) lfirst(arg); @@ -489,7 +489,7 @@ exprTypmod(const Node *expr) typmod = exprTypmod((Node *) linitial(mexpr->args)); if (typmod < 0) return -1; /* no point in trying harder */ - for_each_cell(arg, lnext(list_head(mexpr->args))) + for_each_cell(arg, mexpr->args, list_second_cell(mexpr->args)) { Node *e = (Node *) lfirst(arg); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 0ae2037a004a..1f1ed5a453bc 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -255,7 +255,7 @@ _outList(StringInfo str, const List *node) if (IsA(node, List)) { outNode(str, lfirst(lc)); - if (lnext(lc)) + if (lnext(node, lc)) appendStringInfoChar(str, ' '); } else if (IsA(node, IntList)) @@ -2719,6 +2719,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node) WRITE_NODE_FIELD(cte_plan_ids); WRITE_NODE_FIELD(multiexpr_params); WRITE_NODE_FIELD(eq_classes); + WRITE_BOOL_FIELD(ec_merging_done); WRITE_NODE_FIELD(canon_pathkeys); WRITE_NODE_FIELD(left_join_clauses); WRITE_NODE_FIELD(right_join_clauses); @@ -2787,6 +2788,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node) WRITE_UINT_FIELD(pages); WRITE_FLOAT_FIELD(tuples, "%.0f"); WRITE_FLOAT_FIELD(allvisfrac, "%.6f"); + WRITE_BITMAPSET_FIELD(eclass_indexes); WRITE_NODE_FIELD(subroot); WRITE_NODE_FIELD(subplan_params); WRITE_INT_FIELD(rel_parallel_workers); diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index 373b482c0ff8..c977488264d7 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -420,7 +420,7 @@ print_expr(const Node *expr, const List *rtable) foreach(l, e->args) { print_expr(lfirst(l), rtable); - if (lnext(l)) + if (lnext(e->args, l)) printf(","); } printf(")"); @@ -463,7 +463,7 @@ print_pathkeys(const List *pathkeys, const List *rtable) print_expr((Node *) mem->em_expr, rtable); } printf(")"); - if (lnext(i)) + if (lnext(pathkeys, i)) printf(", "); } printf(")\n"); diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c new file mode 100644 index 000000000000..7b67a29c88ae --- /dev/null +++ b/src/backend/optimizer/geqo/geqo_eval.c @@ -0,0 +1,338 @@ +/*------------------------------------------------------------------------ + * + * geqo_eval.c + * Routines to evaluate query trees + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/backend/optimizer/geqo/geqo_eval.c + * + *------------------------------------------------------------------------- + */ + +/* contributed by: + =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * utesch@aut.tu-freiberg.de * Freiberg, Germany * + =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= + */ + +#include "postgres.h" + +#include +#include +#include + +#include "optimizer/geqo.h" +#include "optimizer/joininfo.h" +#include "optimizer/pathnode.h" +#include "optimizer/paths.h" +#include "utils/memutils.h" + + +/* A "clump" of already-joined relations within gimme_tree */ +typedef struct +{ + RelOptInfo *joinrel; /* joinrel for the set of relations */ + int size; /* number of input relations in clump */ +} Clump; + +static List *merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, + int num_gene, bool force); +static bool desirable_join(PlannerInfo *root, + RelOptInfo *outer_rel, RelOptInfo *inner_rel); + + +/* + * geqo_eval + * + * Returns cost of a query tree as an individual of the population. + * + * If no legal join order can be extracted from the proposed tour, + * returns DBL_MAX. + */ +Cost +geqo_eval(PlannerInfo *root, Gene *tour, int num_gene) +{ + MemoryContext mycontext; + MemoryContext oldcxt; + RelOptInfo *joinrel; + Cost fitness; + int savelength; + struct HTAB *savehash; + + /* + * Create a private memory context that will hold all temp storage + * allocated inside gimme_tree(). + * + * Since geqo_eval() will be called many times, we can't afford to let all + * that memory go unreclaimed until end of statement. Note we make the + * temp context a child of the planner's normal context, so that it will + * be freed even if we abort via ereport(ERROR). + */ + mycontext = AllocSetContextCreate(CurrentMemoryContext, + "GEQO", + ALLOCSET_DEFAULT_SIZES); + oldcxt = MemoryContextSwitchTo(mycontext); + + /* + * gimme_tree will add entries to root->join_rel_list, which may or may + * not already contain some entries. The newly added entries will be + * recycled by the MemoryContextDelete below, so we must ensure that the + * list is restored to its former state before exiting. We can do this by + * truncating the list to its original length. NOTE this assumes that any + * added entries are appended at the end! + * + * We also must take care not to mess up the outer join_rel_hash, if there + * is one. We can do this by just temporarily setting the link to NULL. + * (If we are dealing with enough join rels, which we very likely are, a + * new hash table will get built and used locally.) + * + * join_rel_level[] shouldn't be in use, so just Assert it isn't. + */ + savelength = list_length(root->join_rel_list); + savehash = root->join_rel_hash; + Assert(root->join_rel_level == NULL); + + root->join_rel_hash = NULL; + + /* construct the best path for the given combination of relations */ + joinrel = gimme_tree(root, tour, num_gene); + + /* + * compute fitness, if we found a valid join + * + * XXX geqo does not currently support optimization for partial result + * retrieval, nor do we take any cognizance of possible use of + * parameterized paths --- how to fix? + */ + if (joinrel) + { + Path *best_path = joinrel->cheapest_total_path; + + fitness = best_path->total_cost; + } + else + fitness = DBL_MAX; + + /* + * Restore join_rel_list to its former state, and put back original + * hashtable if any. + */ + root->join_rel_list = list_truncate(root->join_rel_list, + savelength); + root->join_rel_hash = savehash; + + /* release all the memory acquired within gimme_tree */ + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(mycontext); + + return fitness; +} + +/* + * gimme_tree + * Form planner estimates for a join tree constructed in the specified + * order. + * + * 'tour' is the proposed join order, of length 'num_gene' + * + * Returns a new join relation whose cheapest path is the best plan for + * this join order. NB: will return NULL if join order is invalid and + * we can't modify it into a valid order. + * + * The original implementation of this routine always joined in the specified + * order, and so could only build left-sided plans (and right-sided and + * mixtures, as a byproduct of the fact that make_join_rel() is symmetric). + * It could never produce a "bushy" plan. This had a couple of big problems, + * of which the worst was that there are situations involving join order + * restrictions where the only valid plans are bushy. + * + * The present implementation takes the given tour as a guideline, but + * postpones joins that are illegal or seem unsuitable according to some + * heuristic rules. This allows correct bushy plans to be generated at need, + * and as a nice side-effect it seems to materially improve the quality of the + * generated plans. Note however that since it's just a heuristic, it can + * still fail in some cases. (In particular, we might clump together + * relations that actually mustn't be joined yet due to LATERAL restrictions; + * since there's no provision for un-clumping, this must lead to failure.) + */ +RelOptInfo * +gimme_tree(PlannerInfo *root, Gene *tour, int num_gene) +{ + GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private; + List *clumps; + int rel_count; + + /* + * Sometimes, a relation can't yet be joined to others due to heuristics + * or actual semantic restrictions. We maintain a list of "clumps" of + * successfully joined relations, with larger clumps at the front. Each + * new relation from the tour is added to the first clump it can be joined + * to; if there is none then it becomes a new clump of its own. When we + * enlarge an existing clump we check to see if it can now be merged with + * any other clumps. After the tour is all scanned, we forget about the + * heuristics and try to forcibly join any remaining clumps. If we are + * unable to merge all the clumps into one, fail. + */ + clumps = NIL; + + for (rel_count = 0; rel_count < num_gene; rel_count++) + { + int cur_rel_index; + RelOptInfo *cur_rel; + Clump *cur_clump; + + /* Get the next input relation */ + cur_rel_index = (int) tour[rel_count]; + cur_rel = (RelOptInfo *) list_nth(private->initial_rels, + cur_rel_index - 1); + + /* Make it into a single-rel clump */ + cur_clump = (Clump *) palloc(sizeof(Clump)); + cur_clump->joinrel = cur_rel; + cur_clump->size = 1; + + /* Merge it into the clumps list, using only desirable joins */ + clumps = merge_clump(root, clumps, cur_clump, num_gene, false); + } + + if (list_length(clumps) > 1) + { + /* Force-join the remaining clumps in some legal order */ + List *fclumps; + ListCell *lc; + + fclumps = NIL; + foreach(lc, clumps) + { + Clump *clump = (Clump *) lfirst(lc); + + fclumps = merge_clump(root, fclumps, clump, num_gene, true); + } + clumps = fclumps; + } + + /* Did we succeed in forming a single join relation? */ + if (list_length(clumps) != 1) + return NULL; + + return ((Clump *) linitial(clumps))->joinrel; +} + +/* + * Merge a "clump" into the list of existing clumps for gimme_tree. + * + * We try to merge the clump into some existing clump, and repeat if + * successful. When no more merging is possible, insert the clump + * into the list, preserving the list ordering rule (namely, that + * clumps of larger size appear earlier). + * + * If force is true, merge anywhere a join is legal, even if it causes + * a cartesian join to be performed. When force is false, do only + * "desirable" joins. + */ +static List * +merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, int num_gene, + bool force) +{ + ListCell *lc; + int pos; + + /* Look for a clump that new_clump can join to */ + foreach(lc, clumps) + { + Clump *old_clump = (Clump *) lfirst(lc); + + if (force || + desirable_join(root, old_clump->joinrel, new_clump->joinrel)) + { + RelOptInfo *joinrel; + + /* + * Construct a RelOptInfo representing the join of these two input + * relations. Note that we expect the joinrel not to exist in + * root->join_rel_list yet, and so the paths constructed for it + * will only include the ones we want. + */ + joinrel = make_join_rel(root, + old_clump->joinrel, + new_clump->joinrel); + + /* Keep searching if join order is not valid */ + if (joinrel) + { + /* Create paths for partitionwise joins. */ + generate_partitionwise_join_paths(root, joinrel); + + /* + * Except for the topmost scan/join rel, consider gathering + * partial paths. We'll do the same for the topmost scan/join + * rel once we know the final targetlist (see + * grouping_planner). + */ + if (old_clump->size + new_clump->size < num_gene) + generate_gather_paths(root, joinrel, false); + + /* Find and save the cheapest paths for this joinrel */ + set_cheapest(joinrel); + + /* Absorb new clump into old */ + old_clump->joinrel = joinrel; + old_clump->size += new_clump->size; + pfree(new_clump); + + /* Remove old_clump from list */ + clumps = foreach_delete_current(clumps, lc); + + /* + * Recursively try to merge the enlarged old_clump with + * others. When no further merge is possible, we'll reinsert + * it into the list. + */ + return merge_clump(root, clumps, old_clump, num_gene, force); + } + } + } + + /* + * No merging is possible, so add new_clump as an independent clump, in + * proper order according to size. We can be fast for the common case + * where it has size 1 --- it should always go at the end. + */ + if (clumps == NIL || new_clump->size == 1) + return lappend(clumps, new_clump); + + /* Else search for the place to insert it */ + for (pos = 0; pos < list_length(clumps); pos++) + { + Clump *old_clump = (Clump *) list_nth(clumps, pos); + + if (new_clump->size > old_clump->size) + break; /* new_clump belongs before old_clump */ + } + clumps = list_insert_nth(clumps, pos, new_clump); + + return clumps; +} + +/* + * Heuristics for gimme_tree: do we want to join these two relations? + */ +static bool +desirable_join(PlannerInfo *root, + RelOptInfo *outer_rel, RelOptInfo *inner_rel) +{ + /* + * Join if there is an applicable join clause, or if there is a join order + * restriction forcing these rels to be joined. + */ + if (have_relevant_joinclause(root, outer_rel, inner_rel) || + have_join_order_restriction(root, outer_rel, inner_rel)) + return true; + + /* Otherwise postpone the join till later. */ + return false; +} diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 2a0679622ab9..ccb2a9e1b1f3 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -3859,7 +3859,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes, elog(ERROR, "wrong number of tlist entries"); if (exprType((Node *) tle->expr) != lfirst_oid(colType)) safetyInfo->unsafeColumns[tle->resno] = true; - colType = lnext(colType); + colType = lnext(colTypes, colType); } if (colType != NULL) elog(ERROR, "wrong number of tlist entries"); @@ -4440,7 +4440,7 @@ print_restrictclauses(PlannerInfo *root, List *clauses) RestrictInfo *c = lfirst(l); print_expr((Node *) c->clause, root->parse->rtable); - if (lnext(l)) + if (lnext(clauses, l)) printf(", "); } } diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index db7c47b58544..98d890fd5770 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -2035,7 +2035,7 @@ append_nonpartial_cost(List *subpaths, int numpaths, int parallel_workers) * For each of the remaining subpaths, add its cost to the array element * with minimum cost. */ - for_each_cell(l, cell) + for_each_cell(l, subpaths, cell) { Path *subpath = (Path *) lfirst(l); int i; @@ -5293,8 +5293,6 @@ get_foreign_key_join_selectivity(PlannerInfo *root, bool ref_is_outer; List *removedlist; ListCell *cell; - ListCell *prev; - ListCell *next; /* * This FK is not relevant unless it connects a baserel on one side of @@ -5335,14 +5333,12 @@ get_foreign_key_join_selectivity(PlannerInfo *root, worklist = list_copy(worklist); removedlist = NIL; - prev = NULL; - for (cell = list_head(worklist); cell; cell = next) + foreach(cell, worklist) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell); bool remove_it = false; int i; - next = lnext(cell); /* Drop this clause if it matches any column of the FK */ for (i = 0; i < fkinfo->nkeys; i++) { @@ -5382,11 +5378,9 @@ get_foreign_key_join_selectivity(PlannerInfo *root, } if (remove_it) { - worklist = list_delete_cell(worklist, cell, prev); + worklist = foreach_delete_current(worklist, cell); removedlist = lappend(removedlist, rinfo); } - else - prev = cell; } /* diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 688d9b070752..ccc07ba9f0de 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -64,6 +64,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root, bool outer_on_left); static bool reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo); +static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root, + Relids relids); +static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1, + Relids relids2); /* @@ -341,10 +345,11 @@ process_equivalence(PlannerInfo *root, /* * Case 2: need to merge ec1 and ec2. This should never happen after - * we've built any canonical pathkeys; if it did, those pathkeys might - * be rendered non-canonical by the merge. + * the ECs have reached canonical state; otherwise, pathkeys could be + * rendered non-canonical by the merge, and relation eclass indexes + * would get broken by removal of an eq_classes list entry. */ - if (root->canon_pathkeys != NIL) + if (root->ec_merging_done) elog(ERROR, "too late to merge equivalence classes"); /* @@ -743,6 +748,27 @@ get_eclass_for_sort_expr(PlannerInfo *root, root->eq_classes = lappend(root->eq_classes, newec); + /* + * If EC merging is already complete, we have to mop up by adding the new + * EC to the eclass_indexes of the relation(s) mentioned in it. + */ + if (root->ec_merging_done) + { + int ec_index = list_length(root->eq_classes) - 1; + int i = -1; + + while ((i = bms_next_member(newec->ec_relids, i)) > 0) + { + RelOptInfo *rel = root->simple_rel_array[i]; + + Assert(rel->reloptkind == RELOPT_BASEREL || + rel->reloptkind == RELOPT_DEADREL); + + rel->eclass_indexes = bms_add_member(rel->eclass_indexes, + ec_index); + } + } + MemoryContextSwitchTo(oldcontext); return newec; @@ -800,42 +826,71 @@ get_eclass_for_sort_expr(PlannerInfo *root, void generate_base_implied_equalities(PlannerInfo *root) { + int ec_index; ListCell *lc; - Index rti; + /* + * At this point, we're done absorbing knowledge of equivalences in the + * query, so no further EC merging should happen, and ECs remaining in the + * eq_classes list can be considered canonical. (But note that it's still + * possible for new single-member ECs to be added through + * get_eclass_for_sort_expr().) + */ + root->ec_merging_done = true; + + ec_index = 0; foreach(lc, root->eq_classes) { EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc); + bool can_generate_joinclause = false; + int i; Assert(ec->ec_merged == NULL); /* else shouldn't be in list */ Assert(!ec->ec_broken); /* not yet anyway... */ - /* Single-member ECs won't generate any deductions */ - if (list_length(ec->ec_members) <= 1) - continue; + /* + * Generate implied equalities that are restriction clauses. + * Single-member ECs won't generate any deductions, either here or at + * the join level. + */ + if (list_length(ec->ec_members) > 1) + { + if (ec->ec_has_const) + generate_base_implied_equalities_const(root, ec); + else + generate_base_implied_equalities_no_const(root, ec); - if (ec->ec_has_const) - generate_base_implied_equalities_const(root, ec); - else - generate_base_implied_equalities_no_const(root, ec); + /* Recover if we failed to generate required derived clauses */ + if (ec->ec_broken) + generate_base_implied_equalities_broken(root, ec); - /* Recover if we failed to generate required derived clauses */ - if (ec->ec_broken) - generate_base_implied_equalities_broken(root, ec); - } + /* Detect whether this EC might generate join clauses */ + can_generate_joinclause = + (bms_membership(ec->ec_relids) == BMS_MULTIPLE); + } - /* - * This is also a handy place to mark base rels (which should all exist by - * now) with flags showing whether they have pending eclass joins. - */ - for (rti = 1; rti < root->simple_rel_array_size; rti++) - { - RelOptInfo *brel = root->simple_rel_array[rti]; + /* + * Mark the base rels cited in each eclass (which should all exist by + * now) with the eq_classes indexes of all eclasses mentioning them. + * This will let us avoid searching in subsequent lookups. While + * we're at it, we can mark base rels that have pending eclass joins; + * this is a cheap version of has_relevant_eclass_joinclause(). + */ + i = -1; + while ((i = bms_next_member(ec->ec_relids, i)) > 0) + { + RelOptInfo *rel = root->simple_rel_array[i]; - if (brel == NULL) - continue; + Assert(rel->reloptkind == RELOPT_BASEREL); + + rel->eclass_indexes = bms_add_member(rel->eclass_indexes, + ec_index); + + if (can_generate_joinclause) + rel->has_eclass_joins = true; + } - brel->has_eclass_joins = has_relevant_eclass_joinclause(root, brel); + ec_index++; } } @@ -1073,11 +1128,72 @@ generate_join_implied_equalities(PlannerInfo *root, Relids outer_relids, RelOptInfo *inner_rel) { - return generate_join_implied_equalities_for_ecs(root, - root->eq_classes, - join_relids, - outer_relids, - inner_rel); + List *result = NIL; + Relids inner_relids = inner_rel->relids; + Relids nominal_inner_relids; + Relids nominal_join_relids; + Bitmapset * matching_ecs; + int i; + + /* If inner rel is a child, extra setup work is needed */ + if (IS_OTHER_REL(inner_rel)) + { + Assert(!bms_is_empty(inner_rel->top_parent_relids)); + + /* Fetch relid set for the topmost parent rel */ + nominal_inner_relids = inner_rel->top_parent_relids; + /* ECs will be marked with the parent's relid, not the child's */ + nominal_join_relids = bms_union(outer_relids, nominal_inner_relids); + } + else + { + nominal_inner_relids = inner_relids; + nominal_join_relids = join_relids; + } + + /* + * Get all eclasses in common between inner_rel's relids and outer_relids + */ + matching_ecs = get_common_eclass_indexes(root, inner_rel->relids, + outer_relids); + + i = -1; + while ((i = bms_next_member(matching_ecs, i)) >= 0) + { + EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i); + List *sublist = NIL; + + /* ECs containing consts do not need any further enforcement */ + if (ec->ec_has_const) + continue; + + /* Single-member ECs won't generate any deductions */ + if (list_length(ec->ec_members) <= 1) + continue; + + /* Sanity check that this eclass overlaps the join */ + Assert(bms_overlap(ec->ec_relids, nominal_join_relids)); + + if (!ec->ec_broken) + sublist = generate_join_implied_equalities_normal(root, + ec, + join_relids, + outer_relids, + inner_relids); + + /* Recover if we failed to generate required derived clauses */ + if (ec->ec_broken) + sublist = generate_join_implied_equalities_broken(root, + ec, + nominal_join_relids, + outer_relids, + nominal_inner_relids, + inner_rel); + + result = list_concat(result, sublist); + } + + return result; } /* @@ -1573,8 +1689,6 @@ reconsider_outer_join_clauses(PlannerInfo *root) { bool found; ListCell *cell; - ListCell *prev; - ListCell *next; /* Outer loop repeats until we find no more deductions */ do @@ -1582,72 +1696,60 @@ reconsider_outer_join_clauses(PlannerInfo *root) found = false; /* Process the LEFT JOIN clauses */ - prev = NULL; - for (cell = list_head(root->left_join_clauses); cell; cell = next) + foreach(cell, root->left_join_clauses) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell); - next = lnext(cell); if (reconsider_outer_join_clause(root, rinfo, true)) { found = true; /* remove it from the list */ root->left_join_clauses = - list_delete_cell(root->left_join_clauses, cell, prev); + foreach_delete_current(root->left_join_clauses, cell); /* we throw it back anyway (see notes above) */ /* but the thrown-back clause has no extra selectivity */ rinfo->norm_selec = 2.0; rinfo->outer_selec = 1.0; distribute_restrictinfo_to_rels(root, rinfo); } - else - prev = cell; } /* Process the RIGHT JOIN clauses */ - prev = NULL; - for (cell = list_head(root->right_join_clauses); cell; cell = next) + foreach(cell, root->right_join_clauses) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell); - next = lnext(cell); if (reconsider_outer_join_clause(root, rinfo, false)) { found = true; /* remove it from the list */ root->right_join_clauses = - list_delete_cell(root->right_join_clauses, cell, prev); + foreach_delete_current(root->right_join_clauses, cell); /* we throw it back anyway (see notes above) */ /* but the thrown-back clause has no extra selectivity */ rinfo->norm_selec = 2.0; rinfo->outer_selec = 1.0; distribute_restrictinfo_to_rels(root, rinfo); } - else - prev = cell; } /* Process the FULL JOIN clauses */ - prev = NULL; - for (cell = list_head(root->full_join_clauses); cell; cell = next) + foreach(cell, root->full_join_clauses) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell); - next = lnext(cell); if (reconsider_full_join_clause(root, rinfo)) { found = true; /* remove it from the list */ root->full_join_clauses = - list_delete_cell(root->full_join_clauses, cell, prev); + foreach_delete_current(root->full_join_clauses, cell); /* we throw it back anyway (see notes above) */ /* but the thrown-back clause has no extra selectivity */ rinfo->norm_selec = 2.0; rinfo->outer_selec = 1.0; distribute_restrictinfo_to_rels(root, rinfo); } - else - prev = cell; } } while (found); @@ -2036,12 +2138,24 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root, Index var2varno = fkinfo->ref_relid; AttrNumber var2attno = fkinfo->confkey[colno]; Oid eqop = fkinfo->conpfeqop[colno]; + RelOptInfo *rel1 = root->simple_rel_array[var1varno]; + RelOptInfo *rel2 = root->simple_rel_array[var2varno]; List *opfamilies = NIL; /* compute only if needed */ - ListCell *lc1; - - foreach(lc1, root->eq_classes) + Bitmapset *matching_ecs; + int i; + + /* Consider only eclasses mentioning both relations */ + Assert(root->ec_merging_done); + Assert(IS_SIMPLE_REL(rel1)); + Assert(IS_SIMPLE_REL(rel2)); + matching_ecs = bms_intersect(rel1->eclass_indexes, + rel2->eclass_indexes); + + i = -1; + while ((i = bms_next_member(matching_ecs, i)) >= 0) { - EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1); + EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, + i); bool item1member = false; bool item2member = false; ListCell *lc2; @@ -2051,14 +2165,6 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root, continue; /* Note: it seems okay to match to "broken" eclasses here */ - /* - * If eclass visibly doesn't have members for both rels, there's no - * need to grovel through the members. - */ - if (!bms_is_member(var1varno, ec->ec_relids) || - !bms_is_member(var2varno, ec->ec_relids)) - continue; - foreach(lc2, ec->ec_members) { EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2); @@ -2118,12 +2224,20 @@ add_child_rel_equivalences(PlannerInfo *root, RelOptInfo *parent_rel, RelOptInfo *child_rel) { - ListCell *lc1; + int i; - foreach(lc1, root->eq_classes) + /* + * EC merging should be complete already, so we can use the parent rel's + * eclass_indexes to avoid searching all of root->eq_classes. + */ + Assert(root->ec_merging_done); + Assert(IS_SIMPLE_REL(parent_rel)); + + i = -1; + while ((i = bms_next_member(parent_rel->eclass_indexes, i)) >= 0) { - EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1); - ListCell *lc2; + EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i); + int num_members; /* * If this EC contains a volatile expression, then generating child @@ -2133,16 +2247,18 @@ add_child_rel_equivalences(PlannerInfo *root, if (cur_ec->ec_has_volatile) continue; + /* Sanity check eclass_indexes only contain ECs for parent_rel */ + Assert(bms_is_subset(child_rel->top_parent_relids, cur_ec->ec_relids)); + /* - * No point in searching if child's topmost parent rel is not - * mentioned in eclass. + * We don't use foreach() here because there's no point in scanning + * newly-added child members, so we can stop after the last + * pre-existing EC member. */ - if (!bms_is_subset(child_rel->top_parent_relids, cur_ec->ec_relids)) - continue; - - foreach(lc2, cur_ec->ec_members) + num_members = list_length(cur_ec->ec_members); + for (int pos = 0; pos < num_members; pos++) { - EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2); + EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos); if (cur_em->em_is_const) continue; /* ignore consts here */ @@ -2210,6 +2326,9 @@ add_child_rel_equivalences(PlannerInfo *root, (void) add_eq_member(cur_ec, child_expr, new_relids, new_nullable_relids, true, cur_em->em_datatype); + + /* Record this EC index for the child rel */ + child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i); } } } @@ -2249,7 +2368,10 @@ generate_implied_equalities_for_column(PlannerInfo *root, List *result = NIL; bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL); Relids parent_relids; - ListCell *lc1; + int i; + + /* Should be OK to rely on eclass_indexes */ + Assert(root->ec_merging_done); /* Indexes are available only on base or "other" member relations. */ Assert(IS_SIMPLE_REL(rel)); @@ -2260,12 +2382,16 @@ generate_implied_equalities_for_column(PlannerInfo *root, else parent_relids = NULL; /* not used, but keep compiler quiet */ - foreach(lc1, root->eq_classes) + i = -1; + while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0) { - EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1); + EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i); EquivalenceMember *cur_em; ListCell *lc2; + /* Sanity check eclass_indexes only contain ECs for rel */ + Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids)); + /* * Won't generate joinclauses if const or single-member (the latter * test covers the volatile case too) @@ -2273,14 +2399,6 @@ generate_implied_equalities_for_column(PlannerInfo *root, if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1) continue; - /* - * No point in searching if rel not mentioned in eclass (but we can't - * tell that for a child rel). - */ - if (!is_child_rel && - !bms_is_subset(rel->relids, cur_ec->ec_relids)) - continue; - /* * Scan members, looking for a match to the target column. Note that * child EC members are considered, but only when they belong to the @@ -2374,11 +2492,25 @@ bool have_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) { - ListCell *lc1; + Bitmapset *matching_ecs; + int i; - foreach(lc1, root->eq_classes) + /* Examine only eclasses mentioning both rel1 and rel2 */ + matching_ecs = get_common_eclass_indexes(root, rel1->relids, + rel2->relids); + + i = -1; + while ((i = bms_next_member(matching_ecs, i)) >= 0) { - EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1); + EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, + i); + + /* + * Sanity check that get_common_eclass_indexes gave only ECs + * containing both rels. + */ + Assert(bms_overlap(rel1->relids, ec->ec_relids)); + Assert(bms_overlap(rel2->relids, ec->ec_relids)); /* * Won't generate joinclauses if single-member (this test covers the @@ -2390,12 +2522,12 @@ have_relevant_eclass_joinclause(PlannerInfo *root, /* * We do not need to examine the individual members of the EC, because * all that we care about is whether each rel overlaps the relids of - * at least one member, and a test on ec_relids is sufficient to prove - * that. (As with have_relevant_joinclause(), it is not necessary - * that the EC be able to form a joinclause relating exactly the two - * given rels, only that it be able to form a joinclause mentioning - * both, and this will surely be true if both of them overlap - * ec_relids.) + * at least one member, and get_common_eclass_indexes() and the single + * member check above are sufficient to prove that. (As with + * have_relevant_joinclause(), it is not necessary that the EC be able + * to form a joinclause relating exactly the two given rels, only that + * it be able to form a joinclause mentioning both, and this will + * surely be true if both of them overlap ec_relids.) * * Note we don't test ec_broken; if we did, we'd need a separate code * path to look through ec_sources. Checking the membership anyway is @@ -2407,9 +2539,8 @@ have_relevant_eclass_joinclause(PlannerInfo *root, * since the join result is likely to be small even though it'll end * up being an unqualified nestloop. */ - if (bms_overlap(rel1->relids, ec->ec_relids) && - bms_overlap(rel2->relids, ec->ec_relids)) - return true; + + return true; } return false; @@ -2427,11 +2558,17 @@ have_relevant_eclass_joinclause(PlannerInfo *root, bool has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1) { - ListCell *lc1; + Bitmapset *matched_ecs; + int i; - foreach(lc1, root->eq_classes) + /* Examine only eclasses mentioning rel1 */ + matched_ecs = get_eclass_indexes_for_relids(root, rel1->relids); + + i = -1; + while ((i = bms_next_member(matched_ecs, i)) >= 0) { - EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1); + EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, + i); /* * Won't generate joinclauses if single-member (this test covers the @@ -2444,8 +2581,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1) * Per the comment in have_relevant_eclass_joinclause, it's sufficient * to find an EC that mentions both this rel and some other rel. */ - if (bms_overlap(rel1->relids, ec->ec_relids) && - !bms_is_subset(ec->ec_relids, rel1->relids)) + if (!bms_is_subset(ec->ec_relids, rel1->relids)) return true; } @@ -2578,3 +2714,54 @@ is_redundant_with_indexclauses(RestrictInfo *rinfo, List *indexclauses) return false; } + +/* + * get_eclass_indexes_for_relids + * Build and return a Bitmapset containing the indexes into root's + * eq_classes list for all eclasses that mention any of these relids + */ +static Bitmapset * +get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids) +{ + Bitmapset *ec_indexes = NULL; + int i = -1; + + /* Should be OK to rely on eclass_indexes */ + Assert(root->ec_merging_done); + + while ((i = bms_next_member(relids, i)) > 0) + { + RelOptInfo *rel = root->simple_rel_array[i]; + + ec_indexes = bms_add_members(ec_indexes, rel->eclass_indexes); + } + return ec_indexes; +} + +/* + * get_common_eclass_indexes + * Build and return a Bitmapset containing the indexes into root's + * eq_classes list for all eclasses that mention rels in both + * relids1 and relids2. + */ +static Bitmapset * +get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2) +{ + Bitmapset *rel1ecs; + Bitmapset *rel2ecs; + int relid; + + rel1ecs = get_eclass_indexes_for_relids(root, relids1); + + /* + * We can get away with just using the relation's eclass_indexes directly + * when relids2 is a singleton set. + */ + if (bms_get_singleton_member(relids2, &relid)) + rel2ecs = root->simple_rel_array[relid]->eclass_indexes; + else + rel2ecs = get_eclass_indexes_for_relids(root, relids2); + + /* Calculate and return the common EC indexes, recycling the left input. */ + return bms_int_members(rel1ecs, rel2ecs); +} diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 3720e76ed541..1f6859c01759 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -526,7 +526,7 @@ consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel, IndexClause *iclause = (IndexClause *) lfirst(lc); Relids clause_relids = iclause->rinfo->clause_relids; EquivalenceClass *parent_ec = iclause->rinfo->parent_ec; - ListCell *lc2; + int num_considered_relids; /* If we already tried its relids set, no need to do so again */ if (bms_equal_any(clause_relids, *considered_relids)) @@ -539,15 +539,16 @@ consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel, * exponential growth of planning time when there are many clauses, * limit the number of relid sets accepted to 10 * considered_clauses. * - * Note: get_join_index_paths adds entries to *considered_relids, but - * it prepends them to the list, so that we won't visit new entries - * during the inner foreach loop. No real harm would be done if we - * did, since the subset check would reject them; but it would waste - * some cycles. + * Note: get_join_index_paths appends entries to *considered_relids, + * but we do not need to visit such newly-added entries within this + * loop, so we don't use foreach() here. No real harm would be done + * if we did visit them, since the subset check would reject them; but + * it would waste some cycles. */ - foreach(lc2, *considered_relids) + num_considered_relids = list_length(*considered_relids); + for (int pos = 0; pos < num_considered_relids; pos++) { - Relids oldrelids = (Relids) lfirst(lc2); + Relids oldrelids = (Relids) list_nth(*considered_relids, pos); /* * If either is a subset of the other, no new set is possible. @@ -677,10 +678,9 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel, get_index_paths(root, rel, index, &clauseset, bitindexpaths); /* - * Remember we considered paths for this set of relids. We use lcons not - * lappend to avoid confusing the loop in consider_index_join_outer_rels. + * Remember we considered paths for this set of relids. */ - *considered_relids = lcons(relids, *considered_relids); + *considered_relids = lappend(*considered_relids, relids); } /* @@ -1525,7 +1525,6 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths) Cost costsofar; List *qualsofar; Bitmapset *clauseidsofar; - ListCell *lastcell; pathinfo = pathinfoarray[i]; paths = list_make1(pathinfo->path); @@ -1533,7 +1532,6 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths) qualsofar = list_concat(list_copy(pathinfo->quals), list_copy(pathinfo->preds)); clauseidsofar = bms_copy(pathinfo->clauseids); - lastcell = list_head(paths); /* for quick deletions */ for (j = i + 1; j < npaths; j++) { @@ -1574,14 +1572,12 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths) list_copy(pathinfo->preds)); clauseidsofar = bms_add_members(clauseidsofar, pathinfo->clauseids); - lastcell = lnext(lastcell); } else { /* reject new path, remove it from paths list */ - paths = list_delete_cell(paths, lnext(lastcell), lastcell); + paths = list_truncate(paths, list_length(paths) - 1); } - Assert(lnext(lastcell) == NULL); } /* Keep the cheapest AND-group (or singleton) */ @@ -3004,10 +3000,6 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, List *new_ops; List *var_args; List *non_var_args; - ListCell *vargs_cell; - ListCell *nargs_cell; - ListCell *opnos_cell; - ListCell *collids_cell; iclause->rinfo = rinfo; iclause->indexcol = indexcol; @@ -3044,18 +3036,14 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, * indexed relation. */ matching_cols = 1; - vargs_cell = lnext(list_head(var_args)); - nargs_cell = lnext(list_head(non_var_args)); - opnos_cell = lnext(list_head(clause->opnos)); - collids_cell = lnext(list_head(clause->inputcollids)); - while (vargs_cell != NULL) + while (matching_cols < list_length(var_args)) { - Node *varop = (Node *) lfirst(vargs_cell); - Node *constop = (Node *) lfirst(nargs_cell); + Node *varop = (Node *) list_nth(var_args, matching_cols); + Node *constop = (Node *) list_nth(non_var_args, matching_cols); int i; - expr_op = lfirst_oid(opnos_cell); + expr_op = list_nth_oid(clause->opnos, matching_cols); if (!var_on_left) { /* indexkey is on right, so commute the operator */ @@ -3077,7 +3065,8 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, get_op_opfamily_strategy(expr_op, index->opfamily[i]) == op_strategy && IndexCollMatchesExprColl(index->indexcollations[i], - lfirst_oid(collids_cell))) + list_nth_oid(clause->inputcollids, + matching_cols))) break; } if (i >= index->nkeycolumns) @@ -3098,10 +3087,6 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, /* This column matches, keep scanning */ matching_cols++; - vargs_cell = lnext(vargs_cell); - nargs_cell = lnext(nargs_cell); - opnos_cell = lnext(opnos_cell); - collids_cell = lnext(collids_cell); } /* Result is non-lossy if all columns are usable as index quals */ @@ -3900,7 +3885,7 @@ match_index_to_operand(Node *operand, { if (indexpr_item == NULL) elog(ERROR, "wrong number of index expressions"); - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(index->indexprs, indexpr_item); } } if (indexpr_item == NULL) diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 6c93a903bb80..f6f1200e2ec9 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -28,10 +28,11 @@ static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, + List *other_rels_list, ListCell *other_rels); static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, - ListCell *other_rels); + List *other_rels); static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel); static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel); static bool restriction_is_constant_false(List *restrictlist, @@ -108,15 +109,23 @@ join_search_one_level(PlannerInfo *root, int level) * to each initial rel they don't already include but have a join * clause or restriction with. */ + List *other_rels_list; ListCell *other_rels; if (level == 2) /* consider remaining initial rels */ - other_rels = lnext(r); + { + other_rels_list = joinrels[level - 1]; + other_rels = lnext(other_rels_list, r); + } else /* consider all initial rels */ - other_rels = list_head(joinrels[1]); + { + other_rels_list = joinrels[1]; + other_rels = list_head(other_rels_list); + } make_rels_by_clause_joins(root, old_rel, + other_rels_list, other_rels); } else @@ -135,7 +144,7 @@ join_search_one_level(PlannerInfo *root, int level) */ make_rels_by_clauseless_joins(root, old_rel, - list_head(joinrels[1])); + joinrels[1]); } } @@ -161,6 +170,7 @@ join_search_one_level(PlannerInfo *root, int level) foreach(r, joinrels[k]) { RelOptInfo *old_rel = (RelOptInfo *) lfirst(r); + List *other_rels_list; ListCell *other_rels; ListCell *r2; @@ -174,11 +184,18 @@ join_search_one_level(PlannerInfo *root, int level) continue; if (k == other_level) - other_rels = lnext(r); /* only consider remaining rels */ + { + /* only consider remaining rels */ + other_rels_list = joinrels[k]; + other_rels = lnext(other_rels_list, r); + } else - other_rels = list_head(joinrels[other_level]); + { + other_rels_list = joinrels[other_level]; + other_rels = list_head(other_rels_list); + } - for_each_cell(r2, other_rels) + for_each_cell(r2, other_rels_list, other_rels) { RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2); @@ -230,7 +247,7 @@ join_search_one_level(PlannerInfo *root, int level) make_rels_by_clauseless_joins(root, old_rel, - list_head(joinrels[1])); + joinrels[1]); } /*---------- @@ -272,8 +289,9 @@ join_search_one_level(PlannerInfo *root, int level) * automatically ensures that each new joinrel is only added to the list once. * * 'old_rel' is the relation entry for the relation to be joined - * 'other_rels': the first cell in a linked list containing the other + * 'other_rels_list': a list containing the other * rels to be considered for joining + * 'other_rels': the first cell to be considered * * Currently, this is only used with initial rels in other_rels, but it * will work for joining to joinrels too. @@ -281,11 +299,12 @@ join_search_one_level(PlannerInfo *root, int level) static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, + List *other_rels_list, ListCell *other_rels) { ListCell *l; - for_each_cell(l, other_rels) + for_each_cell(l, other_rels_list, other_rels) { RelOptInfo *other_rel = (RelOptInfo *) lfirst(l); @@ -306,8 +325,7 @@ make_rels_by_clause_joins(PlannerInfo *root, * The join rels are returned in root->join_rel_level[join_cur_level]. * * 'old_rel' is the relation entry for the relation to be joined - * 'other_rels': the first cell of a linked list containing the - * other rels to be considered for joining + * 'other_rels': a list containing the other rels to be considered for joining * * Currently, this is only used with initial rels in other_rels, but it would * work for joining to joinrels too. @@ -315,11 +333,11 @@ make_rels_by_clause_joins(PlannerInfo *root, static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, - ListCell *other_rels) + List *other_rels) { ListCell *l; - for_each_cell(l, other_rels) + foreach(l, other_rels) { RelOptInfo *other_rel = (RelOptInfo *) lfirst(l); diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 2040264595cb..11649882ad4b 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -376,9 +376,7 @@ generate_implied_quals(PlannerInfo *root) * entry if there's not one already. * * Note that this function must not be used until after we have completed - * merging EquivalenceClasses. (We don't try to enforce that here; instead, - * equivclass.c will complain if a merge occurs after root->canon_pathkeys - * has become nonempty.) + * merging EquivalenceClasses. */ PathKey * make_canonical_pathkey(PlannerInfo *root, @@ -389,6 +387,10 @@ make_canonical_pathkey(PlannerInfo *root, ListCell *lc; MemoryContext oldcontext; + /* Can't make canonical pathkeys if the set of ECs might still change */ + if (!root->ec_merging_done) + elog(ERROR, "too soon to build canonical pathkeys"); + /* The passed eclass might be non-canonical, so chase up to the top */ while (eclass->ec_merged) eclass = eclass->ec_merged; @@ -2116,7 +2118,7 @@ make_inner_pathkeys_for_merge(PlannerInfo *root, if (!lop) elog(ERROR, "too few pathkeys for mergeclauses"); opathkey = (PathKey *) lfirst(lop); - lop = lnext(lop); + lop = lnext(outer_pathkeys, lop); lastoeclass = opathkey->pk_eclass; if (oeclass != lastoeclass) elog(ERROR, "outer pathkeys do not match mergeclause"); @@ -2196,7 +2198,7 @@ trim_mergeclauses_for_inner_pathkeys(PlannerInfo *root, lip = list_head(pathkeys); pathkey = (PathKey *) lfirst(lip); pathkey_ec = pathkey->pk_eclass; - lip = lnext(lip); + lip = lnext(pathkeys, lip); matched_pathkey = false; /* Scan mergeclauses to see how many we can use */ @@ -2223,7 +2225,7 @@ trim_mergeclauses_for_inner_pathkeys(PlannerInfo *root, break; pathkey = (PathKey *) lfirst(lip); pathkey_ec = pathkey->pk_eclass; - lip = lnext(lip); + lip = lnext(pathkeys, lip); matched_pathkey = false; } diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c index 16c9c7686ea7..b4f7d077b1dc 100644 --- a/src/backend/optimizer/plan/analyzejoins.c +++ b/src/backend/optimizer/plan/analyzejoins.c @@ -96,17 +96,16 @@ remove_useless_joins(PlannerInfo *root, List *joinlist) /* * We can delete this SpecialJoinInfo from the list too, since it's no - * longer of interest. + * longer of interest. (Since we'll restart the foreach loop + * immediately, we don't bother with foreach_delete_current.) */ - root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo); + root->join_info_list = list_delete_cell(root->join_info_list, lc); /* * Restart the scan. This is necessary to ensure we find all * removable joins independently of ordering of the join_info_list * (note that removal of attr_needed bits may make a join appear - * removable that did not before). Also, since we just deleted the - * current list cell, we'd have to have some kluge to continue the - * list scan anyway. + * removable that did not before). */ goto restart; } @@ -316,7 +315,6 @@ remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids) List *joininfos; Index rti; ListCell *l; - ListCell *nextl; /* * Mark the rel as "dead" to show it is no longer part of the join tree. @@ -383,16 +381,15 @@ remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids) * remove or just update the PHV. There is no corresponding test in * join_is_removable because it doesn't need to distinguish those cases. */ - for (l = list_head(root->placeholder_list); l != NULL; l = nextl) + foreach(l, root->placeholder_list) { PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l); - nextl = lnext(l); Assert(!bms_is_member(relid, phinfo->ph_lateral)); if (bms_is_subset(phinfo->ph_needed, joinrelids) && bms_is_member(relid, phinfo->ph_eval_at)) - root->placeholder_list = list_delete_ptr(root->placeholder_list, - phinfo); + root->placeholder_list = foreach_delete_current(root->placeholder_list, + l); else { phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid); @@ -511,13 +508,11 @@ void reduce_unique_semijoins(PlannerInfo *root) { ListCell *lc; - ListCell *next; /* - * Scan the join_info_list to find semijoins. We can't use foreach - * because we may delete the current cell. + * Scan the join_info_list to find semijoins. */ - for (lc = list_head(root->join_info_list); lc != NULL; lc = next) + foreach(lc, root->join_info_list) { SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc); int innerrelid; @@ -525,8 +520,6 @@ reduce_unique_semijoins(PlannerInfo *root) Relids joinrelids; List *restrictlist; - next = lnext(lc); - /* * Must be a non-delaying semijoin to a single baserel, else we aren't * going to be able to do anything with it. (It's probably not @@ -572,7 +565,7 @@ reduce_unique_semijoins(PlannerInfo *root) continue; /* OK, remove the SpecialJoinInfo from the list. */ - root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo); + root->join_info_list = foreach_delete_current(root->join_info_list, lc); } } @@ -915,7 +908,7 @@ query_is_distinct_for(Query *query, List *colnos, List *opids) /* non-resjunk columns should have grouping clauses */ Assert(lg != NULL); sgc = (SortGroupClause *) lfirst(lg); - lg = lnext(lg); + lg = lnext(topop->groupClauses, lg); opid = distinct_col_search(tle->resno, colnos, opids); if (!OidIsValid(opid) || diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 25031379ffb8..73f70afa52c5 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -2459,10 +2459,9 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) chain = NIL; if (list_length(rollups) > 1) { - ListCell *lc2 = lnext(list_head(rollups)); bool is_first_sort = ((RollupData *) linitial(rollups))->is_hashed; - for_each_cell(lc, lc2) + for_each_cell(lc, rollups, list_second_cell(rollups)) { RollupData *rollup = lfirst(lc); AttrNumber *new_grpColIdx; @@ -5238,7 +5237,7 @@ create_mergejoin_plan(PlannerInfo *root, elog(ERROR, "outer pathkeys do not match mergeclauses"); opathkey = (PathKey *) lfirst(lop); opeclass = opathkey->pk_eclass; - lop = lnext(lop); + lop = lnext(outerpathkeys, lop); if (oeclass != opeclass) elog(ERROR, "outer pathkeys do not match mergeclauses"); } @@ -5265,7 +5264,7 @@ create_mergejoin_plan(PlannerInfo *root, if (ieclass == ipeclass) { /* successful first match to this inner pathkey */ - lip = lnext(lip); + lip = lnext(innerpathkeys, lip); first_inner_match = true; } } @@ -5896,7 +5895,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol) else elog(ERROR, "index key does not match expected index column"); } - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(index->indexprs, indexpr_item); } } diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index c1bd1342d14e..71421e58169e 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -148,6 +148,12 @@ query_planner(PlannerInfo *root, /* Select cheapest path (pretty easy in this case...) */ set_cheapest(final_rel); + /* + * We don't need to run generate_base_implied_equalities, but + * we do need to pretend that EC merging is complete. + */ + root->ec_merging_done = true; + /* * We still are required to call qp_callback, in case it's * something like "SELECT 2+2 ORDER BY 1". diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index ecf245c9e4c9..0b7f50d3d67f 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -827,6 +827,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, root->cte_plan_ids = NIL; root->multiexpr_params = NIL; root->eq_classes = NIL; + root->ec_merging_done = false; root->non_eq_clauses = NIL; root->init_plans = NIL; @@ -3747,6 +3748,14 @@ remove_useless_groupby_columns(PlannerInfo *root) if (rte->rtekind != RTE_RELATION) continue; + /* + * We must skip inheritance parent tables as some of the child rels + * may cause duplicate rows. This cannot happen with partitioned + * tables, however. + */ + if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE) + continue; + /* Nothing to do unless this rel has multiple Vars in GROUP BY */ relattnos = groupbyattnos[relid]; if (bms_membership(relattnos) != BMS_MULTIPLE) @@ -3966,7 +3975,7 @@ extract_rollup_sets(List *groupingSets) while (lc1 && lfirst(lc1) == NIL) { ++num_empty; - lc1 = lnext(lc1); + lc1 = lnext(groupingSets, lc1); } /* bail out now if it turns out that all we had were empty sets. */ @@ -4000,7 +4009,7 @@ extract_rollup_sets(List *groupingSets) j = 0; i = 1; - for_each_cell(lc, lc1) + for_each_cell(lc, groupingSets, lc1) { List *candidate = (List *) lfirst(lc); Bitmapset *candidate_set = NULL; @@ -4999,7 +5008,7 @@ consider_groupingsets_paths(PlannerInfo *root, * below, must use the same condition. */ i = 0; - for_each_cell(lc, lnext(list_head(gd->rollups))) + for_each_cell(lc, gd->rollups, list_second_cell(gd->rollups)) { RollupData *rollup = lfirst_node(RollupData, lc); @@ -5033,7 +5042,7 @@ consider_groupingsets_paths(PlannerInfo *root, rollups = list_make1(linitial(gd->rollups)); i = 0; - for_each_cell(lc, lnext(list_head(gd->rollups))) + for_each_cell(lc, gd->rollups, list_second_cell(gd->rollups)) { RollupData *rollup = lfirst_node(RollupData, lc); @@ -5260,7 +5269,7 @@ create_one_window_path(PlannerInfo *root, wc->partitionClause, NIL); - if (lnext(l)) + if (lnext(activeWindows, l)) { /* * Add the current WindowFuncs to the output target for this @@ -6022,7 +6031,7 @@ postprocess_setop_tlist(List *new_tlist, List *orig_tlist) Assert(orig_tlist_item != NULL); orig_tle = lfirst_node(TargetEntry, orig_tlist_item); - orig_tlist_item = lnext(orig_tlist_item); + orig_tlist_item = lnext(orig_tlist, orig_tlist_item); if (orig_tle->resjunk) /* should not happen */ elog(ERROR, "resjunk output columns are not implemented"); Assert(new_tle->resno == orig_tle->resno); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 0337a686c6b6..c384615db721 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -794,7 +794,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, { ptr += sprintf(ptr, "$%d%s", lfirst_int(lc), - lnext(lc) ? "," : ")"); + lnext(splan->setParam, lc) ? "," : ")"); } } diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index d869b16caf8f..566d6b31c834 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1052,6 +1052,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, subroot->cte_plan_ids = NIL; subroot->multiexpr_params = NIL; subroot->eq_classes = NIL; + subroot->ec_merging_done = false; subroot->non_eq_clauses = NIL; subroot->append_rel_list = NIL; subroot->rowMarks = NIL; @@ -3022,8 +3023,6 @@ void remove_useless_result_rtes(PlannerInfo *root) { ListCell *cell; - ListCell *prev; - ListCell *next; /* Top level of jointree must always be a FromExpr */ Assert(IsA(root->parse->jointree, FromExpr)); @@ -3044,16 +3043,12 @@ remove_useless_result_rtes(PlannerInfo *root) * RTE_RESULT RTE; otherwise we'll generate a whole-row Var for the * RTE_RESULT, which the executor has no support for. */ - prev = NULL; - for (cell = list_head(root->rowMarks); cell; cell = next) + foreach(cell, root->rowMarks) { PlanRowMark *rc = (PlanRowMark *) lfirst(cell); - next = lnext(cell); if (rt_fetch(rc->rti, root->parse->rtable)->rtekind == RTE_RESULT) - root->rowMarks = list_delete_cell(root->rowMarks, cell, prev); - else - prev = cell; + root->rowMarks = foreach_delete_current(root->rowMarks, cell); } } @@ -3076,17 +3071,14 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode) FromExpr *f = (FromExpr *) jtnode; Relids result_relids = NULL; ListCell *cell; - ListCell *prev; - ListCell *next; /* * We can drop RTE_RESULT rels from the fromlist so long as at least * one child remains, since joining to a one-row table changes * nothing. The easiest way to mechanize this rule is to modify the - * list in-place, using list_delete_cell. + * list in-place. */ - prev = NULL; - for (cell = list_head(f->fromlist); cell; cell = next) + foreach(cell, f->fromlist) { Node *child = (Node *) lfirst(cell); int varno; @@ -3095,7 +3087,6 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode) child = remove_useless_results_recurse(root, child); /* ... and stick it back into the tree */ lfirst(cell) = child; - next = lnext(cell); /* * If it's an RTE_RESULT with at least one sibling, we can drop @@ -3105,11 +3096,9 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode) if (list_length(f->fromlist) > 1 && (varno = get_result_relid(root, child)) != 0) { - f->fromlist = list_delete_cell(f->fromlist, cell, prev); + f->fromlist = foreach_delete_current(f->fromlist, cell); result_relids = bms_add_member(result_relids, varno); } - else - prev = cell; } /* diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 73a78ceb6748..b306c64715b5 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -300,7 +300,7 @@ expand_targetlist(PlannerInfo *root, List *tlist, int command_type, if (!old_tle->resjunk && old_tle->resno == attrno) { new_tle = old_tle; - tlist_item = lnext(tlist_item); + tlist_item = lnext(tlist, tlist_item); } } @@ -490,7 +490,7 @@ expand_targetlist(PlannerInfo *root, List *tlist, int command_type, } new_tlist = lappend(new_tlist, old_tle); attrno++; - tlist_item = lnext(tlist_item); + tlist_item = lnext(tlist, tlist_item); } return new_tlist; diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 224840be7c09..6e8cf0ba5e7e 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -131,6 +131,15 @@ plan_set_operations(PlannerInfo *root) Assert(parse->windowClause == NIL); Assert(parse->distinctClause == NIL); + /* + * In the outer query level, we won't have any true equivalences to deal + * with; but we do want to be able to make pathkeys, which will require + * single-member EquivalenceClasses. Indicate that EC merging is complete + * so that pathkeys.c won't complain. + */ + Assert(root->eq_classes == NIL); + root->ec_merging_done = true; + /* * We'll need to build RelOptInfos for each of the leaf subqueries, which * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index @@ -381,8 +390,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, * fix_upper_expr() to the Result node's tlist. This would fail if the * Vars generated by generate_setop_tlist() were not exactly equal() * to the corresponding tlist entries of the subplan. However, since - * the subplan was generated by generate_union_plan() or - * generate_nonunion_plan(), and hence its tlist was generated by + * the subplan was generated by generate_union_paths() or + * generate_nonunion_paths(), and hence its tlist was generated by * generate_append_tlist(), this will work. We just tell * generate_setop_tlist() to use varno OUTER (this was changed for * better EXPLAIN output in CDB/MPP; varno 0 is used in PostgreSQL). @@ -1425,7 +1434,7 @@ generate_append_tlist(List *colTypes, List *colCollations, /* types disagree, so force typmod to -1 */ colTypmods[colindex] = -1; } - curColType = lnext(curColType); + curColType = lnext(colTypes, curColType); colindex++; } Assert(curColType == NULL); @@ -1525,7 +1534,7 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist) /* non-resjunk columns should have grouping clauses */ Assert(lg != NULL); sgc = (SortGroupClause *) lfirst(lg); - lg = lnext(lg); + lg = lnext(grouplist, lg); Assert(sgc->tleSortGroupRef == 0); sgc->tleSortGroupRef = tle->ressortgroupref; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 6d25bc25ac9e..c4139854047f 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -959,11 +959,9 @@ is_parallel_safe(PlannerInfo *root, Node *node) foreach(l, proot->init_plans) { SubPlan *initsubplan = (SubPlan *) lfirst(l); - ListCell *l2; - foreach(l2, initsubplan->setParam) - context.safe_param_ids = lcons_int(lfirst_int(l2), - context.safe_param_ids); + context.safe_param_ids = list_concat(context.safe_param_ids, + initsubplan->setParam); } } @@ -1093,6 +1091,7 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) context->safe_param_ids); if (max_parallel_hazard_walker(subplan->testexpr, context)) return true; /* no need to restore safe_param_ids */ + list_free(context->safe_param_ids); context->safe_param_ids = save_safe_param_ids; /* we must also check args, but no special Param treatment there */ if (max_parallel_hazard_walker((Node *) subplan->args, context)) @@ -4487,8 +4486,8 @@ add_function_defaults(List *args, HeapTuple func_tuple) ndelete = nargsprovided + list_length(defaults) - funcform->pronargs; if (ndelete < 0) elog(ERROR, "not enough default arguments"); - while (ndelete-- > 0) - defaults = list_delete_first(defaults); + if (ndelete > 0) + defaults = list_copy_tail(defaults, ndelete); /* And form the combined argument list, not modifying the input list */ return list_concat(list_copy(args), defaults); @@ -5037,9 +5036,9 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid, * Recursively try to simplify the modified expression. Here we must add * the current function to the context list of active functions. */ - context->active_fns = lcons_oid(funcid, context->active_fns); + context->active_fns = lappend_oid(context->active_fns, funcid); newexpr = eval_const_expressions_mutator(newexpr, context); - context->active_fns = list_delete_first(context->active_fns); + context->active_fns = list_delete_last(context->active_fns); error_context_stack = sqlerrcontext.previous; @@ -5731,7 +5730,7 @@ tlist_matches_coltypelist(List *tlist, List *coltypelist) return false; /* too many tlist items */ coltype = lfirst_oid(clistitem); - clistitem = lnext(clistitem); + clistitem = lnext(coltypelist, clistitem); if (exprType((Node *) tle->expr) != coltype) return false; /* column type mismatch */ diff --git a/src/backend/optimizer/util/paramassign.c b/src/backend/optimizer/util/paramassign.c index d098058205e1..fbb4351bd913 100644 --- a/src/backend/optimizer/util/paramassign.c +++ b/src/backend/optimizer/util/paramassign.c @@ -560,17 +560,12 @@ identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids) { List *result; ListCell *cell; - ListCell *prev; - ListCell *next; result = NIL; - prev = NULL; - for (cell = list_head(root->curOuterParams); cell; cell = next) + foreach(cell, root->curOuterParams) { NestLoopParam *nlp = (NestLoopParam *) lfirst(cell); - next = lnext(cell); - /* * We are looking for Vars and PHVs that can be supplied by the * lefthand rels. The "bms_overlap" test is just an optimization to @@ -579,8 +574,8 @@ identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids) if (IsA(nlp->paramval, Var) && bms_is_member(nlp->paramval->varno, leftrelids)) { - root->curOuterParams = list_delete_cell(root->curOuterParams, - cell, prev); + root->curOuterParams = foreach_delete_current(root->curOuterParams, + cell); result = lappend(result, nlp); } else if (IsA(nlp->paramval, PlaceHolderVar) && @@ -591,12 +586,10 @@ identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids) false)->ph_eval_at, leftrelids)) { - root->curOuterParams = list_delete_cell(root->curOuterParams, - cell, prev); + root->curOuterParams = foreach_delete_current(root->curOuterParams, + cell); result = lappend(result, nlp); } - else - prev = cell; } return result; } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 1a454a97a062..f4981bdad598 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -65,8 +65,8 @@ typedef enum #define STD_FUZZ_FACTOR 1.01 static List *translate_sub_tlist(List *tlist, int relid); -static int append_total_cost_compare(const void *a, const void *b); -static int append_startup_cost_compare(const void *a, const void *b); +static int append_total_cost_compare(const ListCell *a, const ListCell *b); +static int append_startup_cost_compare(const ListCell *a, const ListCell *b); static List *reparameterize_pathlist_by_child(PlannerInfo *root, List *pathlist, RelOptInfo *child_rel); @@ -448,11 +448,9 @@ void add_path(RelOptInfo *parent_rel, Path *new_path) { bool accept_new = true; /* unless we find a superior old path */ - ListCell *insert_after = NULL; /* where to insert new item */ + int insert_at = 0; /* where to insert new item */ List *new_path_pathkeys; ListCell *p1; - ListCell *p1_prev; - ListCell *p1_next; /* * This is a convenient place to check for query cancel --- no part of the @@ -479,12 +477,8 @@ add_path(RelOptInfo *parent_rel, Path *new_path) * Loop to check proposed new path against old paths. Note it is possible * for more than one old path to be tossed out because new_path dominates * it. - * - * We can't use foreach here because the loop body may delete the current - * list cell. */ - p1_prev = NULL; - for (p1 = list_head(parent_rel->pathlist); p1 != NULL; p1 = p1_next) + foreach(p1, parent_rel->pathlist) { Path *old_path = (Path *) lfirst(p1); bool remove_old = false; /* unless new proves superior */ @@ -492,8 +486,6 @@ add_path(RelOptInfo *parent_rel, Path *new_path) PathKeysComparison keyscmp; BMS_Comparison outercmp; - p1_next = lnext(p1); - /* * Do a fuzzy cost comparison with standard fuzziness limit. */ @@ -660,23 +652,20 @@ add_path(RelOptInfo *parent_rel, Path *new_path) */ if (remove_old) { - parent_rel->pathlist = list_delete_cell(parent_rel->pathlist, - p1, p1_prev); + parent_rel->pathlist = foreach_delete_current(parent_rel->pathlist, + p1); /* * Delete the data pointed-to by the deleted cell, if possible */ if (!IsA(old_path, IndexPath)) pfree(old_path); - /* p1_prev does not advance */ } else { /* new belongs after this old path if it has cost >= old's */ if (new_path->total_cost >= old_path->total_cost) - insert_after = p1; - /* p1_prev advances */ - p1_prev = p1; + insert_at = foreach_current_index(p1) + 1; } /* @@ -691,10 +680,8 @@ add_path(RelOptInfo *parent_rel, Path *new_path) if (accept_new) { /* Accept the new path: insert it at proper place in pathlist */ - if (insert_after) - lappend_cell(parent_rel->pathlist, insert_after, new_path); - else - parent_rel->pathlist = lcons(new_path, parent_rel->pathlist); + parent_rel->pathlist = + list_insert_nth(parent_rel->pathlist, insert_at, new_path); } else { @@ -830,10 +817,8 @@ void add_partial_path(RelOptInfo *parent_rel, Path *new_path) { bool accept_new = true; /* unless we find a superior old path */ - ListCell *insert_after = NULL; /* where to insert new item */ + int insert_at = 0; /* where to insert new item */ ListCell *p1; - ListCell *p1_prev; - ListCell *p1_next; /* Check for query cancel. */ CHECK_FOR_INTERRUPTS(); @@ -848,16 +833,12 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path) * As in add_path, throw out any paths which are dominated by the new * path, but throw out the new path if some existing path dominates it. */ - p1_prev = NULL; - for (p1 = list_head(parent_rel->partial_pathlist); p1 != NULL; - p1 = p1_next) + foreach(p1, parent_rel->partial_pathlist) { Path *old_path = (Path *) lfirst(p1); bool remove_old = false; /* unless new proves superior */ PathKeysComparison keyscmp; - p1_next = lnext(p1); - /* Compare pathkeys. */ keyscmp = compare_pathkeys(new_path->pathkeys, old_path->pathkeys); @@ -908,17 +889,14 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path) if (remove_old) { parent_rel->partial_pathlist = - list_delete_cell(parent_rel->partial_pathlist, p1, p1_prev); + foreach_delete_current(parent_rel->partial_pathlist, p1); pfree(old_path); - /* p1_prev does not advance */ } else { /* new belongs after this old path if it has cost >= old's */ if (new_path->total_cost >= old_path->total_cost) - insert_after = p1; - /* p1_prev advances */ - p1_prev = p1; + insert_at = foreach_current_index(p1) + 1; } /* @@ -933,11 +911,8 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path) if (accept_new) { /* Accept the new path: insert it at proper place */ - if (insert_after) - lappend_cell(parent_rel->partial_pathlist, insert_after, new_path); - else - parent_rel->partial_pathlist = - lcons(new_path, parent_rel->partial_pathlist); + parent_rel->partial_pathlist = + list_insert_nth(parent_rel->partial_pathlist, insert_at, new_path); } else { @@ -1366,9 +1341,8 @@ create_append_path(PlannerInfo *root, */ Assert(pathkeys == NIL); - subpaths = list_qsort(subpaths, append_total_cost_compare); - partial_subpaths = list_qsort(partial_subpaths, - append_startup_cost_compare); + list_sort(subpaths, append_total_cost_compare); + list_sort(partial_subpaths, append_startup_cost_compare); } pathnode->first_partial_path = list_length(subpaths); pathnode->subpaths = list_concat(subpaths, partial_subpaths); @@ -1426,17 +1400,18 @@ create_append_path(PlannerInfo *root, /* * append_total_cost_compare - * qsort comparator for sorting append child paths by total_cost descending + * list_sort comparator for sorting append child paths + * by total_cost descending * * For equal total costs, we fall back to comparing startup costs; if those * are equal too, break ties using bms_compare on the paths' relids. - * (This is to avoid getting unpredictable results from qsort.) + * (This is to avoid getting unpredictable results from list_sort.) */ static int -append_total_cost_compare(const void *a, const void *b) +append_total_cost_compare(const ListCell *a, const ListCell *b) { - Path *path1 = (Path *) lfirst(*(ListCell **) a); - Path *path2 = (Path *) lfirst(*(ListCell **) b); + Path *path1 = (Path *) lfirst(a); + Path *path2 = (Path *) lfirst(b); int cmp; cmp = compare_path_costs(path1, path2, TOTAL_COST); @@ -1447,17 +1422,18 @@ append_total_cost_compare(const void *a, const void *b) /* * append_startup_cost_compare - * qsort comparator for sorting append child paths by startup_cost descending + * list_sort comparator for sorting append child paths + * by startup_cost descending * * For equal startup costs, we fall back to comparing total costs; if those * are equal too, break ties using bms_compare on the paths' relids. - * (This is to avoid getting unpredictable results from qsort.) + * (This is to avoid getting unpredictable results from list_sort.) */ static int -append_startup_cost_compare(const void *a, const void *b) +append_startup_cost_compare(const ListCell *a, const ListCell *b) { - Path *path1 = (Path *) lfirst(*(ListCell **) a); - Path *path2 = (Path *) lfirst(*(ListCell **) b); + Path *path1 = (Path *) lfirst(a); + Path *path2 = (Path *) lfirst(b); int cmp; cmp = compare_path_costs(path1, path2, STARTUP_COST); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 17b281475105..3e5d5d7bb14f 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -440,6 +440,13 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, index_close(indexRelation, NoLock); + /* + * We've historically used lcons() here. It'd make more sense to + * use lappend(), but that causes the planner to change behavior + * in cases where two indexes seem equally attractive. For now, + * stick with lcons() --- few tables should have so many indexes + * that the O(N^2) behavior of lcons() is really a problem. + */ indexinfos = lcons(info, indexinfos); } @@ -1570,7 +1577,7 @@ get_relation_statistics(RelOptInfo *rel, Relation relation) info->kind = STATS_EXT_NDISTINCT; info->keys = bms_copy(keys); - stainfos = lcons(info, stainfos); + stainfos = lappend(stainfos, info); } if (statext_is_kind_built(dtup, STATS_EXT_DEPENDENCIES)) @@ -1582,7 +1589,7 @@ get_relation_statistics(RelOptInfo *rel, Relation relation) info->kind = STATS_EXT_DEPENDENCIES; info->keys = bms_copy(keys); - stainfos = lcons(info, stainfos); + stainfos = lappend(stainfos, info); } if (statext_is_kind_built(dtup, STATS_EXT_MCV)) @@ -1594,7 +1601,7 @@ get_relation_statistics(RelOptInfo *rel, Relation relation) info->kind = STATS_EXT_MCV; info->keys = bms_copy(keys); - stainfos = lcons(info, stainfos); + stainfos = lappend(stainfos, info); } ReleaseSysCache(htup); @@ -2003,7 +2010,7 @@ build_index_tlist(PlannerInfo *root, IndexOptInfo *index, if (indexpr_item == NULL) elog(ERROR, "wrong number of index expressions"); indexvar = (Expr *) lfirst(indexpr_item); - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(index->indexprs, indexpr_item); } tlist = lappend(tlist, @@ -2562,7 +2569,7 @@ set_baserel_partition_key_exprs(Relation relation, /* Re-stamp the expression with given varno. */ partexpr = (Expr *) copyObject(lfirst(lc)); ChangeVarNodes((Node *) partexpr, 1, varno, 0); - lc = lnext(lc); + lc = lnext(partkey->partexprs, lc); } partexprs[cnt] = list_make1(partexpr); diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c index bc9fd94d7d6a..ba237a182b8a 100644 --- a/src/backend/optimizer/util/predtest.c +++ b/src/backend/optimizer/util/predtest.c @@ -65,6 +65,7 @@ typedef struct PredIterInfoData { /* node-type-specific iteration state */ void *state; + List *state_list; /* initialize to do the iteration */ void (*startup_fn) (Node *clause, PredIterInfo info); /* next-component iteration function */ @@ -912,7 +913,8 @@ predicate_classify(Node *clause, PredIterInfo info) static void list_startup_fn(Node *clause, PredIterInfo info) { - info->state = (void *) list_head((List *) clause); + info->state_list = (List *) clause; + info->state = (void *) list_head(info->state_list); } static Node * @@ -924,7 +926,7 @@ list_next_fn(PredIterInfo info) if (l == NULL) return NULL; n = lfirst(l); - info->state = (void *) lnext(l); + info->state = (void *) lnext(info->state_list, l); return n; } @@ -941,7 +943,8 @@ list_cleanup_fn(PredIterInfo info) static void boolexpr_startup_fn(Node *clause, PredIterInfo info) { - info->state = (void *) list_head(((BoolExpr *) clause)->args); + info->state_list = ((BoolExpr *) clause)->args; + info->state = (void *) list_head(info->state_list); } /* @@ -1064,6 +1067,7 @@ arrayexpr_startup_fn(Node *clause, PredIterInfo info) /* Initialize iteration variable to first member of ArrayExpr */ arrayexpr = (ArrayExpr *) lsecond(saop->args); + info->state_list = arrayexpr->elements; state->next = list_head(arrayexpr->elements); } @@ -1075,7 +1079,7 @@ arrayexpr_next_fn(PredIterInfo info) if (state->next == NULL) return NULL; lsecond(state->opexpr.args) = lfirst(state->next); - state->next = lnext(state->next); + state->next = lnext(info->state_list, state->next); return (Node *) &(state->opexpr); } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 305e5ccb56fe..a2f83bf87870 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -222,6 +222,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->pages = 0; rel->tuples = 0; rel->allvisfrac = 0; + rel->eclass_indexes = NULL; rel->subroot = NULL; rel->subplan_params = NIL; rel->rel_parallel_workers = -1; /* set up in get_relation_info */ @@ -666,6 +667,7 @@ build_join_rel(PlannerInfo *root, joinrel->pages = 0; joinrel->tuples = 0; joinrel->allvisfrac = 0; + joinrel->eclass_indexes = NULL; joinrel->subroot = NULL; joinrel->subplan_params = NIL; joinrel->rel_parallel_workers = -1; @@ -853,6 +855,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, joinrel->pages = 0; joinrel->tuples = 0; joinrel->allvisfrac = 0; + joinrel->eclass_indexes = NULL; joinrel->subroot = NULL; joinrel->subplan_params = NIL; joinrel->serverid = InvalidOid; diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 0d3e2e0e9d73..e0a918d495ca 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -329,7 +329,7 @@ tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK) return false; /* tlist longer than colTypes */ if (exprType((Node *) tle->expr) != lfirst_oid(curColType)) return false; - curColType = lnext(curColType); + curColType = lnext(colTypes, curColType); } } if (curColType != NULL) @@ -363,7 +363,7 @@ tlist_same_collations(List *tlist, List *colCollations, bool junkOK) return false; /* tlist longer than colCollations */ if (exprCollation((Node *) tle->expr) != lfirst_oid(curColColl)) return false; - curColColl = lnext(curColColl); + curColColl = lnext(colCollations, curColColl); } } if (curColColl != NULL) @@ -1237,7 +1237,7 @@ split_pathtarget_at_srfs(PlannerInfo *root, List *level_srfs = (List *) lfirst(lc1); PathTarget *ntarget; - if (lnext(lc1) == NULL) + if (lnext(context.level_srfs, lc1) == NULL) { ntarget = target; } @@ -1252,13 +1252,15 @@ split_pathtarget_at_srfs(PlannerInfo *root, * later levels. */ add_sp_items_to_pathtarget(ntarget, level_srfs); - for_each_cell(lc, lnext(lc2)) + for_each_cell(lc, context.level_input_vars, + lnext(context.level_input_vars, lc2)) { List *input_vars = (List *) lfirst(lc); add_sp_items_to_pathtarget(ntarget, input_vars); } - for_each_cell(lc, lnext(lc3)) + for_each_cell(lc, context.level_input_srfs, + lnext(context.level_input_srfs, lc3)) { List *input_srfs = (List *) lfirst(lc); ListCell *lcx; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index e27e267da616..01961bfb21c5 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -2809,7 +2809,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) target_rte->updatedCols = bms_add_member(target_rte->updatedCols, attrno - FirstLowInvalidHeapAttributeNumber); - orig_tl = lnext(orig_tl); + orig_tl = lnext(origTlist, orig_tl); } if (orig_tl != NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 619382c30cb3..2208a8961437 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -18916,7 +18916,7 @@ makeColumnRef(char *colname, List *indirection, else if (IsA(lfirst(l), A_Star)) { /* We only allow '*' at the end of a ColumnRef */ - if (lnext(l) != NULL) + if (lnext(indirection, l) != NULL) parser_yyerror("improper use of \"*\""); } nfields++; @@ -19105,7 +19105,7 @@ check_indirection(List *indirection, core_yyscan_t yyscanner) { if (IsA(lfirst(l), A_Star)) { - if (lnext(l) != NULL) + if (lnext(indirection, l) != NULL) parser_yyerror("improper use of \"*\""); } } @@ -19549,20 +19549,15 @@ SplitColQualList(List *qualList, core_yyscan_t yyscanner) { ListCell *cell; - ListCell *prev; - ListCell *next; *collClause = NULL; - prev = NULL; - for (cell = list_head(qualList); cell; cell = next) + foreach(cell, qualList) { Node *n = (Node *) lfirst(cell); - next = lnext(cell); if (IsA(n, Constraint)) { /* keep it in list */ - prev = cell; continue; } if (IsA(n, CollateClause)) @@ -19579,7 +19574,7 @@ SplitColQualList(List *qualList, else elog(ERROR, "unexpected node type %d", (int) n->type); /* remove non-Constraint nodes from qualList */ - qualList = list_delete_cell(qualList, cell, prev); + qualList = foreach_delete_current(qualList, cell); } *constraintList = qualList; } diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 79c28c1c5f36..0cbba25f30c8 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -63,7 +63,7 @@ static int check_agg_arguments(ParseState *pstate, static bool check_agg_arguments_walker(Node *node, check_agg_arguments_context *context); static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry, - List *groupClauses, List *groupClauseVars, + List *groupClauses, List *groupClauseCommonVars, bool have_non_var_grouping, List **func_grouped_rels); static bool check_ungrouped_columns_walker(Node *node, @@ -1153,7 +1153,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) if (gset_common) { - for_each_cell(l, lnext(list_head(gsets))) + for_each_cell(l, gsets, list_second_cell(gsets)) { gset_common = list_intersection_int(gset_common, lfirst(l)); if (!gset_common) @@ -1202,7 +1202,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) if (expr == NULL) continue; /* probably cannot happen */ - groupClauses = lcons(expr, groupClauses); + groupClauses = lappend(groupClauses, expr); } /* @@ -1802,11 +1802,12 @@ expand_groupingset_node(GroupingSet *gs) return result; } +/* list_sort comparator to sort sub-lists by length */ static int -cmp_list_len_asc(const void *a, const void *b) +cmp_list_len_asc(const ListCell *a, const ListCell *b) { - int la = list_length(*(List *const *) a); - int lb = list_length(*(List *const *) b); + int la = list_length((const List *) lfirst(a)); + int lb = list_length((const List *) lfirst(b)); return (la > lb) ? 1 : (la == lb) ? 0 : -1; } @@ -1857,7 +1858,7 @@ expand_grouping_sets(List *groupingSets, int limit) result = lappend(result, list_union_int(NIL, (List *) lfirst(lc))); } - for_each_cell(lc, lnext(list_head(expanded_groups))) + for_each_cell(lc, expanded_groups, list_second_cell(expanded_groups)) { List *p = lfirst(lc); List *new_result = NIL; @@ -1877,27 +1878,8 @@ expand_grouping_sets(List *groupingSets, int limit) result = new_result; } - if (list_length(result) > 1) - { - int result_len = list_length(result); - List **buf = palloc(sizeof(List *) * result_len); - List **ptr = buf; - - foreach(lc, result) - { - *ptr++ = lfirst(lc); - } - - qsort(buf, result_len, sizeof(List *), cmp_list_len_asc); - - result = NIL; - ptr = buf; - - while (result_len-- > 0) - result = lappend(result, *ptr++); - - pfree(buf); - } + /* Now sort the lists by length */ + list_sort(result, cmp_list_len_asc); return result; } diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index f127cdd2904a..93dc3d8b7a6d 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -1129,7 +1129,7 @@ coerce_record_to_complex(ParseState *pstate, Node *node, parser_coercion_errposition(pstate, location, expr))); newargs = lappend(newargs, cexpr); ucolno++; - arg = lnext(arg); + arg = lnext(args, arg); } if (arg != NULL) ereport(ERROR, @@ -1327,7 +1327,7 @@ select_common_type(ParseState *pstate, List *exprs, const char *context, Assert(exprs != NIL); pexpr = (Node *) linitial(exprs); - lc = lnext(list_head(exprs)); + lc = list_second_cell(exprs); ptype = exprType(pexpr); /* @@ -1337,7 +1337,7 @@ select_common_type(ParseState *pstate, List *exprs, const char *context, */ if (ptype != UNKNOWNOID) { - for_each_cell(lc, lc) + for_each_cell(lc, exprs, lc) { Node *nexpr = (Node *) lfirst(lc); Oid ntype = exprType(nexpr); @@ -1361,7 +1361,7 @@ select_common_type(ParseState *pstate, List *exprs, const char *context, ptype = getBaseType(ptype); get_type_category_preferred(ptype, &pcategory, &pispreferred); - for_each_cell(lc, lc) + for_each_cell(lc, exprs, lc) { Node *nexpr = (Node *) lfirst(lc); Oid ntype = getBaseType(exprType(nexpr)); diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c index 82f2e4e1eee2..c431eab55a00 100644 --- a/src/backend/parser/parse_collate.c +++ b/src/backend/parser/parse_collate.c @@ -952,7 +952,7 @@ assign_hypothetical_collations(Aggref *aggref, while (extra_args-- > 0) { (void) assign_collations_walker((Node *) lfirst(h_cell), loccontext); - h_cell = lnext(h_cell); + h_cell = lnext(aggref->aggdirectargs, h_cell); } /* Scan hypothetical args and aggregated args in parallel */ @@ -1033,8 +1033,8 @@ assign_hypothetical_collations(Aggref *aggref, paircontext.location2, loccontext); - h_cell = lnext(h_cell); - s_cell = lnext(s_cell); + h_cell = lnext(aggref->aggdirectargs, h_cell); + s_cell = lnext(aggref->args, s_cell); } Assert(h_cell == NULL && s_cell == NULL); } diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index 10f10f2139af..e3001ae53e66 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -135,7 +135,7 @@ transformWithClause(ParseState *pstate, WithClause *withClause) CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); ListCell *rest; - for_each_cell(rest, lnext(lc)) + for_each_cell(rest, withClause->ctes, lnext(withClause->ctes, lc)) { CommonTableExpr *cte2 = (CommonTableExpr *) lfirst(rest); @@ -353,9 +353,9 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) get_collation_name(exprCollation(texpr))), errhint("Use the COLLATE clause to set the collation of the non-recursive term."), parser_errposition(pstate, exprLocation(texpr)))); - lctyp = lnext(lctyp); - lctypmod = lnext(lctypmod); - lccoll = lnext(lccoll); + lctyp = lnext(cte->ctecoltypes, lctyp); + lctypmod = lnext(cte->ctecoltypmods, lctypmod); + lccoll = lnext(cte->ctecolcollations, lccoll); } if (lctyp != NULL || lctypmod != NULL || lccoll != NULL) /* shouldn't happen */ elog(ERROR, "wrong number of output columns in WITH"); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index cd25b0cea434..5dbe0a385483 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2332,7 +2332,6 @@ transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault) RowExpr *newr; char fname[16]; int fnum; - ListCell *lc; newr = makeNode(RowExpr); @@ -2354,10 +2353,9 @@ transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault) /* ROW() has anonymous columns, so invent some field names */ newr->colnames = NIL; - fnum = 1; - foreach(lc, newr->args) + for (fnum = 1; fnum <= list_length(newr->args); fnum++) { - snprintf(fname, sizeof(fname), "f%d", fnum++); + snprintf(fname, sizeof(fname), "f%d", fnum); newr->colnames = lappend(newr->colnames, makeString(pstrdup(fname))); } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 0d69979ef9da..49a42d035388 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -108,7 +108,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, Oid rettype; Oid funcid; ListCell *l; - ListCell *nextl; Node *first_arg = NULL; int nargs; int nargsplusdefs; @@ -155,21 +154,18 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * to distinguish "input" and "output" parameter symbols while parsing * function-call constructs. Don't do this if dealing with column syntax, * nor if we had WITHIN GROUP (because in that case it's critical to keep - * the argument count unchanged). We can't use foreach() because we may - * modify the list ... + * the argument count unchanged). */ nargs = 0; - for (l = list_head(fargs); l != NULL; l = nextl) + foreach(l, fargs) { Node *arg = lfirst(l); Oid argtype = exprType(arg); - nextl = lnext(l); - if (argtype == VOIDOID && IsA(arg, Param) && !is_column && !agg_within_group) { - fargs = list_delete_ptr(fargs, arg); + fargs = foreach_delete_current(fargs, l); continue; } @@ -1737,8 +1733,8 @@ func_get_detail(List *funcname, int ndelete; ndelete = list_length(defaults) - best_candidate->ndargs; - while (ndelete-- > 0) - defaults = list_delete_first(defaults); + if (ndelete > 0) + defaults = list_copy_tail(defaults, ndelete); *argdefaults = defaults; } } @@ -1792,11 +1788,9 @@ unify_hypothetical_args(ParseState *pstate, Oid *actual_arg_types, Oid *declared_arg_types) { - Node *args[FUNC_MAX_ARGS]; int numDirectArgs, numNonHypotheticalArgs; - int i; - ListCell *lc; + int hargpos; numDirectArgs = list_length(fargs) - numAggregatedArgs; numNonHypotheticalArgs = numDirectArgs - numAggregatedArgs; @@ -1804,25 +1798,20 @@ unify_hypothetical_args(ParseState *pstate, if (numNonHypotheticalArgs < 0) elog(ERROR, "incorrect number of arguments to hypothetical-set aggregate"); - /* Deconstruct fargs into an array for ease of subscripting */ - i = 0; - foreach(lc, fargs) - { - args[i++] = (Node *) lfirst(lc); - } - /* Check each hypothetical arg and corresponding aggregated arg */ - for (i = numNonHypotheticalArgs; i < numDirectArgs; i++) + for (hargpos = numNonHypotheticalArgs; hargpos < numDirectArgs; hargpos++) { - int aargpos = numDirectArgs + (i - numNonHypotheticalArgs); + int aargpos = numDirectArgs + (hargpos - numNonHypotheticalArgs); + ListCell *harg = list_nth_cell(fargs, hargpos); + ListCell *aarg = list_nth_cell(fargs, aargpos); Oid commontype; /* A mismatch means AggregateCreate didn't check properly ... */ - if (declared_arg_types[i] != declared_arg_types[aargpos]) + if (declared_arg_types[hargpos] != declared_arg_types[aargpos]) elog(ERROR, "hypothetical-set aggregate has inconsistent declared argument types"); /* No need to unify if make_fn_arguments will coerce */ - if (declared_arg_types[i] != ANYOID) + if (declared_arg_types[hargpos] != ANYOID) continue; /* @@ -1831,7 +1820,7 @@ unify_hypothetical_args(ParseState *pstate, * the aggregated values). */ commontype = select_common_type(pstate, - list_make2(args[aargpos], args[i]), + list_make2(lfirst(aarg), lfirst(harg)), "WITHIN GROUP", NULL); @@ -1839,30 +1828,23 @@ unify_hypothetical_args(ParseState *pstate, * Perform the coercions. We don't need to worry about NamedArgExprs * here because they aren't supported with aggregates. */ - args[i] = coerce_type(pstate, - args[i], - actual_arg_types[i], - commontype, -1, - COERCION_IMPLICIT, - COERCE_IMPLICIT_CAST, - -1); - actual_arg_types[i] = commontype; - args[aargpos] = coerce_type(pstate, - args[aargpos], - actual_arg_types[aargpos], - commontype, -1, - COERCION_IMPLICIT, - COERCE_IMPLICIT_CAST, - -1); + lfirst(harg) = coerce_type(pstate, + (Node *) lfirst(harg), + actual_arg_types[hargpos], + commontype, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); + actual_arg_types[hargpos] = commontype; + lfirst(aarg) = coerce_type(pstate, + (Node *) lfirst(aarg), + actual_arg_types[aargpos], + commontype, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); actual_arg_types[aargpos] = commontype; } - - /* Reconstruct fargs from array */ - i = 0; - foreach(lc, fargs) - { - lfirst(lc) = args[i++]; - } } @@ -2063,7 +2045,7 @@ funcname_signature_string(const char *funcname, int nargs, if (i >= numposargs) { appendStringInfo(&argbuf, "%s => ", (char *) lfirst(lc)); - lc = lnext(lc); + lc = lnext(argnames, lc); } appendStringInfoString(&argbuf, format_type_be(argtypes[i])); } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index b13cd385eb54..4317a66af17c 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1070,6 +1070,7 @@ static void buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref) { int maxattrs = tupdesc->natts; + List *aliaslist; ListCell *aliaslc; int numaliases; int varattno; @@ -1079,13 +1080,15 @@ buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref) if (alias) { - aliaslc = list_head(alias->colnames); - numaliases = list_length(alias->colnames); + aliaslist = alias->colnames; + aliaslc = list_head(aliaslist); + numaliases = list_length(aliaslist); /* We'll rebuild the alias colname list */ alias->colnames = NIL; } else { + aliaslist = NIL; aliaslc = NULL; numaliases = 0; } @@ -1107,7 +1110,7 @@ buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref) { /* Use the next user-supplied alias */ attrname = (Value *) lfirst(aliaslc); - aliaslc = lnext(aliaslc); + aliaslc = lnext(aliaslist, aliaslc); alias->colnames = lappend(alias->colnames, attrname); } else @@ -2581,7 +2584,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, *colvars = lappend(*colvars, varnode); } - aliasp_item = lnext(aliasp_item); + aliasp_item = lnext(rte->eref->colnames, aliasp_item); } } break; @@ -2809,7 +2812,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, *colnames = lappend(*colnames, makeString(pstrdup(""))); - aliasp_item = lnext(aliasp_item); + aliasp_item = lnext(rte->eref->colnames, aliasp_item); } if (colvars) @@ -2881,19 +2884,11 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset, int location, bool include_dropped, List **colnames, List **colvars) { - ListCell *aliascell = list_head(eref->colnames); + ListCell *aliascell; int varattno; - if (colnames) - { - int i; - - for (i = 0; i < offset; i++) - { - if (aliascell) - aliascell = lnext(aliascell); - } - } + aliascell = (offset < list_length(eref->colnames)) ? + list_nth_cell(eref->colnames, offset) : NULL; Assert(count <= tupdesc->natts); for (varattno = 0; varattno < count; varattno++) @@ -2917,7 +2912,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset, } } if (aliascell) - aliascell = lnext(aliascell); + aliascell = lnext(eref->colnames, aliascell); continue; } @@ -2928,7 +2923,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset, if (aliascell) { label = strVal(lfirst(aliascell)); - aliascell = lnext(aliascell); + aliascell = lnext(eref->colnames, aliascell); } else { diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index cf4044a4a540..64a19acbee49 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -42,7 +42,8 @@ static Node *transformAssignmentIndirection(ParseState *pstate, Oid targetTypeId, int32 targetTypMod, Oid targetCollation, - ListCell *indirection, + List *indirection, + ListCell *indirection_cell, Node *rhs, int location); static Node *transformAssignmentSubscripts(ParseState *pstate, @@ -53,6 +54,7 @@ static Node *transformAssignmentSubscripts(ParseState *pstate, Oid targetCollation, List *subscripts, bool isSlice, + List *indirection, ListCell *next_indirection, Node *rhs, int location); @@ -563,6 +565,7 @@ transformAssignedExpr(ParseState *pstate, attrtype, attrtypmod, attrcollation, + indirection, list_head(indirection), (Node *) expr, location); @@ -664,8 +667,9 @@ updateTargetListEntry(ParseState *pstate, * collation of the object to be assigned to (initially the target column, * later some subobject). * - * indirection is the sublist remaining to process. When it's NULL, we're - * done recursing and can just coerce and return the RHS. + * indirection is the list of indirection nodes, and indirection_cell is the + * start of the sublist remaining to process. When it's NULL, we're done + * recursing and can just coerce and return the RHS. * * rhs is the already-transformed value to be assigned; note it has not been * coerced to any particular type. @@ -683,7 +687,8 @@ transformAssignmentIndirection(ParseState *pstate, Oid targetTypeId, int32 targetTypMod, Oid targetCollation, - ListCell *indirection, + List *indirection, + ListCell *indirection_cell, Node *rhs, int location) { @@ -692,7 +697,7 @@ transformAssignmentIndirection(ParseState *pstate, bool isSlice = false; ListCell *i; - if (indirection && !basenode) + if (indirection_cell && !basenode) { /* * Set up a substitution. We abuse CaseTestExpr for this. It's safe @@ -714,7 +719,7 @@ transformAssignmentIndirection(ParseState *pstate, * subscripting. Adjacent A_Indices nodes have to be treated as a single * multidimensional subscript operation. */ - for_each_cell(i, indirection) + for_each_cell(i, indirection, indirection_cell) { Node *n = lfirst(i); @@ -756,6 +761,7 @@ transformAssignmentIndirection(ParseState *pstate, targetCollation, subscripts, isSlice, + indirection, i, rhs, location); @@ -805,7 +811,8 @@ transformAssignmentIndirection(ParseState *pstate, fieldTypeId, fieldTypMod, fieldCollation, - lnext(i), + indirection, + lnext(indirection, i), rhs, location); @@ -842,6 +849,7 @@ transformAssignmentIndirection(ParseState *pstate, targetCollation, subscripts, isSlice, + indirection, NULL, rhs, location); @@ -894,6 +902,7 @@ transformAssignmentSubscripts(ParseState *pstate, Oid targetCollation, List *subscripts, bool isSlice, + List *indirection, ListCell *next_indirection, Node *rhs, int location) @@ -933,6 +942,7 @@ transformAssignmentSubscripts(ParseState *pstate, typeNeeded, containerTypMod, collationNeeded, + indirection, next_indirection, rhs, location); diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index b85a0c0aa463..033c35b11bc0 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1192,7 +1192,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla * find sequence owned by old column; extract sequence parameters; * build new create sequence command */ - seq_relid = getOwnedSequence(RelationGetRelid(relation), attribute->attnum); + seq_relid = getIdentitySequence(RelationGetRelid(relation), attribute->attnum, false); seq_options = sequence_options(seq_relid); generateSerialExtraStmts(cxt, def, InvalidOid, seq_options, true, @@ -1710,7 +1710,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, if (indexpr_item == NULL) elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(indexprs, indexpr_item); /* Adjust Vars to match new table's column numbering */ indexkey = map_variable_attnos(indexkey, @@ -4306,7 +4306,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, if (attnum != InvalidAttrNumber && TupleDescAttr(tupdesc, attnum - 1)->attidentity) { - Oid seq_relid = getOwnedSequence(relid, attnum); + Oid seq_relid = getIdentitySequence(relid, attnum, false); Oid typeOid = typenameTypeId(pstate, def->typeName); AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt); @@ -4357,7 +4357,6 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, ListCell *lc; List *newseqopts = NIL; List *newdef = NIL; - List *seqlist; AttrNumber attnum; /* @@ -4378,14 +4377,13 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, if (attnum) { - seqlist = getOwnedSequences(relid, attnum); - if (seqlist) + Oid seq_relid = getIdentitySequence(relid, attnum, true); + + if (seq_relid) { AlterSeqStmt *seqstmt; - Oid seq_relid; seqstmt = makeNode(AlterSeqStmt); - seq_relid = linitial_oid(seqlist); seqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)), get_rel_name(seq_relid), -1); seqstmt->options = newseqopts; diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index fbe2ed17bee2..67d6be043abc 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -2047,7 +2047,7 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec) else { keyCol = (Node *) copyObject(lfirst(partexprs_item)); - partexprs_item = lnext(partexprs_item); + partexprs_item = lnext(key->partexprs, partexprs_item); } args = lappend(args, keyCol); @@ -2492,19 +2492,20 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec, j = i; partexprs_item = partexprs_item_saved; - for_both_cell(cell1, lower_or_start_datum, cell2, upper_or_start_datum) + for_both_cell(cell1, spec->lowerdatums, lower_or_start_datum, + cell2, spec->upperdatums, upper_or_start_datum) { PartitionRangeDatum *ldatum_next = NULL, *udatum_next = NULL; ldatum = castNode(PartitionRangeDatum, lfirst(cell1)); - if (lnext(cell1)) + if (lnext(spec->lowerdatums, cell1)) ldatum_next = castNode(PartitionRangeDatum, - lfirst(lnext(cell1))); + lfirst(lnext(spec->lowerdatums, cell1))); udatum = castNode(PartitionRangeDatum, lfirst(cell2)); - if (lnext(cell2)) + if (lnext(spec->upperdatums, cell2)) udatum_next = castNode(PartitionRangeDatum, - lfirst(lnext(cell2))); + lfirst(lnext(spec->upperdatums, cell2))); get_range_key_properties(key, j, ldatum, udatum, &partexprs_item, &keyCol, @@ -2669,7 +2670,7 @@ get_range_key_properties(PartitionKey key, int keynum, if (*partexprs_item == NULL) elog(ERROR, "wrong number of partition key expressions"); *keyCol = copyObject(lfirst(*partexprs_item)); - *partexprs_item = lnext(*partexprs_item); + *partexprs_item = lnext(key->partexprs, *partexprs_item); } /* Get appropriate Const nodes for the bounds */ @@ -2717,7 +2718,7 @@ get_range_nulltest(PartitionKey key) if (partexprs_item == NULL) elog(ERROR, "wrong number of partition key expressions"); keyCol = copyObject(lfirst(partexprs_item)); - partexprs_item = lnext(partexprs_item); + partexprs_item = lnext(key->partexprs, partexprs_item); } nulltest = makeNode(NullTest); @@ -2835,7 +2836,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) PartitionKey key; int j; - /* Open parent relation and fetch partition keyinfo */ + /* Open parent relation and fetch partition key info */ parent = try_relation_open(parentId, AccessShareLock, false); if (parent == NULL) PG_RETURN_NULL(); diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 377db8ca32b4..d4336735eed5 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -179,6 +179,7 @@ static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context Oid step_lastcmpfn, int step_lastkeyno, Bitmapset *step_nullkeys, + List *prefix, ListCell *start, List *step_exprs, List *step_cmpfns); @@ -199,8 +200,10 @@ static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context, PartitionPruneStepCombine *cstep, PruneStepResult **step_results); -static bool match_boolean_partition_clause(Oid partopfamily, Expr *clause, - Expr *partkey, Expr **outconst); +static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily, + Expr *clause, + Expr *partkey, + Expr **outconst); static void partkey_datum_from_expr(PartitionPruneContext *context, Expr *expr, int stateidx, Datum *value, bool *isnull); @@ -1546,7 +1549,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, * of expressions of different keys, which * get_steps_using_prefix will take care of for us. */ - for_each_cell(lc1, lc) + for_each_cell(lc1, eq_clauses, lc) { pc = lfirst(lc1); @@ -1650,6 +1653,7 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context, bool *clause_is_not_null, PartClauseInfo **pc, List **clause_steps) { + PartClauseMatchStatus boolmatchstatus; PartitionScheme part_scheme = context->rel->part_scheme; Oid partopfamily = part_scheme->partopfamily[partkeyidx], partcoll = part_scheme->partcollation[partkeyidx]; @@ -1658,7 +1662,10 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context, /* * Recognize specially shaped clauses that match a Boolean partition key. */ - if (match_boolean_partition_clause(partopfamily, clause, partkey, &expr)) + boolmatchstatus = match_boolean_partition_clause(partopfamily, clause, + partkey, &expr); + + if (boolmatchstatus == PARTCLAUSE_MATCH_CLAUSE) { PartClauseInfo *partclause; @@ -2178,7 +2185,21 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context, return PARTCLAUSE_MATCH_NULLNESS; } - return PARTCLAUSE_UNSUPPORTED; + /* + * If we get here then the return value depends on the result of the + * match_boolean_partition_clause call above. If the call returned + * PARTCLAUSE_UNSUPPORTED then we're either not dealing with a bool qual + * or the bool qual is not suitable for pruning. Since the qual didn't + * match up to any of the other qual types supported here, then trying to + * match it against any other partition key is a waste of time, so just + * return PARTCLAUSE_UNSUPPORTED. If the qual just couldn't be matched to + * this partition key, then it may match another, so return + * PARTCLAUSE_NOMATCH. The only other value that + * match_boolean_partition_clause can return is PARTCLAUSE_MATCH_CLAUSE, + * and since that value was already dealt with above, then we can just + * return boolmatchstatus. + */ + return boolmatchstatus; } /* @@ -2224,6 +2245,7 @@ get_steps_using_prefix(GeneratePruningStepsContext *context, step_lastcmpfn, step_lastkeyno, step_nullkeys, + prefix, list_head(prefix), NIL, NIL); } @@ -2235,6 +2257,7 @@ get_steps_using_prefix(GeneratePruningStepsContext *context, * column that is less than the one for which we're currently generating * steps (that is, step_lastkeyno) * + * 'prefix' is the list of PartClauseInfos. * 'start' is where we should start iterating for the current invocation. * 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns * we've generated so far from the clauses for the previous part keys. @@ -2247,6 +2270,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, Oid step_lastcmpfn, int step_lastkeyno, Bitmapset *step_nullkeys, + List *prefix, ListCell *start, List *step_exprs, List *step_cmpfns) @@ -2272,7 +2296,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, * next_start to the ListCell of the first clause for the next * partition key. */ - for_each_cell(lc, start) + for_each_cell(lc, prefix, start) { pc = lfirst(lc); @@ -2281,7 +2305,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, } next_start = lc; - for_each_cell(lc, start) + for_each_cell(lc, prefix, start) { List *moresteps; @@ -2315,6 +2339,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, step_lastcmpfn, step_lastkeyno, step_nullkeys, + prefix, next_start, step_exprs, step_cmpfns); @@ -2329,7 +2354,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, * till the end of the list. */ Assert(list_length(step_exprs) == cur_keyno); - for_each_cell(lc, start) + for_each_cell(lc, prefix, start) { PartClauseInfo *pc = lfirst(lc); PartitionPruneStep *step; @@ -3274,8 +3299,8 @@ perform_pruning_base_step(PartitionPruneContext *context, values[keyno] = datum; nvalues++; - lc1 = lnext(lc1); - lc2 = lnext(lc2); + lc1 = lnext(opstep->exprs, lc1); + lc2 = lnext(opstep->cmpfns, lc2); } } @@ -3426,11 +3451,15 @@ perform_pruning_combine_step(PartitionPruneContext *context, /* * match_boolean_partition_clause * - * Sets *outconst to a Const containing true or false value and returns true if - * we're able to match the clause to the partition key as specially-shaped - * Boolean clause. Returns false otherwise with *outconst set to NULL. + * If we're able to match the clause to the partition key as specially-shaped + * boolean clause, set *outconst to a Const containing a true or false value + * and return PARTCLAUSE_MATCH_CLAUSE. Returns PARTCLAUSE_UNSUPPORTED if the + * clause is not a boolean clause or if the boolean clause is unsuitable for + * partition pruning. Returns PARTCLAUSE_NOMATCH if it's a bool quals but + * just does not match this partition key. *outconst is set to NULL in the + * latter two cases. */ -static bool +static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey, Expr **outconst) { @@ -3439,7 +3468,7 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey, *outconst = NULL; if (!IsBooleanOpfamily(partopfamily)) - return false; + return PARTCLAUSE_UNSUPPORTED; if (IsA(clause, BooleanTest)) { @@ -3448,7 +3477,7 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey, /* Only IS [NOT] TRUE/FALSE are any good to us */ if (btest->booltesttype == IS_UNKNOWN || btest->booltesttype == IS_NOT_UNKNOWN) - return false; + return PARTCLAUSE_UNSUPPORTED; leftop = btest->arg; if (IsA(leftop, RelabelType)) @@ -3461,7 +3490,7 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey, : (Expr *) makeBoolConst(false, false); if (*outconst) - return true; + return PARTCLAUSE_MATCH_CLAUSE; } else { @@ -3481,10 +3510,10 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey, *outconst = (Expr *) makeBoolConst(false, false); if (*outconst) - return true; + return PARTCLAUSE_MATCH_CLAUSE; } - return false; + return PARTCLAUSE_NOMATCH; } /* diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c index f08fbd16908e..013c122451d2 100644 --- a/src/backend/port/win32_sema.c +++ b/src/backend/port/win32_sema.c @@ -127,7 +127,6 @@ PGSemaphoreReset(PGSemaphore sema) * PGSemaphoreLock * * Lock a semaphore (decrement count), blocking if count would be < 0. - * Serve the interrupt if interruptOK is true. */ void PGSemaphoreLock(PGSemaphore sema) diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 433b7f536d6e..d5fd65dceaab 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -1134,7 +1134,7 @@ rebuild_database_list(Oid newdb) current_time = GetCurrentTimestamp(); /* - * move the elements from the array into the dllist, setting the + * move the elements from the array into the dlist, setting the * next_worker while walking the array */ for (i = 0; i < nelems; i++) diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index 8391660ed3a7..5d78e9324506 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -294,10 +294,10 @@ BackgroundWriterMain(void) * significantly bigger than BgWriterDelay, so we don't complicate the * overall timeout handling but just assume we're going to get called * often enough even if hibernation mode is active. It's not that - * important that log_snap_interval_ms is met strictly. To make sure - * we're not waking the disk up unnecessarily on an idle system we - * check whether there has been any WAL inserted since the last time - * we've logged a running xacts. + * important that LOG_SNAPSHOT_INTERVAL_MS is met strictly. To make + * sure we're not waking the disk up unnecessarily on an idle system + * we check whether there has been any WAL inserted since the last + * time we've logged a running xacts. * * We do this logging in the bgwriter as it is the only process that * is run regularly and returns to its mainloop all the time. E.g. diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 34081c7da118..634876205128 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -2993,7 +2993,7 @@ ClosePostmasterPorts(bool am_syslogger) * do this as early as possible, so that if postmaster dies, others won't * think that it's still running because we're holding the pipe open. */ - if (close(postmaster_alive_fds[POSTMASTER_FD_OWN])) + if (close(postmaster_alive_fds[POSTMASTER_FD_OWN]) != 0) ereport(FATAL, (errcode_for_file_access(), errmsg_internal("could not close postmaster death monitoring pipe in child process: %m"))); diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index c58da271931b..1eebbfede723 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -90,7 +90,7 @@ static void base_backup_cleanup(int code, Datum arg); static void perform_base_backup(basebackup_options *opt); static void parse_basebackup_options(List *options, basebackup_options *opt); static void SendXlogRecPtrResult(XLogRecPtr ptr, TimeLineID tli); -static int compareWalFileNames(const void *a, const void *b); +static int compareWalFileNames(const ListCell *a, const ListCell *b); static void throttle(size_t increment); static bool is_checksummed_file(const char *fullpath, const char *filename); @@ -126,7 +126,7 @@ static TimestampTz throttled_last; static XLogRecPtr startptr; /* Total number of checksum failures during base backup. */ -static int64 total_checksum_failures; +static long long int total_checksum_failures; /* Do not verify checksums. */ static bool noverify_checksums = false; @@ -397,7 +397,7 @@ perform_base_backup(basebackup_options *opt) */ if (opt->includewal && ti->path == NULL) { - Assert(lnext(lc) == NULL); + Assert(lnext(tablespaces, lc) == NULL); } else pq_putemptymessage('c'); /* CopyDone */ @@ -421,13 +421,10 @@ perform_base_backup(basebackup_options *opt) struct stat statbuf; List *historyFileList = NIL; List *walFileList = NIL; - char **walFiles; - int nWalFiles; char firstoff[MAXFNAMELEN]; char lastoff[MAXFNAMELEN]; DIR *dir; struct dirent *de; - int i; ListCell *lc; TimeLineID tli; @@ -470,24 +467,17 @@ perform_base_backup(basebackup_options *opt) CheckXLogRemoved(startsegno, ThisTimeLineID); /* - * Put the WAL filenames into an array, and sort. We send the files in - * order from oldest to newest, to reduce the chance that a file is - * recycled before we get a chance to send it over. + * Sort the WAL filenames. We want to send the files in order from + * oldest to newest, to reduce the chance that a file is recycled + * before we get a chance to send it over. */ - nWalFiles = list_length(walFileList); - walFiles = palloc(nWalFiles * sizeof(char *)); - i = 0; - foreach(lc, walFileList) - { - walFiles[i++] = lfirst(lc); - } - qsort(walFiles, nWalFiles, sizeof(char *), compareWalFileNames); + list_sort(walFileList, compareWalFileNames); /* * There must be at least one xlog file in the pg_wal directory, since * we are doing backup-including-xlog. */ - if (nWalFiles < 1) + if (walFileList == NIL) ereport(ERROR, (errmsg("could not find any WAL files"))); @@ -495,7 +485,8 @@ perform_base_backup(basebackup_options *opt) * Sanity check: the first and last segment should cover startptr and * endptr, with no gaps in between. */ - XLogFromFileName(walFiles[0], &tli, &segno, wal_segment_size); + XLogFromFileName((char *) linitial(walFileList), + &tli, &segno, wal_segment_size); if (segno != startsegno) { char startfname[MAXFNAMELEN]; @@ -505,12 +496,13 @@ perform_base_backup(basebackup_options *opt) ereport(ERROR, (errmsg("could not find WAL file \"%s\"", startfname))); } - for (i = 0; i < nWalFiles; i++) + foreach(lc, walFileList) { + char *walFileName = (char *) lfirst(lc); XLogSegNo currsegno = segno; XLogSegNo nextsegno = segno + 1; - XLogFromFileName(walFiles[i], &tli, &segno, wal_segment_size); + XLogFromFileName(walFileName, &tli, &segno, wal_segment_size); if (!(nextsegno == segno || currsegno == segno)) { char nextfname[MAXFNAMELEN]; @@ -531,15 +523,16 @@ perform_base_backup(basebackup_options *opt) } /* Ok, we have everything we need. Send the WAL files. */ - for (i = 0; i < nWalFiles; i++) + foreach(lc, walFileList) { + char *walFileName = (char *) lfirst(lc); FILE *fp; char buf[TAR_SEND_SIZE]; size_t cnt; pgoff_t len = 0; - snprintf(pathbuf, MAXPGPATH, XLOGDIR "/%s", walFiles[i]); - XLogFromFileName(walFiles[i], &tli, &segno, wal_segment_size); + snprintf(pathbuf, MAXPGPATH, XLOGDIR "/%s", walFileName); + XLogFromFileName(walFileName, &tli, &segno, wal_segment_size); fp = AllocateFile(pathbuf, "rb"); if (fp == NULL) @@ -569,7 +562,7 @@ perform_base_backup(basebackup_options *opt) CheckXLogRemoved(segno, tli); ereport(ERROR, (errcode_for_file_access(), - errmsg("unexpected WAL file size \"%s\"", walFiles[i]))); + errmsg("unexpected WAL file size \"%s\"", walFileName))); } /* send the WAL file itself */ @@ -597,7 +590,7 @@ perform_base_backup(basebackup_options *opt) CheckXLogRemoved(segno, tli); ereport(ERROR, (errcode_for_file_access(), - errmsg("unexpected WAL file size \"%s\"", walFiles[i]))); + errmsg("unexpected WAL file size \"%s\"", walFileName))); } elogif(debug_basebackup, LOG, @@ -613,7 +606,7 @@ perform_base_backup(basebackup_options *opt) * walreceiver.c always doing an XLogArchiveForceDone() after a * complete segment. */ - StatusFilePath(pathbuf, walFiles[i], ".done"); + StatusFilePath(pathbuf, walFileName, ".done"); sendFileWithContent(pathbuf, ""); } @@ -652,14 +645,9 @@ perform_base_backup(basebackup_options *opt) if (total_checksum_failures) { if (total_checksum_failures > 1) - { - char buf[64]; - - snprintf(buf, sizeof(buf), INT64_FORMAT, total_checksum_failures); - ereport(WARNING, - (errmsg("%s total checksum verification failures", buf))); - } + (errmsg("%lld total checksum verification failures", total_checksum_failures))); + ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("checksum verification failure during base backup"))); @@ -668,14 +656,14 @@ perform_base_backup(basebackup_options *opt) } /* - * qsort comparison function, to compare log/seg portion of WAL segment + * list_sort comparison function, to compare log/seg portion of WAL segment * filenames, ignoring the timeline portion. */ static int -compareWalFileNames(const void *a, const void *b) +compareWalFileNames(const ListCell *a, const ListCell *b) { - char *fna = *((char **) a); - char *fnb = *((char **) b); + char *fna = (char *) lfirst(a); + char *fnb = (char *) lfirst(b); return strcmp(fna + 8, fnb + 8); } @@ -1252,7 +1240,7 @@ sendDir(const char *path, int basepathlen, bool sizeonly, List *tablespaces, * error in that case. The error handler further up will call * do_pg_abort_backup() for us. Also check that if the backup was * started while still in recovery, the server wasn't promoted. - * dp_pg_stop_backup() will check that too, but it's better to stop + * do_pg_stop_backup() will check that too, but it's better to stop * the backup early than continue to the end and fail there. */ CHECK_FOR_INTERRUPTS(); diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c index 681132c922b2..7477ceae283f 100644 --- a/src/backend/replication/logical/origin.c +++ b/src/backend/replication/logical/origin.c @@ -650,7 +650,7 @@ CheckPointReplicationOrigin(void) tmppath))); } - if (CloseTransientFile(tmpfd)) + if (CloseTransientFile(tmpfd) != 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", @@ -789,7 +789,7 @@ StartupReplicationOrigin(void) errmsg("replication slot checkpoint has wrong checksum %u, expected %u", crc, file_crc))); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index e7c32f2a1323..e8ffa0492f1d 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -3236,7 +3236,7 @@ ReorderBufferToastReset(ReorderBuffer *rb, ReorderBufferTXN *txn) * ------------------------------------------------------------------------- */ -/* struct for qsort()ing mapping files by lsn somewhat efficiently */ +/* struct for sorting mapping files by LSN efficiently */ typedef struct RewriteMappingFile { XLogRecPtr lsn; @@ -3360,7 +3360,7 @@ ApplyLogicalMappingFile(HTAB *tuplecid_data, Oid relid, const char *fname) } } - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); @@ -3378,13 +3378,13 @@ TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num) } /* - * qsort() comparator for sorting RewriteMappingFiles in LSN order. + * list_sort() comparator for sorting RewriteMappingFiles in LSN order. */ static int -file_sort_by_lsn(const void *a_p, const void *b_p) +file_sort_by_lsn(const ListCell *a_p, const ListCell *b_p) { - RewriteMappingFile *a = *(RewriteMappingFile **) a_p; - RewriteMappingFile *b = *(RewriteMappingFile **) b_p; + RewriteMappingFile *a = (RewriteMappingFile *) lfirst(a_p); + RewriteMappingFile *b = (RewriteMappingFile *) lfirst(b_p); if (a->lsn < b->lsn) return -1; @@ -3404,8 +3404,6 @@ UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot) struct dirent *mapping_de; List *files = NIL; ListCell *file; - RewriteMappingFile **files_a; - size_t off; Oid dboid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId; mapping_dir = AllocateDir("pg_logical/mappings"); @@ -3459,21 +3457,12 @@ UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot) } FreeDir(mapping_dir); - /* build array we can easily sort */ - files_a = palloc(list_length(files) * sizeof(RewriteMappingFile *)); - off = 0; - foreach(file, files) - { - files_a[off++] = lfirst(file); - } - /* sort files so we apply them in LSN order */ - qsort(files_a, list_length(files), sizeof(RewriteMappingFile *), - file_sort_by_lsn); + list_sort(files, file_sort_by_lsn); - for (off = 0; off < list_length(files); off++) + foreach(file, files) { - RewriteMappingFile *f = files_a[off]; + RewriteMappingFile *f = (RewriteMappingFile *) lfirst(file); elog(DEBUG1, "applying mapping: \"%s\" in %u", f->fname, snapshot->subxip[0]); diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c index 21666d8777f6..8aa3768be8fa 100644 --- a/src/backend/replication/logical/snapbuild.c +++ b/src/backend/replication/logical/snapbuild.c @@ -1651,7 +1651,7 @@ SnapBuildSerialize(SnapBuild *builder, XLogRecPtr lsn) } pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tmppath))); @@ -1849,7 +1849,7 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn) } COMP_CRC32C(checksum, ondisk.builder.committed.xip, sz); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index b5e4c0481fd1..82948b4bf3e9 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -1331,7 +1331,7 @@ SaveSlotToPath(ReplicationSlot *slot, const char *dir, int elevel) } pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) { ereport(elevel, (errcode_for_file_access(), @@ -1488,7 +1488,7 @@ RestoreSlotFromDisk(const char *name) path, readBytes, (Size) cp.length))); } - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c index 9fdc96ef4aff..252928f79b82 100644 --- a/src/backend/replication/syncrep.c +++ b/src/backend/replication/syncrep.c @@ -1027,18 +1027,14 @@ SyncRepGetSyncStandbysPriority(bool *am_sync) while (priority <= lowest_priority) { ListCell *cell; - ListCell *prev = NULL; - ListCell *next; next_highest_priority = lowest_priority + 1; - for (cell = list_head(pending); cell != NULL; cell = next) + foreach(cell, pending) { i = lfirst_int(cell); walsnd = &WalSndCtl->walsnds[i]; - next = lnext(cell); - this_priority = walsnd->sync_standby_priority; if (this_priority == priority) { @@ -1061,15 +1057,13 @@ SyncRepGetSyncStandbysPriority(bool *am_sync) * Remove the entry for this sync standby from the list to * prevent us from looking at the same entry again. */ - pending = list_delete_cell(pending, cell, prev); + pending = foreach_delete_current(pending, cell); - continue; + continue; /* don't adjust next_highest_priority */ } if (this_priority < next_highest_priority) next_highest_priority = this_priority; - - prev = cell; } priority = next_highest_priority; diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index ecead8c75391..98170bc97fc7 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -545,7 +545,7 @@ SendTimeLineHistory(TimeLineHistoryCmd *cmd) bytesleft -= nread; } - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", path))); @@ -2314,7 +2314,7 @@ WalSndLoop(WalSndSendDataCallback send_data) WL_SOCKET_READABLE; /* - * Use fresh timestamp, not last_processed, to reduce the chance + * Use fresh timestamp, not last_processing, to reduce the chance * of reaching wal_sender_timeout before sending a keepalive. */ sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp()); @@ -2753,7 +2753,7 @@ XLogSendPhysical(void) * very close to together here so that we'll get a later position if it is * still moving. * - * Because LagTrackerWriter ignores samples when the LSN hasn't advanced, + * Because LagTrackerWrite ignores samples when the LSN hasn't advanced, * this gives us a cheap approximation for the WAL flush time for this * LSN. * diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 13fc2895d171..4e97d5e5b95d 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1186,7 +1186,7 @@ build_column_default(Relation rel, int attrno) { NextValueExpr *nve = makeNode(NextValueExpr); - nve->seqid = getOwnedSequence(RelationGetRelid(rel), attrno); + nve->seqid = getIdentitySequence(RelationGetRelid(rel), attrno, false); nve->typeId = att_tup->atttypid; return (Node *) nve; @@ -2085,7 +2085,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("infinite recursion detected in rules for relation \"%s\"", RelationGetRelationName(rel)))); - activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs); + activeRIRs = lappend_oid(activeRIRs, RelationGetRelid(rel)); foreach(l, locks) { @@ -2098,7 +2098,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) activeRIRs); } - activeRIRs = list_delete_first(activeRIRs); + activeRIRs = list_delete_last(activeRIRs); } } @@ -2171,7 +2171,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) errmsg("infinite recursion detected in policy for relation \"%s\"", RelationGetRelationName(rel)))); - activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs); + activeRIRs = lappend_oid(activeRIRs, RelationGetRelid(rel)); /* * get_row_security_policies just passed back securityQuals @@ -2196,7 +2196,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) expression_tree_walker((Node *) withCheckOptions, fireRIRonSubLink, (void *) activeRIRs); - activeRIRs = list_delete_first(activeRIRs); + activeRIRs = list_delete_last(activeRIRs); } /* @@ -3824,7 +3824,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) rev = (rewrite_event *) palloc(sizeof(rewrite_event)); rev->relation = RelationGetRelid(rt_entry_relation); rev->event = event; - rewrite_events = lcons(rev, rewrite_events); + rewrite_events = lappend(rewrite_events, rev); foreach(n, product_queries) { @@ -3835,7 +3835,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) rewritten = list_concat(rewritten, newstuff); } - rewrite_events = list_delete_first(rewrite_events); + rewrite_events = list_delete_last(rewrite_events); } /* diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index b70594d6ce31..f3f17e7146be 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1257,7 +1257,7 @@ replace_rte_variables_mutator(Node *node, * a ConvertRowtypeExpr to map back to the rowtype expected by the expression. * (Therefore, to_rowtype had better be a child rowtype of the rowtype of the * RTE we're changing references to.) Callers that don't provide to_rowtype - * should report an error if *found_row_type is true; we don't do that here + * should report an error if *found_whole_row is true; we don't do that here * because we don't know exactly what wording for the error message would * be most appropriate. The caller will be aware of the context. * diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 300af6f06f2c..1c44714589b5 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -63,9 +63,9 @@ static void get_policies_for_relation(Relation relation, List **permissive_policies, List **restrictive_policies); -static List *sort_policies_by_name(List *policies); +static void sort_policies_by_name(List *policies); -static int row_security_policy_cmp(const void *a, const void *b); +static int row_security_policy_cmp(const ListCell *a, const ListCell *b); static void add_security_quals(int rt_index, List *permissive_policies, @@ -470,7 +470,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id, * We sort restrictive policies by name so that any WCOs they generate are * checked in a well-defined order. */ - *restrictive_policies = sort_policies_by_name(*restrictive_policies); + sort_policies_by_name(*restrictive_policies); /* * Then add any permissive or restrictive policies defined by extensions. @@ -488,7 +488,7 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id, * always check all built-in restrictive policies, in name order, * before checking restrictive policies added by hooks, in name order. */ - hook_policies = sort_policies_by_name(hook_policies); + sort_policies_by_name(hook_policies); foreach(item, hook_policies) { @@ -522,43 +522,20 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id, * This is not necessary for permissive policies, since they are all combined * together using OR into a single WithCheckOption check. */ -static List * +static void sort_policies_by_name(List *policies) { - int npol = list_length(policies); - RowSecurityPolicy *pols; - ListCell *item; - int ii = 0; - - if (npol <= 1) - return policies; - - pols = (RowSecurityPolicy *) palloc(sizeof(RowSecurityPolicy) * npol); - - foreach(item, policies) - { - RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); - - pols[ii++] = *policy; - } - - qsort(pols, npol, sizeof(RowSecurityPolicy), row_security_policy_cmp); - - policies = NIL; - for (ii = 0; ii < npol; ii++) - policies = lappend(policies, &pols[ii]); - - return policies; + list_sort(policies, row_security_policy_cmp); } /* - * qsort comparator to sort RowSecurityPolicy entries by name + * list_sort comparator to sort RowSecurityPolicy entries by name */ static int -row_security_policy_cmp(const void *a, const void *b) +row_security_policy_cmp(const ListCell *a, const ListCell *b) { - const RowSecurityPolicy *pa = (const RowSecurityPolicy *) a; - const RowSecurityPolicy *pb = (const RowSecurityPolicy *) b; + const RowSecurityPolicy *pa = (const RowSecurityPolicy *) lfirst(a); + const RowSecurityPolicy *pb = (const RowSecurityPolicy *) lfirst(b); /* Guard against NULL policy names from extensions */ if (pa->policy_name == NULL) diff --git a/src/backend/snowball/Makefile b/src/backend/snowball/Makefile index 9063bd33264f..bf55b1923470 100644 --- a/src/backend/snowball/Makefile +++ b/src/backend/snowball/Makefile @@ -41,6 +41,7 @@ OBJS= $(WIN32RES) dict_snowball.o api.o utilities.o \ stem_UTF_8_finnish.o \ stem_UTF_8_french.o \ stem_UTF_8_german.o \ + stem_UTF_8_greek.o \ stem_UTF_8_hungarian.o \ stem_UTF_8_indonesian.o \ stem_UTF_8_irish.o \ @@ -63,24 +64,25 @@ OBJS= $(WIN32RES) dict_snowball.o api.o utilities.o \ # must come after creation of that language LANGUAGES= \ arabic arabic \ - danish danish \ - dutch dutch \ - english english \ - finnish finnish \ - french french \ - german german \ - hungarian hungarian \ + danish danish \ + dutch dutch \ + english english \ + finnish finnish \ + french french \ + german german \ + greek greek \ + hungarian hungarian \ indonesian indonesian \ irish irish \ - italian italian \ + italian italian \ lithuanian lithuanian \ nepali nepali \ - norwegian norwegian \ - portuguese portuguese \ - romanian romanian \ - russian english \ - spanish spanish \ - swedish swedish \ + norwegian norwegian \ + portuguese portuguese \ + romanian romanian \ + russian english \ + spanish spanish \ + swedish swedish \ tamil tamil \ turkish turkish diff --git a/src/backend/snowball/README b/src/backend/snowball/README index bada9d96cae5..3463289f1c4b 100644 --- a/src/backend/snowball/README +++ b/src/backend/snowball/README @@ -29,8 +29,8 @@ We choose to include the derived files in the PostgreSQL distribution because most installations will not have the Snowball compiler available. We are currently synced with the Snowball git commit -1964ce688cbeca505263c8f77e16ed923296ce7a -of 2018-06-29. +4456b82c26c02493e8807a66f30593a98c5d2888 +of 2019-06-24. To update the PostgreSQL sources from a new Snowball version: @@ -57,7 +57,7 @@ do not require any changes. 4. Check whether any stemmer modules have been added or removed. If so, edit the OBJS list in Makefile, the list of #include's in dict_snowball.c, and the stemmer_modules[] table in dict_snowball.c. You might also need to change -the LANGUAGES list in Makefile. +the LANGUAGES list in Makefile and tsearch_config_languages in initdb.c. 5. The various stopword files in stopwords/ must be downloaded individually from pages on the snowballstem.org website. diff --git a/src/backend/snowball/dict_snowball.c b/src/backend/snowball/dict_snowball.c index 516673831081..1d32b7d356d1 100644 --- a/src/backend/snowball/dict_snowball.c +++ b/src/backend/snowball/dict_snowball.c @@ -50,6 +50,7 @@ #include "snowball/libstemmer/stem_UTF_8_finnish.h" #include "snowball/libstemmer/stem_UTF_8_french.h" #include "snowball/libstemmer/stem_UTF_8_german.h" +#include "snowball/libstemmer/stem_UTF_8_greek.h" #include "snowball/libstemmer/stem_UTF_8_hungarian.h" #include "snowball/libstemmer/stem_UTF_8_indonesian.h" #include "snowball/libstemmer/stem_UTF_8_irish.h" @@ -115,6 +116,7 @@ static const stemmer_module stemmer_modules[] = STEMMER_MODULE(finnish, PG_UTF8, UTF_8), STEMMER_MODULE(french, PG_UTF8, UTF_8), STEMMER_MODULE(german, PG_UTF8, UTF_8), + STEMMER_MODULE(greek, PG_UTF8, UTF_8), STEMMER_MODULE(hungarian, PG_UTF8, UTF_8), STEMMER_MODULE(indonesian, PG_UTF8, UTF_8), STEMMER_MODULE(irish, PG_UTF8, UTF_8), diff --git a/src/backend/snowball/libstemmer/stem_ISO_8859_1_danish.c b/src/backend/snowball/libstemmer/stem_ISO_8859_1_danish.c index 263c226e3409..0ecbbb76a8f2 100644 --- a/src/backend/snowball/libstemmer/stem_ISO_8859_1_danish.c +++ b/src/backend/snowball/libstemmer/stem_ISO_8859_1_danish.c @@ -124,6 +124,8 @@ static const struct among a_2[5] = /* 4 */ { 4, s_2_4, -1, 2, 0} }; +static const unsigned char g_c[] = { 119, 223, 119, 1 }; + static const unsigned char g_v[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 128 }; static const unsigned char g_s_ending[] = { 239, 254, 42, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 }; @@ -133,25 +135,25 @@ static const symbol s_1[] = { 'i', 'g' }; static const symbol s_2[] = { 'l', 0xF8, 's' }; static int r_mark_regions(struct SN_env * z) { /* forwardmode */ - z->I[0] = z->l; /* $p1 = , line 31 */ - { int c_test1 = z->c; /* test, line 33 */ - { int ret = z->c + 3; /* hop, line 33 */ + z->I[0] = z->l; /* $p1 = , line 33 */ + { int c_test1 = z->c; /* test, line 35 */ + { int ret = z->c + 3; /* hop, line 35 */ if (0 > ret || ret > z->l) return 0; z->c = ret; } - z->I[1] = z->c; /* setmark x, line 33 */ + z->I[1] = z->c; /* setmark x, line 35 */ z->c = c_test1; } - if (out_grouping(z, g_v, 97, 248, 1) < 0) return 0; /* goto */ /* grouping v, line 34 */ - { /* gopast */ /* non v, line 34 */ + if (out_grouping(z, g_v, 97, 248, 1) < 0) return 0; /* goto */ /* grouping v, line 36 */ + { /* gopast */ /* non v, line 36 */ int ret = in_grouping(z, g_v, 97, 248, 1); if (ret < 0) return 0; z->c += ret; } - z->I[0] = z->c; /* setmark p1, line 34 */ - /* try, line 35 */ - if (!(z->I[0] < z->I[1])) goto lab0; /* $( < ), line 35 */ - z->I[0] = z->I[1]; /* $p1 = , line 35 */ + z->I[0] = z->c; /* setmark p1, line 36 */ + /* try, line 37 */ + if (!(z->I[0] < z->I[1])) goto lab0; /* $( < ), line 37 */ + z->I[0] = z->I[1]; /* $p1 = , line 37 */ lab0: return 1; } @@ -159,25 +161,25 @@ static int r_mark_regions(struct SN_env * z) { /* forwardmode */ static int r_main_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - { int mlimit1; /* setlimit, line 41 */ + { int mlimit1; /* setlimit, line 43 */ if (z->c < z->I[0]) return 0; mlimit1 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 41 */ - if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1851440 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit1; return 0; } /* substring, line 41 */ + z->ket = z->c; /* [, line 43 */ + if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1851440 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit1; return 0; } /* substring, line 43 */ among_var = find_among_b(z, a_0, 32); if (!(among_var)) { z->lb = mlimit1; return 0; } - z->bra = z->c; /* ], line 41 */ + z->bra = z->c; /* ], line 43 */ z->lb = mlimit1; } - switch (among_var) { /* among, line 42 */ + switch (among_var) { /* among, line 44 */ case 1: - { int ret = slice_del(z); /* delete, line 48 */ + { int ret = slice_del(z); /* delete, line 50 */ if (ret < 0) return ret; } break; case 2: - if (in_grouping_b(z, g_s_ending, 97, 229, 0)) return 0; /* grouping s_ending, line 50 */ - { int ret = slice_del(z); /* delete, line 50 */ + if (in_grouping_b(z, g_s_ending, 97, 229, 0)) return 0; /* grouping s_ending, line 52 */ + { int ret = slice_del(z); /* delete, line 52 */ if (ret < 0) return ret; } break; @@ -186,23 +188,23 @@ static int r_main_suffix(struct SN_env * z) { /* backwardmode */ } static int r_consonant_pair(struct SN_env * z) { /* backwardmode */ - { int m_test1 = z->l - z->c; /* test, line 55 */ + { int m_test1 = z->l - z->c; /* test, line 57 */ - { int mlimit2; /* setlimit, line 56 */ + { int mlimit2; /* setlimit, line 58 */ if (z->c < z->I[0]) return 0; mlimit2 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 56 */ - if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 100 && z->p[z->c - 1] != 116)) { z->lb = mlimit2; return 0; } /* substring, line 56 */ + z->ket = z->c; /* [, line 58 */ + if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 100 && z->p[z->c - 1] != 116)) { z->lb = mlimit2; return 0; } /* substring, line 58 */ if (!(find_among_b(z, a_1, 4))) { z->lb = mlimit2; return 0; } - z->bra = z->c; /* ], line 56 */ + z->bra = z->c; /* ], line 58 */ z->lb = mlimit2; } z->c = z->l - m_test1; } if (z->c <= z->lb) return 0; - z->c--; /* next, line 62 */ - z->bra = z->c; /* ], line 62 */ - { int ret = slice_del(z); /* delete, line 62 */ + z->c--; /* next, line 64 */ + z->bra = z->c; /* ], line 64 */ + { int ret = slice_del(z); /* delete, line 64 */ if (ret < 0) return ret; } return 1; @@ -210,35 +212,35 @@ static int r_consonant_pair(struct SN_env * z) { /* backwardmode */ static int r_other_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - { int m1 = z->l - z->c; (void)m1; /* do, line 66 */ - z->ket = z->c; /* [, line 66 */ - if (!(eq_s_b(z, 2, s_0))) goto lab0; /* literal, line 66 */ - z->bra = z->c; /* ], line 66 */ - if (!(eq_s_b(z, 2, s_1))) goto lab0; /* literal, line 66 */ - { int ret = slice_del(z); /* delete, line 66 */ + { int m1 = z->l - z->c; (void)m1; /* do, line 68 */ + z->ket = z->c; /* [, line 68 */ + if (!(eq_s_b(z, 2, s_0))) goto lab0; /* literal, line 68 */ + z->bra = z->c; /* ], line 68 */ + if (!(eq_s_b(z, 2, s_1))) goto lab0; /* literal, line 68 */ + { int ret = slice_del(z); /* delete, line 68 */ if (ret < 0) return ret; } lab0: z->c = z->l - m1; } - { int mlimit2; /* setlimit, line 67 */ + { int mlimit2; /* setlimit, line 69 */ if (z->c < z->I[0]) return 0; mlimit2 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 67 */ - if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1572992 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit2; return 0; } /* substring, line 67 */ + z->ket = z->c; /* [, line 69 */ + if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1572992 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit2; return 0; } /* substring, line 69 */ among_var = find_among_b(z, a_2, 5); if (!(among_var)) { z->lb = mlimit2; return 0; } - z->bra = z->c; /* ], line 67 */ + z->bra = z->c; /* ], line 69 */ z->lb = mlimit2; } - switch (among_var) { /* among, line 68 */ + switch (among_var) { /* among, line 70 */ case 1: - { int ret = slice_del(z); /* delete, line 70 */ + { int ret = slice_del(z); /* delete, line 72 */ if (ret < 0) return ret; } - { int m3 = z->l - z->c; (void)m3; /* do, line 70 */ - { int ret = r_consonant_pair(z); /* call consonant_pair, line 70 */ + { int m3 = z->l - z->c; (void)m3; /* do, line 72 */ + { int ret = r_consonant_pair(z); /* call consonant_pair, line 72 */ if (ret == 0) goto lab1; if (ret < 0) return ret; } @@ -247,7 +249,7 @@ static int r_other_suffix(struct SN_env * z) { /* backwardmode */ } break; case 2: - { int ret = slice_from_s(z, 3, s_2); /* <-, line 72 */ + { int ret = slice_from_s(z, 3, s_2); /* <-, line 74 */ if (ret < 0) return ret; } break; @@ -257,60 +259,60 @@ static int r_other_suffix(struct SN_env * z) { /* backwardmode */ static int r_undouble(struct SN_env * z) { /* backwardmode */ - { int mlimit1; /* setlimit, line 76 */ + { int mlimit1; /* setlimit, line 78 */ if (z->c < z->I[0]) return 0; mlimit1 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 76 */ - if (out_grouping_b(z, g_v, 97, 248, 0)) { z->lb = mlimit1; return 0; } /* non v, line 76 */ - z->bra = z->c; /* ], line 76 */ - z->S[0] = slice_to(z, z->S[0]); /* -> ch, line 76 */ - if (z->S[0] == 0) return -1; /* -> ch, line 76 */ + z->ket = z->c; /* [, line 78 */ + if (in_grouping_b(z, g_c, 98, 122, 0)) { z->lb = mlimit1; return 0; } /* grouping c, line 78 */ + z->bra = z->c; /* ], line 78 */ + z->S[0] = slice_to(z, z->S[0]); /* -> ch, line 78 */ + if (z->S[0] == 0) return -1; /* -> ch, line 78 */ z->lb = mlimit1; } - if (!(eq_v_b(z, z->S[0]))) return 0; /* name ch, line 77 */ - { int ret = slice_del(z); /* delete, line 78 */ + if (!(eq_v_b(z, z->S[0]))) return 0; /* name ch, line 79 */ + { int ret = slice_del(z); /* delete, line 80 */ if (ret < 0) return ret; } return 1; } extern int danish_ISO_8859_1_stem(struct SN_env * z) { /* forwardmode */ - { int c1 = z->c; /* do, line 84 */ - { int ret = r_mark_regions(z); /* call mark_regions, line 84 */ + { int c1 = z->c; /* do, line 86 */ + { int ret = r_mark_regions(z); /* call mark_regions, line 86 */ if (ret == 0) goto lab0; if (ret < 0) return ret; } lab0: z->c = c1; } - z->lb = z->c; z->c = z->l; /* backwards, line 85 */ + z->lb = z->c; z->c = z->l; /* backwards, line 87 */ - { int m2 = z->l - z->c; (void)m2; /* do, line 86 */ - { int ret = r_main_suffix(z); /* call main_suffix, line 86 */ + { int m2 = z->l - z->c; (void)m2; /* do, line 88 */ + { int ret = r_main_suffix(z); /* call main_suffix, line 88 */ if (ret == 0) goto lab1; if (ret < 0) return ret; } lab1: z->c = z->l - m2; } - { int m3 = z->l - z->c; (void)m3; /* do, line 87 */ - { int ret = r_consonant_pair(z); /* call consonant_pair, line 87 */ + { int m3 = z->l - z->c; (void)m3; /* do, line 89 */ + { int ret = r_consonant_pair(z); /* call consonant_pair, line 89 */ if (ret == 0) goto lab2; if (ret < 0) return ret; } lab2: z->c = z->l - m3; } - { int m4 = z->l - z->c; (void)m4; /* do, line 88 */ - { int ret = r_other_suffix(z); /* call other_suffix, line 88 */ + { int m4 = z->l - z->c; (void)m4; /* do, line 90 */ + { int ret = r_other_suffix(z); /* call other_suffix, line 90 */ if (ret == 0) goto lab3; if (ret < 0) return ret; } lab3: z->c = z->l - m4; } - { int m5 = z->l - z->c; (void)m5; /* do, line 89 */ - { int ret = r_undouble(z); /* call undouble, line 89 */ + { int m5 = z->l - z->c; (void)m5; /* do, line 91 */ + { int ret = r_undouble(z); /* call undouble, line 91 */ if (ret == 0) goto lab4; if (ret < 0) return ret; } diff --git a/src/backend/snowball/libstemmer/stem_ISO_8859_1_french.c b/src/backend/snowball/libstemmer/stem_ISO_8859_1_french.c index 1a1732b21618..4e9f76cd9a41 100644 --- a/src/backend/snowball/libstemmer/stem_ISO_8859_1_french.c +++ b/src/backend/snowball/libstemmer/stem_ISO_8859_1_french.c @@ -45,16 +45,22 @@ static const struct among a_0[3] = /* 2 */ { 3, s_0_2, -1, -1, 0} }; -static const symbol s_1_1[1] = { 'I' }; -static const symbol s_1_2[1] = { 'U' }; -static const symbol s_1_3[1] = { 'Y' }; +static const symbol s_1_1[1] = { 'H' }; +static const symbol s_1_2[2] = { 'H', 'e' }; +static const symbol s_1_3[2] = { 'H', 'i' }; +static const symbol s_1_4[1] = { 'I' }; +static const symbol s_1_5[1] = { 'U' }; +static const symbol s_1_6[1] = { 'Y' }; -static const struct among a_1[4] = +static const struct among a_1[7] = { -/* 0 */ { 0, 0, -1, 4, 0}, -/* 1 */ { 1, s_1_1, 0, 1, 0}, -/* 2 */ { 1, s_1_2, 0, 2, 0}, -/* 3 */ { 1, s_1_3, 0, 3, 0} +/* 0 */ { 0, 0, -1, 7, 0}, +/* 1 */ { 1, s_1_1, 0, 6, 0}, +/* 2 */ { 2, s_1_2, 1, 4, 0}, +/* 3 */ { 2, s_1_3, 1, 5, 0}, +/* 4 */ { 1, s_1_4, 0, 1, 0}, +/* 5 */ { 1, s_1_5, 0, 2, 0}, +/* 6 */ { 1, s_1_6, 0, 3, 0} }; static const symbol s_2_0[3] = { 'i', 'q', 'U' }; @@ -338,17 +344,15 @@ static const symbol s_7_2[4] = { 'i', 0xE8, 'r', 'e' }; static const symbol s_7_3[3] = { 'i', 'o', 'n' }; static const symbol s_7_4[3] = { 'I', 'e', 'r' }; static const symbol s_7_5[3] = { 'i', 'e', 'r' }; -static const symbol s_7_6[1] = { 0xEB }; -static const struct among a_7[7] = +static const struct among a_7[6] = { /* 0 */ { 1, s_7_0, -1, 3, 0}, /* 1 */ { 4, s_7_1, 0, 2, 0}, /* 2 */ { 4, s_7_2, 0, 2, 0}, /* 3 */ { 3, s_7_3, -1, 1, 0}, /* 4 */ { 3, s_7_4, -1, 2, 0}, -/* 5 */ { 3, s_7_5, -1, 2, 0}, -/* 6 */ { 1, s_7_6, -1, 4, 0} +/* 5 */ { 3, s_7_5, -1, 2, 0} }; static const symbol s_8_0[3] = { 'e', 'l', 'l' }; @@ -373,34 +377,38 @@ static const unsigned char g_keep_with_s[] = { 1, 65, 20, 0, 0, 0, 0, 0, 0, 0, 0 static const symbol s_0[] = { 'U' }; static const symbol s_1[] = { 'I' }; static const symbol s_2[] = { 'Y' }; -static const symbol s_3[] = { 'Y' }; -static const symbol s_4[] = { 'U' }; -static const symbol s_5[] = { 'i' }; -static const symbol s_6[] = { 'u' }; -static const symbol s_7[] = { 'y' }; -static const symbol s_8[] = { 'i', 'c' }; -static const symbol s_9[] = { 'i', 'q', 'U' }; -static const symbol s_10[] = { 'l', 'o', 'g' }; -static const symbol s_11[] = { 'u' }; -static const symbol s_12[] = { 'e', 'n', 't' }; -static const symbol s_13[] = { 'a', 't' }; -static const symbol s_14[] = { 'e', 'u', 'x' }; -static const symbol s_15[] = { 'i' }; -static const symbol s_16[] = { 'a', 'b', 'l' }; -static const symbol s_17[] = { 'i', 'q', 'U' }; -static const symbol s_18[] = { 'a', 't' }; -static const symbol s_19[] = { 'i', 'c' }; -static const symbol s_20[] = { 'i', 'q', 'U' }; -static const symbol s_21[] = { 'e', 'a', 'u' }; -static const symbol s_22[] = { 'a', 'l' }; -static const symbol s_23[] = { 'e', 'u', 'x' }; -static const symbol s_24[] = { 'a', 'n', 't' }; -static const symbol s_25[] = { 'e', 'n', 't' }; -static const symbol s_26[] = { 'i' }; -static const symbol s_27[] = { 'g', 'u' }; -static const symbol s_28[] = { 'e' }; -static const symbol s_29[] = { 'i' }; -static const symbol s_30[] = { 'c' }; +static const symbol s_3[] = { 'H', 'e' }; +static const symbol s_4[] = { 'H', 'i' }; +static const symbol s_5[] = { 'Y' }; +static const symbol s_6[] = { 'U' }; +static const symbol s_7[] = { 'i' }; +static const symbol s_8[] = { 'u' }; +static const symbol s_9[] = { 'y' }; +static const symbol s_10[] = { 0xEB }; +static const symbol s_11[] = { 0xEF }; +static const symbol s_12[] = { 'i', 'c' }; +static const symbol s_13[] = { 'i', 'q', 'U' }; +static const symbol s_14[] = { 'l', 'o', 'g' }; +static const symbol s_15[] = { 'u' }; +static const symbol s_16[] = { 'e', 'n', 't' }; +static const symbol s_17[] = { 'a', 't' }; +static const symbol s_18[] = { 'e', 'u', 'x' }; +static const symbol s_19[] = { 'i' }; +static const symbol s_20[] = { 'a', 'b', 'l' }; +static const symbol s_21[] = { 'i', 'q', 'U' }; +static const symbol s_22[] = { 'a', 't' }; +static const symbol s_23[] = { 'i', 'c' }; +static const symbol s_24[] = { 'i', 'q', 'U' }; +static const symbol s_25[] = { 'e', 'a', 'u' }; +static const symbol s_26[] = { 'a', 'l' }; +static const symbol s_27[] = { 'e', 'u', 'x' }; +static const symbol s_28[] = { 'a', 'n', 't' }; +static const symbol s_29[] = { 'e', 'n', 't' }; +static const symbol s_30[] = { 'H', 'i' }; +static const symbol s_31[] = { 'i' }; +static const symbol s_32[] = { 'e' }; +static const symbol s_33[] = { 'i' }; +static const symbol s_34[] = { 'c' }; static int r_prelude(struct SN_env * z) { /* forwardmode */ while(1) { /* repeat, line 38 */ @@ -443,23 +451,43 @@ static int r_prelude(struct SN_env * z) { /* forwardmode */ lab3: z->c = c3; z->bra = z->c; /* [, line 45 */ - if (z->c == z->l || z->p[z->c] != 'y') goto lab7; /* literal, line 45 */ + if (z->c == z->l || z->p[z->c] != 0xEB) goto lab7; /* literal, line 45 */ z->c++; z->ket = z->c; /* ], line 45 */ - if (in_grouping(z, g_v, 97, 251, 0)) goto lab7; /* grouping v, line 45 */ - { int ret = slice_from_s(z, 1, s_3); /* <-, line 45 */ + { int ret = slice_from_s(z, 2, s_3); /* <-, line 45 */ if (ret < 0) return ret; } goto lab2; lab7: z->c = c3; - if (z->c == z->l || z->p[z->c] != 'q') goto lab1; /* literal, line 47 */ - z->c++; z->bra = z->c; /* [, line 47 */ - if (z->c == z->l || z->p[z->c] != 'u') goto lab1; /* literal, line 47 */ + if (z->c == z->l || z->p[z->c] != 0xEF) goto lab8; /* literal, line 47 */ z->c++; z->ket = z->c; /* ], line 47 */ - { int ret = slice_from_s(z, 1, s_4); /* <-, line 47 */ + { int ret = slice_from_s(z, 2, s_4); /* <-, line 47 */ + if (ret < 0) return ret; + } + goto lab2; + lab8: + z->c = c3; + z->bra = z->c; /* [, line 49 */ + if (z->c == z->l || z->p[z->c] != 'y') goto lab9; /* literal, line 49 */ + z->c++; + z->ket = z->c; /* ], line 49 */ + if (in_grouping(z, g_v, 97, 251, 0)) goto lab9; /* grouping v, line 49 */ + { int ret = slice_from_s(z, 1, s_5); /* <-, line 49 */ + if (ret < 0) return ret; + } + goto lab2; + lab9: + z->c = c3; + if (z->c == z->l || z->p[z->c] != 'q') goto lab1; /* literal, line 51 */ + z->c++; + z->bra = z->c; /* [, line 51 */ + if (z->c == z->l || z->p[z->c] != 'u') goto lab1; /* literal, line 51 */ + z->c++; + z->ket = z->c; /* ], line 51 */ + { int ret = slice_from_s(z, 1, s_6); /* <-, line 51 */ if (ret < 0) return ret; } } @@ -480,59 +508,59 @@ static int r_prelude(struct SN_env * z) { /* forwardmode */ } static int r_mark_regions(struct SN_env * z) { /* forwardmode */ - z->I[0] = z->l; /* $pV = , line 52 */ - z->I[1] = z->l; /* $p1 = , line 53 */ - z->I[2] = z->l; /* $p2 = , line 54 */ - { int c1 = z->c; /* do, line 56 */ - { int c2 = z->c; /* or, line 58 */ - if (in_grouping(z, g_v, 97, 251, 0)) goto lab2; /* grouping v, line 57 */ - if (in_grouping(z, g_v, 97, 251, 0)) goto lab2; /* grouping v, line 57 */ + z->I[0] = z->l; /* $pV = , line 56 */ + z->I[1] = z->l; /* $p1 = , line 57 */ + z->I[2] = z->l; /* $p2 = , line 58 */ + { int c1 = z->c; /* do, line 60 */ + { int c2 = z->c; /* or, line 62 */ + if (in_grouping(z, g_v, 97, 251, 0)) goto lab2; /* grouping v, line 61 */ + if (in_grouping(z, g_v, 97, 251, 0)) goto lab2; /* grouping v, line 61 */ if (z->c >= z->l) goto lab2; - z->c++; /* next, line 57 */ + z->c++; /* next, line 61 */ goto lab1; lab2: z->c = c2; - if (z->c + 2 >= z->l || z->p[z->c + 2] >> 5 != 3 || !((331776 >> (z->p[z->c + 2] & 0x1f)) & 1)) goto lab3; /* among, line 59 */ + if (z->c + 2 >= z->l || z->p[z->c + 2] >> 5 != 3 || !((331776 >> (z->p[z->c + 2] & 0x1f)) & 1)) goto lab3; /* among, line 63 */ if (!(find_among(z, a_0, 3))) goto lab3; goto lab1; lab3: z->c = c2; if (z->c >= z->l) goto lab0; - z->c++; /* next, line 66 */ - { /* gopast */ /* grouping v, line 66 */ + z->c++; /* next, line 70 */ + { /* gopast */ /* grouping v, line 70 */ int ret = out_grouping(z, g_v, 97, 251, 1); if (ret < 0) goto lab0; z->c += ret; } } lab1: - z->I[0] = z->c; /* setmark pV, line 67 */ + z->I[0] = z->c; /* setmark pV, line 71 */ lab0: z->c = c1; } - { int c3 = z->c; /* do, line 69 */ - { /* gopast */ /* grouping v, line 70 */ + { int c3 = z->c; /* do, line 73 */ + { /* gopast */ /* grouping v, line 74 */ int ret = out_grouping(z, g_v, 97, 251, 1); if (ret < 0) goto lab4; z->c += ret; } - { /* gopast */ /* non v, line 70 */ + { /* gopast */ /* non v, line 74 */ int ret = in_grouping(z, g_v, 97, 251, 1); if (ret < 0) goto lab4; z->c += ret; } - z->I[1] = z->c; /* setmark p1, line 70 */ - { /* gopast */ /* grouping v, line 71 */ + z->I[1] = z->c; /* setmark p1, line 74 */ + { /* gopast */ /* grouping v, line 75 */ int ret = out_grouping(z, g_v, 97, 251, 1); if (ret < 0) goto lab4; z->c += ret; } - { /* gopast */ /* non v, line 71 */ + { /* gopast */ /* non v, line 75 */ int ret = in_grouping(z, g_v, 97, 251, 1); if (ret < 0) goto lab4; z->c += ret; } - z->I[2] = z->c; /* setmark p2, line 71 */ + z->I[2] = z->c; /* setmark p2, line 75 */ lab4: z->c = c3; } @@ -541,32 +569,47 @@ static int r_mark_regions(struct SN_env * z) { /* forwardmode */ static int r_postlude(struct SN_env * z) { /* forwardmode */ int among_var; - while(1) { /* repeat, line 75 */ + while(1) { /* repeat, line 79 */ int c1 = z->c; - z->bra = z->c; /* [, line 77 */ - if (z->c >= z->l || z->p[z->c + 0] >> 5 != 2 || !((35652096 >> (z->p[z->c + 0] & 0x1f)) & 1)) among_var = 4; else /* substring, line 77 */ - among_var = find_among(z, a_1, 4); + z->bra = z->c; /* [, line 81 */ + if (z->c >= z->l || z->p[z->c + 0] >> 5 != 2 || !((35652352 >> (z->p[z->c + 0] & 0x1f)) & 1)) among_var = 7; else /* substring, line 81 */ + among_var = find_among(z, a_1, 7); if (!(among_var)) goto lab0; - z->ket = z->c; /* ], line 77 */ - switch (among_var) { /* among, line 77 */ + z->ket = z->c; /* ], line 81 */ + switch (among_var) { /* among, line 81 */ case 1: - { int ret = slice_from_s(z, 1, s_5); /* <-, line 78 */ + { int ret = slice_from_s(z, 1, s_7); /* <-, line 82 */ if (ret < 0) return ret; } break; case 2: - { int ret = slice_from_s(z, 1, s_6); /* <-, line 79 */ + { int ret = slice_from_s(z, 1, s_8); /* <-, line 83 */ if (ret < 0) return ret; } break; case 3: - { int ret = slice_from_s(z, 1, s_7); /* <-, line 80 */ + { int ret = slice_from_s(z, 1, s_9); /* <-, line 84 */ if (ret < 0) return ret; } break; case 4: + { int ret = slice_from_s(z, 1, s_10); /* <-, line 85 */ + if (ret < 0) return ret; + } + break; + case 5: + { int ret = slice_from_s(z, 1, s_11); /* <-, line 86 */ + if (ret < 0) return ret; + } + break; + case 6: + { int ret = slice_del(z); /* delete, line 87 */ + if (ret < 0) return ret; + } + break; + case 7: if (z->c >= z->l) goto lab0; - z->c++; /* next, line 81 */ + z->c++; /* next, line 88 */ break; } continue; @@ -578,58 +621,58 @@ static int r_postlude(struct SN_env * z) { /* forwardmode */ } static int r_RV(struct SN_env * z) { /* backwardmode */ - if (!(z->I[0] <= z->c)) return 0; /* $( <= ), line 87 */ + if (!(z->I[0] <= z->c)) return 0; /* $( <= ), line 94 */ return 1; } static int r_R1(struct SN_env * z) { /* backwardmode */ - if (!(z->I[1] <= z->c)) return 0; /* $( <= ), line 88 */ + if (!(z->I[1] <= z->c)) return 0; /* $( <= ), line 95 */ return 1; } static int r_R2(struct SN_env * z) { /* backwardmode */ - if (!(z->I[2] <= z->c)) return 0; /* $( <= ), line 89 */ + if (!(z->I[2] <= z->c)) return 0; /* $( <= ), line 96 */ return 1; } static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - z->ket = z->c; /* [, line 92 */ - among_var = find_among_b(z, a_4, 43); /* substring, line 92 */ + z->ket = z->c; /* [, line 99 */ + among_var = find_among_b(z, a_4, 43); /* substring, line 99 */ if (!(among_var)) return 0; - z->bra = z->c; /* ], line 92 */ - switch (among_var) { /* among, line 92 */ + z->bra = z->c; /* ], line 99 */ + switch (among_var) { /* among, line 99 */ case 1: - { int ret = r_R2(z); /* call R2, line 96 */ + { int ret = r_R2(z); /* call R2, line 103 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 96 */ + { int ret = slice_del(z); /* delete, line 103 */ if (ret < 0) return ret; } break; case 2: - { int ret = r_R2(z); /* call R2, line 99 */ + { int ret = r_R2(z); /* call R2, line 106 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 99 */ + { int ret = slice_del(z); /* delete, line 106 */ if (ret < 0) return ret; } - { int m1 = z->l - z->c; (void)m1; /* try, line 100 */ - z->ket = z->c; /* [, line 100 */ - if (!(eq_s_b(z, 2, s_8))) { z->c = z->l - m1; goto lab0; } /* literal, line 100 */ - z->bra = z->c; /* ], line 100 */ - { int m2 = z->l - z->c; (void)m2; /* or, line 100 */ - { int ret = r_R2(z); /* call R2, line 100 */ + { int m1 = z->l - z->c; (void)m1; /* try, line 107 */ + z->ket = z->c; /* [, line 107 */ + if (!(eq_s_b(z, 2, s_12))) { z->c = z->l - m1; goto lab0; } /* literal, line 107 */ + z->bra = z->c; /* ], line 107 */ + { int m2 = z->l - z->c; (void)m2; /* or, line 107 */ + { int ret = r_R2(z); /* call R2, line 107 */ if (ret == 0) goto lab2; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 100 */ + { int ret = slice_del(z); /* delete, line 107 */ if (ret < 0) return ret; } goto lab1; lab2: z->c = z->l - m2; - { int ret = slice_from_s(z, 3, s_9); /* <-, line 100 */ + { int ret = slice_from_s(z, 3, s_13); /* <-, line 107 */ if (ret < 0) return ret; } } @@ -639,98 +682,98 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ } break; case 3: - { int ret = r_R2(z); /* call R2, line 104 */ + { int ret = r_R2(z); /* call R2, line 111 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_10); /* <-, line 104 */ + { int ret = slice_from_s(z, 3, s_14); /* <-, line 111 */ if (ret < 0) return ret; } break; case 4: - { int ret = r_R2(z); /* call R2, line 107 */ + { int ret = r_R2(z); /* call R2, line 114 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 1, s_11); /* <-, line 107 */ + { int ret = slice_from_s(z, 1, s_15); /* <-, line 114 */ if (ret < 0) return ret; } break; case 5: - { int ret = r_R2(z); /* call R2, line 110 */ + { int ret = r_R2(z); /* call R2, line 117 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_12); /* <-, line 110 */ + { int ret = slice_from_s(z, 3, s_16); /* <-, line 117 */ if (ret < 0) return ret; } break; case 6: - { int ret = r_RV(z); /* call RV, line 114 */ + { int ret = r_RV(z); /* call RV, line 121 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 114 */ + { int ret = slice_del(z); /* delete, line 121 */ if (ret < 0) return ret; } - { int m3 = z->l - z->c; (void)m3; /* try, line 115 */ - z->ket = z->c; /* [, line 116 */ - among_var = find_among_b(z, a_2, 6); /* substring, line 116 */ + { int m3 = z->l - z->c; (void)m3; /* try, line 122 */ + z->ket = z->c; /* [, line 123 */ + among_var = find_among_b(z, a_2, 6); /* substring, line 123 */ if (!(among_var)) { z->c = z->l - m3; goto lab3; } - z->bra = z->c; /* ], line 116 */ - switch (among_var) { /* among, line 116 */ + z->bra = z->c; /* ], line 123 */ + switch (among_var) { /* among, line 123 */ case 1: - { int ret = r_R2(z); /* call R2, line 117 */ + { int ret = r_R2(z); /* call R2, line 124 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 117 */ + { int ret = slice_del(z); /* delete, line 124 */ if (ret < 0) return ret; } - z->ket = z->c; /* [, line 117 */ - if (!(eq_s_b(z, 2, s_13))) { z->c = z->l - m3; goto lab3; } /* literal, line 117 */ - z->bra = z->c; /* ], line 117 */ - { int ret = r_R2(z); /* call R2, line 117 */ + z->ket = z->c; /* [, line 124 */ + if (!(eq_s_b(z, 2, s_17))) { z->c = z->l - m3; goto lab3; } /* literal, line 124 */ + z->bra = z->c; /* ], line 124 */ + { int ret = r_R2(z); /* call R2, line 124 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 117 */ + { int ret = slice_del(z); /* delete, line 124 */ if (ret < 0) return ret; } break; case 2: - { int m4 = z->l - z->c; (void)m4; /* or, line 118 */ - { int ret = r_R2(z); /* call R2, line 118 */ + { int m4 = z->l - z->c; (void)m4; /* or, line 125 */ + { int ret = r_R2(z); /* call R2, line 125 */ if (ret == 0) goto lab5; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 118 */ + { int ret = slice_del(z); /* delete, line 125 */ if (ret < 0) return ret; } goto lab4; lab5: z->c = z->l - m4; - { int ret = r_R1(z); /* call R1, line 118 */ + { int ret = r_R1(z); /* call R1, line 125 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_from_s(z, 3, s_14); /* <-, line 118 */ + { int ret = slice_from_s(z, 3, s_18); /* <-, line 125 */ if (ret < 0) return ret; } } lab4: break; case 3: - { int ret = r_R2(z); /* call R2, line 120 */ + { int ret = r_R2(z); /* call R2, line 127 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 120 */ + { int ret = slice_del(z); /* delete, line 127 */ if (ret < 0) return ret; } break; case 4: - { int ret = r_RV(z); /* call RV, line 122 */ + { int ret = r_RV(z); /* call RV, line 129 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_from_s(z, 1, s_15); /* <-, line 122 */ + { int ret = slice_from_s(z, 1, s_19); /* <-, line 129 */ if (ret < 0) return ret; } break; @@ -740,61 +783,61 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ } break; case 7: - { int ret = r_R2(z); /* call R2, line 129 */ + { int ret = r_R2(z); /* call R2, line 136 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 129 */ + { int ret = slice_del(z); /* delete, line 136 */ if (ret < 0) return ret; } - { int m5 = z->l - z->c; (void)m5; /* try, line 130 */ - z->ket = z->c; /* [, line 131 */ - if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((4198408 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->c = z->l - m5; goto lab6; } /* substring, line 131 */ + { int m5 = z->l - z->c; (void)m5; /* try, line 137 */ + z->ket = z->c; /* [, line 138 */ + if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((4198408 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->c = z->l - m5; goto lab6; } /* substring, line 138 */ among_var = find_among_b(z, a_3, 3); if (!(among_var)) { z->c = z->l - m5; goto lab6; } - z->bra = z->c; /* ], line 131 */ - switch (among_var) { /* among, line 131 */ + z->bra = z->c; /* ], line 138 */ + switch (among_var) { /* among, line 138 */ case 1: - { int m6 = z->l - z->c; (void)m6; /* or, line 132 */ - { int ret = r_R2(z); /* call R2, line 132 */ + { int m6 = z->l - z->c; (void)m6; /* or, line 139 */ + { int ret = r_R2(z); /* call R2, line 139 */ if (ret == 0) goto lab8; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 132 */ + { int ret = slice_del(z); /* delete, line 139 */ if (ret < 0) return ret; } goto lab7; lab8: z->c = z->l - m6; - { int ret = slice_from_s(z, 3, s_16); /* <-, line 132 */ + { int ret = slice_from_s(z, 3, s_20); /* <-, line 139 */ if (ret < 0) return ret; } } lab7: break; case 2: - { int m7 = z->l - z->c; (void)m7; /* or, line 133 */ - { int ret = r_R2(z); /* call R2, line 133 */ + { int m7 = z->l - z->c; (void)m7; /* or, line 140 */ + { int ret = r_R2(z); /* call R2, line 140 */ if (ret == 0) goto lab10; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 133 */ + { int ret = slice_del(z); /* delete, line 140 */ if (ret < 0) return ret; } goto lab9; lab10: z->c = z->l - m7; - { int ret = slice_from_s(z, 3, s_17); /* <-, line 133 */ + { int ret = slice_from_s(z, 3, s_21); /* <-, line 140 */ if (ret < 0) return ret; } } lab9: break; case 3: - { int ret = r_R2(z); /* call R2, line 134 */ + { int ret = r_R2(z); /* call R2, line 141 */ if (ret == 0) { z->c = z->l - m5; goto lab6; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 134 */ + { int ret = slice_del(z); /* delete, line 141 */ if (ret < 0) return ret; } break; @@ -804,38 +847,38 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ } break; case 8: - { int ret = r_R2(z); /* call R2, line 141 */ + { int ret = r_R2(z); /* call R2, line 148 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 141 */ + { int ret = slice_del(z); /* delete, line 148 */ if (ret < 0) return ret; } - { int m8 = z->l - z->c; (void)m8; /* try, line 142 */ - z->ket = z->c; /* [, line 142 */ - if (!(eq_s_b(z, 2, s_18))) { z->c = z->l - m8; goto lab11; } /* literal, line 142 */ - z->bra = z->c; /* ], line 142 */ - { int ret = r_R2(z); /* call R2, line 142 */ + { int m8 = z->l - z->c; (void)m8; /* try, line 149 */ + z->ket = z->c; /* [, line 149 */ + if (!(eq_s_b(z, 2, s_22))) { z->c = z->l - m8; goto lab11; } /* literal, line 149 */ + z->bra = z->c; /* ], line 149 */ + { int ret = r_R2(z); /* call R2, line 149 */ if (ret == 0) { z->c = z->l - m8; goto lab11; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 142 */ + { int ret = slice_del(z); /* delete, line 149 */ if (ret < 0) return ret; } - z->ket = z->c; /* [, line 142 */ - if (!(eq_s_b(z, 2, s_19))) { z->c = z->l - m8; goto lab11; } /* literal, line 142 */ - z->bra = z->c; /* ], line 142 */ - { int m9 = z->l - z->c; (void)m9; /* or, line 142 */ - { int ret = r_R2(z); /* call R2, line 142 */ + z->ket = z->c; /* [, line 149 */ + if (!(eq_s_b(z, 2, s_23))) { z->c = z->l - m8; goto lab11; } /* literal, line 149 */ + z->bra = z->c; /* ], line 149 */ + { int m9 = z->l - z->c; (void)m9; /* or, line 149 */ + { int ret = r_R2(z); /* call R2, line 149 */ if (ret == 0) goto lab13; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 142 */ + { int ret = slice_del(z); /* delete, line 149 */ if (ret < 0) return ret; } goto lab12; lab13: z->c = z->l - m9; - { int ret = slice_from_s(z, 3, s_20); /* <-, line 142 */ + { int ret = slice_from_s(z, 3, s_24); /* <-, line 149 */ if (ret < 0) return ret; } } @@ -845,78 +888,78 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ } break; case 9: - { int ret = slice_from_s(z, 3, s_21); /* <-, line 144 */ + { int ret = slice_from_s(z, 3, s_25); /* <-, line 151 */ if (ret < 0) return ret; } break; case 10: - { int ret = r_R1(z); /* call R1, line 145 */ + { int ret = r_R1(z); /* call R1, line 152 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 2, s_22); /* <-, line 145 */ + { int ret = slice_from_s(z, 2, s_26); /* <-, line 152 */ if (ret < 0) return ret; } break; case 11: - { int m10 = z->l - z->c; (void)m10; /* or, line 147 */ - { int ret = r_R2(z); /* call R2, line 147 */ + { int m10 = z->l - z->c; (void)m10; /* or, line 154 */ + { int ret = r_R2(z); /* call R2, line 154 */ if (ret == 0) goto lab15; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 147 */ + { int ret = slice_del(z); /* delete, line 154 */ if (ret < 0) return ret; } goto lab14; lab15: z->c = z->l - m10; - { int ret = r_R1(z); /* call R1, line 147 */ + { int ret = r_R1(z); /* call R1, line 154 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_23); /* <-, line 147 */ + { int ret = slice_from_s(z, 3, s_27); /* <-, line 154 */ if (ret < 0) return ret; } } lab14: break; case 12: - { int ret = r_R1(z); /* call R1, line 150 */ + { int ret = r_R1(z); /* call R1, line 157 */ if (ret <= 0) return ret; } - if (out_grouping_b(z, g_v, 97, 251, 0)) return 0; /* non v, line 150 */ - { int ret = slice_del(z); /* delete, line 150 */ + if (out_grouping_b(z, g_v, 97, 251, 0)) return 0; /* non v, line 157 */ + { int ret = slice_del(z); /* delete, line 157 */ if (ret < 0) return ret; } break; case 13: - { int ret = r_RV(z); /* call RV, line 155 */ + { int ret = r_RV(z); /* call RV, line 162 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_24); /* <-, line 155 */ + { int ret = slice_from_s(z, 3, s_28); /* <-, line 162 */ if (ret < 0) return ret; } - return 0; /* fail, line 155 */ + return 0; /* fail, line 162 */ break; case 14: - { int ret = r_RV(z); /* call RV, line 156 */ + { int ret = r_RV(z); /* call RV, line 163 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_25); /* <-, line 156 */ + { int ret = slice_from_s(z, 3, s_29); /* <-, line 163 */ if (ret < 0) return ret; } - return 0; /* fail, line 156 */ + return 0; /* fail, line 163 */ break; case 15: - { int m_test11 = z->l - z->c; /* test, line 158 */ - if (in_grouping_b(z, g_v, 97, 251, 0)) return 0; /* grouping v, line 158 */ - { int ret = r_RV(z); /* call RV, line 158 */ + { int m_test11 = z->l - z->c; /* test, line 165 */ + if (in_grouping_b(z, g_v, 97, 251, 0)) return 0; /* grouping v, line 165 */ + { int ret = r_RV(z); /* call RV, line 165 */ if (ret <= 0) return ret; } z->c = z->l - m_test11; } - { int ret = slice_del(z); /* delete, line 158 */ + { int ret = slice_del(z); /* delete, line 165 */ if (ret < 0) return ret; } - return 0; /* fail, line 158 */ + return 0; /* fail, line 165 */ break; } return 1; @@ -924,15 +967,22 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ static int r_i_verb_suffix(struct SN_env * z) { /* backwardmode */ - { int mlimit1; /* setlimit, line 163 */ + { int mlimit1; /* setlimit, line 170 */ if (z->c < z->I[0]) return 0; mlimit1 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 164 */ - if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((68944418 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit1; return 0; } /* substring, line 164 */ + z->ket = z->c; /* [, line 171 */ + if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((68944418 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit1; return 0; } /* substring, line 171 */ if (!(find_among_b(z, a_5, 35))) { z->lb = mlimit1; return 0; } - z->bra = z->c; /* ], line 164 */ - if (out_grouping_b(z, g_v, 97, 251, 0)) { z->lb = mlimit1; return 0; } /* non v, line 170 */ - { int ret = slice_del(z); /* delete, line 170 */ + z->bra = z->c; /* ], line 171 */ + { int m2 = z->l - z->c; (void)m2; /* not, line 177 */ + if (z->c <= z->lb || z->p[z->c - 1] != 'H') goto lab0; /* literal, line 177 */ + z->c--; + { z->lb = mlimit1; return 0; } + lab0: + z->c = z->l - m2; + } + if (out_grouping_b(z, g_v, 97, 251, 0)) { z->lb = mlimit1; return 0; } /* non v, line 177 */ + { int ret = slice_del(z); /* delete, line 177 */ if (ret < 0) return ret; } z->lb = mlimit1; @@ -943,38 +993,38 @@ static int r_i_verb_suffix(struct SN_env * z) { /* backwardmode */ static int r_verb_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - { int mlimit1; /* setlimit, line 174 */ + { int mlimit1; /* setlimit, line 181 */ if (z->c < z->I[0]) return 0; mlimit1 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 175 */ - among_var = find_among_b(z, a_6, 38); /* substring, line 175 */ + z->ket = z->c; /* [, line 182 */ + among_var = find_among_b(z, a_6, 38); /* substring, line 182 */ if (!(among_var)) { z->lb = mlimit1; return 0; } - z->bra = z->c; /* ], line 175 */ - switch (among_var) { /* among, line 175 */ + z->bra = z->c; /* ], line 182 */ + switch (among_var) { /* among, line 182 */ case 1: - { int ret = r_R2(z); /* call R2, line 177 */ + { int ret = r_R2(z); /* call R2, line 184 */ if (ret == 0) { z->lb = mlimit1; return 0; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 177 */ + { int ret = slice_del(z); /* delete, line 184 */ if (ret < 0) return ret; } break; case 2: - { int ret = slice_del(z); /* delete, line 185 */ + { int ret = slice_del(z); /* delete, line 192 */ if (ret < 0) return ret; } break; case 3: - { int ret = slice_del(z); /* delete, line 190 */ + { int ret = slice_del(z); /* delete, line 197 */ if (ret < 0) return ret; } - { int m2 = z->l - z->c; (void)m2; /* try, line 191 */ - z->ket = z->c; /* [, line 191 */ - if (z->c <= z->lb || z->p[z->c - 1] != 'e') { z->c = z->l - m2; goto lab0; } /* literal, line 191 */ + { int m2 = z->l - z->c; (void)m2; /* try, line 198 */ + z->ket = z->c; /* [, line 198 */ + if (z->c <= z->lb || z->p[z->c - 1] != 'e') { z->c = z->l - m2; goto lab0; } /* literal, line 198 */ z->c--; - z->bra = z->c; /* ], line 191 */ - { int ret = slice_del(z); /* delete, line 191 */ + z->bra = z->c; /* ], line 198 */ + { int ret = slice_del(z); /* delete, line 198 */ if (ret < 0) return ret; } lab0: @@ -989,82 +1039,84 @@ static int r_verb_suffix(struct SN_env * z) { /* backwardmode */ static int r_residual_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - { int m1 = z->l - z->c; (void)m1; /* try, line 199 */ - z->ket = z->c; /* [, line 199 */ - if (z->c <= z->lb || z->p[z->c - 1] != 's') { z->c = z->l - m1; goto lab0; } /* literal, line 199 */ + { int m1 = z->l - z->c; (void)m1; /* try, line 206 */ + z->ket = z->c; /* [, line 206 */ + if (z->c <= z->lb || z->p[z->c - 1] != 's') { z->c = z->l - m1; goto lab0; } /* literal, line 206 */ z->c--; - z->bra = z->c; /* ], line 199 */ - { int m_test2 = z->l - z->c; /* test, line 199 */ - if (out_grouping_b(z, g_keep_with_s, 97, 232, 0)) { z->c = z->l - m1; goto lab0; } /* non keep_with_s, line 199 */ + z->bra = z->c; /* ], line 206 */ + { int m_test2 = z->l - z->c; /* test, line 206 */ + { int m3 = z->l - z->c; (void)m3; /* or, line 206 */ + if (!(eq_s_b(z, 2, s_30))) goto lab2; /* literal, line 206 */ + goto lab1; + lab2: + z->c = z->l - m3; + if (out_grouping_b(z, g_keep_with_s, 97, 232, 0)) { z->c = z->l - m1; goto lab0; } /* non keep_with_s, line 206 */ + } + lab1: z->c = z->l - m_test2; } - { int ret = slice_del(z); /* delete, line 199 */ + { int ret = slice_del(z); /* delete, line 206 */ if (ret < 0) return ret; } lab0: ; } - { int mlimit3; /* setlimit, line 200 */ + { int mlimit4; /* setlimit, line 207 */ if (z->c < z->I[0]) return 0; - mlimit3 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 201 */ - among_var = find_among_b(z, a_7, 7); /* substring, line 201 */ - if (!(among_var)) { z->lb = mlimit3; return 0; } - z->bra = z->c; /* ], line 201 */ - switch (among_var) { /* among, line 201 */ + mlimit4 = z->lb; z->lb = z->I[0]; + z->ket = z->c; /* [, line 208 */ + if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((278560 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit4; return 0; } /* substring, line 208 */ + among_var = find_among_b(z, a_7, 6); + if (!(among_var)) { z->lb = mlimit4; return 0; } + z->bra = z->c; /* ], line 208 */ + switch (among_var) { /* among, line 208 */ case 1: - { int ret = r_R2(z); /* call R2, line 202 */ - if (ret == 0) { z->lb = mlimit3; return 0; } + { int ret = r_R2(z); /* call R2, line 209 */ + if (ret == 0) { z->lb = mlimit4; return 0; } if (ret < 0) return ret; } - { int m4 = z->l - z->c; (void)m4; /* or, line 202 */ - if (z->c <= z->lb || z->p[z->c - 1] != 's') goto lab2; /* literal, line 202 */ + { int m5 = z->l - z->c; (void)m5; /* or, line 209 */ + if (z->c <= z->lb || z->p[z->c - 1] != 's') goto lab4; /* literal, line 209 */ z->c--; - goto lab1; - lab2: - z->c = z->l - m4; - if (z->c <= z->lb || z->p[z->c - 1] != 't') { z->lb = mlimit3; return 0; } /* literal, line 202 */ + goto lab3; + lab4: + z->c = z->l - m5; + if (z->c <= z->lb || z->p[z->c - 1] != 't') { z->lb = mlimit4; return 0; } /* literal, line 209 */ z->c--; } - lab1: - { int ret = slice_del(z); /* delete, line 202 */ + lab3: + { int ret = slice_del(z); /* delete, line 209 */ if (ret < 0) return ret; } break; case 2: - { int ret = slice_from_s(z, 1, s_26); /* <-, line 204 */ + { int ret = slice_from_s(z, 1, s_31); /* <-, line 211 */ if (ret < 0) return ret; } break; case 3: - { int ret = slice_del(z); /* delete, line 205 */ - if (ret < 0) return ret; - } - break; - case 4: - if (!(eq_s_b(z, 2, s_27))) { z->lb = mlimit3; return 0; } /* literal, line 206 */ - { int ret = slice_del(z); /* delete, line 206 */ + { int ret = slice_del(z); /* delete, line 212 */ if (ret < 0) return ret; } break; } - z->lb = mlimit3; + z->lb = mlimit4; } return 1; } static int r_un_double(struct SN_env * z) { /* backwardmode */ - { int m_test1 = z->l - z->c; /* test, line 212 */ - if (z->c - 2 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1069056 >> (z->p[z->c - 1] & 0x1f)) & 1)) return 0; /* among, line 212 */ + { int m_test1 = z->l - z->c; /* test, line 218 */ + if (z->c - 2 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1069056 >> (z->p[z->c - 1] & 0x1f)) & 1)) return 0; /* among, line 218 */ if (!(find_among_b(z, a_8, 5))) return 0; z->c = z->l - m_test1; } - z->ket = z->c; /* [, line 212 */ + z->ket = z->c; /* [, line 218 */ if (z->c <= z->lb) return 0; - z->c--; /* next, line 212 */ - z->bra = z->c; /* ], line 212 */ - { int ret = slice_del(z); /* delete, line 212 */ + z->c--; /* next, line 218 */ + z->bra = z->c; /* ], line 218 */ + { int ret = slice_del(z); /* delete, line 218 */ if (ret < 0) return ret; } return 1; @@ -1072,8 +1124,8 @@ static int r_un_double(struct SN_env * z) { /* backwardmode */ static int r_un_accent(struct SN_env * z) { /* backwardmode */ { int i = 1; - while(1) { /* atleast, line 216 */ - if (out_grouping_b(z, g_v, 97, 251, 0)) goto lab0; /* non v, line 216 */ + while(1) { /* atleast, line 222 */ + if (out_grouping_b(z, g_v, 97, 251, 0)) goto lab0; /* non v, line 222 */ i--; continue; lab0: @@ -1081,82 +1133,82 @@ static int r_un_accent(struct SN_env * z) { /* backwardmode */ } if (i > 0) return 0; } - z->ket = z->c; /* [, line 217 */ - { int m1 = z->l - z->c; (void)m1; /* or, line 217 */ - if (z->c <= z->lb || z->p[z->c - 1] != 0xE9) goto lab2; /* literal, line 217 */ + z->ket = z->c; /* [, line 223 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 223 */ + if (z->c <= z->lb || z->p[z->c - 1] != 0xE9) goto lab2; /* literal, line 223 */ z->c--; goto lab1; lab2: z->c = z->l - m1; - if (z->c <= z->lb || z->p[z->c - 1] != 0xE8) return 0; /* literal, line 217 */ + if (z->c <= z->lb || z->p[z->c - 1] != 0xE8) return 0; /* literal, line 223 */ z->c--; } lab1: - z->bra = z->c; /* ], line 217 */ - { int ret = slice_from_s(z, 1, s_28); /* <-, line 217 */ + z->bra = z->c; /* ], line 223 */ + { int ret = slice_from_s(z, 1, s_32); /* <-, line 223 */ if (ret < 0) return ret; } return 1; } extern int french_ISO_8859_1_stem(struct SN_env * z) { /* forwardmode */ - { int c1 = z->c; /* do, line 223 */ - { int ret = r_prelude(z); /* call prelude, line 223 */ + { int c1 = z->c; /* do, line 229 */ + { int ret = r_prelude(z); /* call prelude, line 229 */ if (ret == 0) goto lab0; if (ret < 0) return ret; } lab0: z->c = c1; } - /* do, line 224 */ - { int ret = r_mark_regions(z); /* call mark_regions, line 224 */ + /* do, line 230 */ + { int ret = r_mark_regions(z); /* call mark_regions, line 230 */ if (ret == 0) goto lab1; if (ret < 0) return ret; } lab1: - z->lb = z->c; z->c = z->l; /* backwards, line 225 */ + z->lb = z->c; z->c = z->l; /* backwards, line 231 */ - { int m2 = z->l - z->c; (void)m2; /* do, line 227 */ - { int m3 = z->l - z->c; (void)m3; /* or, line 237 */ - { int m4 = z->l - z->c; (void)m4; /* and, line 233 */ - { int m5 = z->l - z->c; (void)m5; /* or, line 229 */ - { int ret = r_standard_suffix(z); /* call standard_suffix, line 229 */ + { int m2 = z->l - z->c; (void)m2; /* do, line 233 */ + { int m3 = z->l - z->c; (void)m3; /* or, line 243 */ + { int m4 = z->l - z->c; (void)m4; /* and, line 239 */ + { int m5 = z->l - z->c; (void)m5; /* or, line 235 */ + { int ret = r_standard_suffix(z); /* call standard_suffix, line 235 */ if (ret == 0) goto lab6; if (ret < 0) return ret; } goto lab5; lab6: z->c = z->l - m5; - { int ret = r_i_verb_suffix(z); /* call i_verb_suffix, line 230 */ + { int ret = r_i_verb_suffix(z); /* call i_verb_suffix, line 236 */ if (ret == 0) goto lab7; if (ret < 0) return ret; } goto lab5; lab7: z->c = z->l - m5; - { int ret = r_verb_suffix(z); /* call verb_suffix, line 231 */ + { int ret = r_verb_suffix(z); /* call verb_suffix, line 237 */ if (ret == 0) goto lab4; if (ret < 0) return ret; } } lab5: z->c = z->l - m4; - { int m6 = z->l - z->c; (void)m6; /* try, line 234 */ - z->ket = z->c; /* [, line 234 */ - { int m7 = z->l - z->c; (void)m7; /* or, line 234 */ - if (z->c <= z->lb || z->p[z->c - 1] != 'Y') goto lab10; /* literal, line 234 */ + { int m6 = z->l - z->c; (void)m6; /* try, line 240 */ + z->ket = z->c; /* [, line 240 */ + { int m7 = z->l - z->c; (void)m7; /* or, line 240 */ + if (z->c <= z->lb || z->p[z->c - 1] != 'Y') goto lab10; /* literal, line 240 */ z->c--; - z->bra = z->c; /* ], line 234 */ - { int ret = slice_from_s(z, 1, s_29); /* <-, line 234 */ + z->bra = z->c; /* ], line 240 */ + { int ret = slice_from_s(z, 1, s_33); /* <-, line 240 */ if (ret < 0) return ret; } goto lab9; lab10: z->c = z->l - m7; - if (z->c <= z->lb || z->p[z->c - 1] != 0xE7) { z->c = z->l - m6; goto lab8; } /* literal, line 235 */ + if (z->c <= z->lb || z->p[z->c - 1] != 0xE7) { z->c = z->l - m6; goto lab8; } /* literal, line 241 */ z->c--; - z->bra = z->c; /* ], line 235 */ - { int ret = slice_from_s(z, 1, s_30); /* <-, line 235 */ + z->bra = z->c; /* ], line 241 */ + { int ret = slice_from_s(z, 1, s_34); /* <-, line 241 */ if (ret < 0) return ret; } } @@ -1168,7 +1220,7 @@ extern int french_ISO_8859_1_stem(struct SN_env * z) { /* forwardmode */ goto lab3; lab4: z->c = z->l - m3; - { int ret = r_residual_suffix(z); /* call residual_suffix, line 238 */ + { int ret = r_residual_suffix(z); /* call residual_suffix, line 244 */ if (ret == 0) goto lab2; if (ret < 0) return ret; } @@ -1177,16 +1229,16 @@ extern int french_ISO_8859_1_stem(struct SN_env * z) { /* forwardmode */ lab2: z->c = z->l - m2; } - { int m8 = z->l - z->c; (void)m8; /* do, line 243 */ - { int ret = r_un_double(z); /* call un_double, line 243 */ + { int m8 = z->l - z->c; (void)m8; /* do, line 249 */ + { int ret = r_un_double(z); /* call un_double, line 249 */ if (ret == 0) goto lab11; if (ret < 0) return ret; } lab11: z->c = z->l - m8; } - { int m9 = z->l - z->c; (void)m9; /* do, line 244 */ - { int ret = r_un_accent(z); /* call un_accent, line 244 */ + { int m9 = z->l - z->c; (void)m9; /* do, line 250 */ + { int ret = r_un_accent(z); /* call un_accent, line 250 */ if (ret == 0) goto lab12; if (ret < 0) return ret; } @@ -1194,8 +1246,8 @@ extern int french_ISO_8859_1_stem(struct SN_env * z) { /* forwardmode */ z->c = z->l - m9; } z->c = z->lb; - { int c10 = z->c; /* do, line 246 */ - { int ret = r_postlude(z); /* call postlude, line 246 */ + { int c10 = z->c; /* do, line 252 */ + { int ret = r_postlude(z); /* call postlude, line 252 */ if (ret == 0) goto lab13; if (ret < 0) return ret; } diff --git a/src/backend/snowball/libstemmer/stem_UTF_8_danish.c b/src/backend/snowball/libstemmer/stem_UTF_8_danish.c index 4f7e3016bb2a..901491c7b6e3 100644 --- a/src/backend/snowball/libstemmer/stem_UTF_8_danish.c +++ b/src/backend/snowball/libstemmer/stem_UTF_8_danish.c @@ -124,6 +124,8 @@ static const struct among a_2[5] = /* 4 */ { 5, s_2_4, -1, 2, 0} }; +static const unsigned char g_c[] = { 119, 223, 119, 1 }; + static const unsigned char g_v[] = { 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 128 }; static const unsigned char g_s_ending[] = { 239, 254, 42, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 }; @@ -133,25 +135,25 @@ static const symbol s_1[] = { 'i', 'g' }; static const symbol s_2[] = { 'l', 0xC3, 0xB8, 's' }; static int r_mark_regions(struct SN_env * z) { /* forwardmode */ - z->I[0] = z->l; /* $p1 = , line 31 */ - { int c_test1 = z->c; /* test, line 33 */ - { int ret = skip_utf8(z->p, z->c, 0, z->l, + 3); /* hop, line 33 */ + z->I[0] = z->l; /* $p1 = , line 33 */ + { int c_test1 = z->c; /* test, line 35 */ + { int ret = skip_utf8(z->p, z->c, 0, z->l, + 3); /* hop, line 35 */ if (ret < 0) return 0; z->c = ret; } - z->I[1] = z->c; /* setmark x, line 33 */ + z->I[1] = z->c; /* setmark x, line 35 */ z->c = c_test1; } - if (out_grouping_U(z, g_v, 97, 248, 1) < 0) return 0; /* goto */ /* grouping v, line 34 */ - { /* gopast */ /* non v, line 34 */ + if (out_grouping_U(z, g_v, 97, 248, 1) < 0) return 0; /* goto */ /* grouping v, line 36 */ + { /* gopast */ /* non v, line 36 */ int ret = in_grouping_U(z, g_v, 97, 248, 1); if (ret < 0) return 0; z->c += ret; } - z->I[0] = z->c; /* setmark p1, line 34 */ - /* try, line 35 */ - if (!(z->I[0] < z->I[1])) goto lab0; /* $( < ), line 35 */ - z->I[0] = z->I[1]; /* $p1 = , line 35 */ + z->I[0] = z->c; /* setmark p1, line 36 */ + /* try, line 37 */ + if (!(z->I[0] < z->I[1])) goto lab0; /* $( < ), line 37 */ + z->I[0] = z->I[1]; /* $p1 = , line 37 */ lab0: return 1; } @@ -159,25 +161,25 @@ static int r_mark_regions(struct SN_env * z) { /* forwardmode */ static int r_main_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - { int mlimit1; /* setlimit, line 41 */ + { int mlimit1; /* setlimit, line 43 */ if (z->c < z->I[0]) return 0; mlimit1 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 41 */ - if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1851440 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit1; return 0; } /* substring, line 41 */ + z->ket = z->c; /* [, line 43 */ + if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1851440 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit1; return 0; } /* substring, line 43 */ among_var = find_among_b(z, a_0, 32); if (!(among_var)) { z->lb = mlimit1; return 0; } - z->bra = z->c; /* ], line 41 */ + z->bra = z->c; /* ], line 43 */ z->lb = mlimit1; } - switch (among_var) { /* among, line 42 */ + switch (among_var) { /* among, line 44 */ case 1: - { int ret = slice_del(z); /* delete, line 48 */ + { int ret = slice_del(z); /* delete, line 50 */ if (ret < 0) return ret; } break; case 2: - if (in_grouping_b_U(z, g_s_ending, 97, 229, 0)) return 0; /* grouping s_ending, line 50 */ - { int ret = slice_del(z); /* delete, line 50 */ + if (in_grouping_b_U(z, g_s_ending, 97, 229, 0)) return 0; /* grouping s_ending, line 52 */ + { int ret = slice_del(z); /* delete, line 52 */ if (ret < 0) return ret; } break; @@ -186,25 +188,25 @@ static int r_main_suffix(struct SN_env * z) { /* backwardmode */ } static int r_consonant_pair(struct SN_env * z) { /* backwardmode */ - { int m_test1 = z->l - z->c; /* test, line 55 */ + { int m_test1 = z->l - z->c; /* test, line 57 */ - { int mlimit2; /* setlimit, line 56 */ + { int mlimit2; /* setlimit, line 58 */ if (z->c < z->I[0]) return 0; mlimit2 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 56 */ - if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 100 && z->p[z->c - 1] != 116)) { z->lb = mlimit2; return 0; } /* substring, line 56 */ + z->ket = z->c; /* [, line 58 */ + if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 100 && z->p[z->c - 1] != 116)) { z->lb = mlimit2; return 0; } /* substring, line 58 */ if (!(find_among_b(z, a_1, 4))) { z->lb = mlimit2; return 0; } - z->bra = z->c; /* ], line 56 */ + z->bra = z->c; /* ], line 58 */ z->lb = mlimit2; } z->c = z->l - m_test1; } { int ret = skip_utf8(z->p, z->c, z->lb, 0, -1); if (ret < 0) return 0; - z->c = ret; /* next, line 62 */ + z->c = ret; /* next, line 64 */ } - z->bra = z->c; /* ], line 62 */ - { int ret = slice_del(z); /* delete, line 62 */ + z->bra = z->c; /* ], line 64 */ + { int ret = slice_del(z); /* delete, line 64 */ if (ret < 0) return ret; } return 1; @@ -212,35 +214,35 @@ static int r_consonant_pair(struct SN_env * z) { /* backwardmode */ static int r_other_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - { int m1 = z->l - z->c; (void)m1; /* do, line 66 */ - z->ket = z->c; /* [, line 66 */ - if (!(eq_s_b(z, 2, s_0))) goto lab0; /* literal, line 66 */ - z->bra = z->c; /* ], line 66 */ - if (!(eq_s_b(z, 2, s_1))) goto lab0; /* literal, line 66 */ - { int ret = slice_del(z); /* delete, line 66 */ + { int m1 = z->l - z->c; (void)m1; /* do, line 68 */ + z->ket = z->c; /* [, line 68 */ + if (!(eq_s_b(z, 2, s_0))) goto lab0; /* literal, line 68 */ + z->bra = z->c; /* ], line 68 */ + if (!(eq_s_b(z, 2, s_1))) goto lab0; /* literal, line 68 */ + { int ret = slice_del(z); /* delete, line 68 */ if (ret < 0) return ret; } lab0: z->c = z->l - m1; } - { int mlimit2; /* setlimit, line 67 */ + { int mlimit2; /* setlimit, line 69 */ if (z->c < z->I[0]) return 0; mlimit2 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 67 */ - if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1572992 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit2; return 0; } /* substring, line 67 */ + z->ket = z->c; /* [, line 69 */ + if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1572992 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit2; return 0; } /* substring, line 69 */ among_var = find_among_b(z, a_2, 5); if (!(among_var)) { z->lb = mlimit2; return 0; } - z->bra = z->c; /* ], line 67 */ + z->bra = z->c; /* ], line 69 */ z->lb = mlimit2; } - switch (among_var) { /* among, line 68 */ + switch (among_var) { /* among, line 70 */ case 1: - { int ret = slice_del(z); /* delete, line 70 */ + { int ret = slice_del(z); /* delete, line 72 */ if (ret < 0) return ret; } - { int m3 = z->l - z->c; (void)m3; /* do, line 70 */ - { int ret = r_consonant_pair(z); /* call consonant_pair, line 70 */ + { int m3 = z->l - z->c; (void)m3; /* do, line 72 */ + { int ret = r_consonant_pair(z); /* call consonant_pair, line 72 */ if (ret == 0) goto lab1; if (ret < 0) return ret; } @@ -249,7 +251,7 @@ static int r_other_suffix(struct SN_env * z) { /* backwardmode */ } break; case 2: - { int ret = slice_from_s(z, 4, s_2); /* <-, line 72 */ + { int ret = slice_from_s(z, 4, s_2); /* <-, line 74 */ if (ret < 0) return ret; } break; @@ -259,60 +261,60 @@ static int r_other_suffix(struct SN_env * z) { /* backwardmode */ static int r_undouble(struct SN_env * z) { /* backwardmode */ - { int mlimit1; /* setlimit, line 76 */ + { int mlimit1; /* setlimit, line 78 */ if (z->c < z->I[0]) return 0; mlimit1 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 76 */ - if (out_grouping_b_U(z, g_v, 97, 248, 0)) { z->lb = mlimit1; return 0; } /* non v, line 76 */ - z->bra = z->c; /* ], line 76 */ - z->S[0] = slice_to(z, z->S[0]); /* -> ch, line 76 */ - if (z->S[0] == 0) return -1; /* -> ch, line 76 */ + z->ket = z->c; /* [, line 78 */ + if (in_grouping_b_U(z, g_c, 98, 122, 0)) { z->lb = mlimit1; return 0; } /* grouping c, line 78 */ + z->bra = z->c; /* ], line 78 */ + z->S[0] = slice_to(z, z->S[0]); /* -> ch, line 78 */ + if (z->S[0] == 0) return -1; /* -> ch, line 78 */ z->lb = mlimit1; } - if (!(eq_v_b(z, z->S[0]))) return 0; /* name ch, line 77 */ - { int ret = slice_del(z); /* delete, line 78 */ + if (!(eq_v_b(z, z->S[0]))) return 0; /* name ch, line 79 */ + { int ret = slice_del(z); /* delete, line 80 */ if (ret < 0) return ret; } return 1; } extern int danish_UTF_8_stem(struct SN_env * z) { /* forwardmode */ - { int c1 = z->c; /* do, line 84 */ - { int ret = r_mark_regions(z); /* call mark_regions, line 84 */ + { int c1 = z->c; /* do, line 86 */ + { int ret = r_mark_regions(z); /* call mark_regions, line 86 */ if (ret == 0) goto lab0; if (ret < 0) return ret; } lab0: z->c = c1; } - z->lb = z->c; z->c = z->l; /* backwards, line 85 */ + z->lb = z->c; z->c = z->l; /* backwards, line 87 */ - { int m2 = z->l - z->c; (void)m2; /* do, line 86 */ - { int ret = r_main_suffix(z); /* call main_suffix, line 86 */ + { int m2 = z->l - z->c; (void)m2; /* do, line 88 */ + { int ret = r_main_suffix(z); /* call main_suffix, line 88 */ if (ret == 0) goto lab1; if (ret < 0) return ret; } lab1: z->c = z->l - m2; } - { int m3 = z->l - z->c; (void)m3; /* do, line 87 */ - { int ret = r_consonant_pair(z); /* call consonant_pair, line 87 */ + { int m3 = z->l - z->c; (void)m3; /* do, line 89 */ + { int ret = r_consonant_pair(z); /* call consonant_pair, line 89 */ if (ret == 0) goto lab2; if (ret < 0) return ret; } lab2: z->c = z->l - m3; } - { int m4 = z->l - z->c; (void)m4; /* do, line 88 */ - { int ret = r_other_suffix(z); /* call other_suffix, line 88 */ + { int m4 = z->l - z->c; (void)m4; /* do, line 90 */ + { int ret = r_other_suffix(z); /* call other_suffix, line 90 */ if (ret == 0) goto lab3; if (ret < 0) return ret; } lab3: z->c = z->l - m4; } - { int m5 = z->l - z->c; (void)m5; /* do, line 89 */ - { int ret = r_undouble(z); /* call undouble, line 89 */ + { int m5 = z->l - z->c; (void)m5; /* do, line 91 */ + { int ret = r_undouble(z); /* call undouble, line 91 */ if (ret == 0) goto lab4; if (ret < 0) return ret; } diff --git a/src/backend/snowball/libstemmer/stem_UTF_8_french.c b/src/backend/snowball/libstemmer/stem_UTF_8_french.c index 80564a8e36f4..9e12865c6789 100644 --- a/src/backend/snowball/libstemmer/stem_UTF_8_french.c +++ b/src/backend/snowball/libstemmer/stem_UTF_8_french.c @@ -45,16 +45,22 @@ static const struct among a_0[3] = /* 2 */ { 3, s_0_2, -1, -1, 0} }; -static const symbol s_1_1[1] = { 'I' }; -static const symbol s_1_2[1] = { 'U' }; -static const symbol s_1_3[1] = { 'Y' }; +static const symbol s_1_1[1] = { 'H' }; +static const symbol s_1_2[2] = { 'H', 'e' }; +static const symbol s_1_3[2] = { 'H', 'i' }; +static const symbol s_1_4[1] = { 'I' }; +static const symbol s_1_5[1] = { 'U' }; +static const symbol s_1_6[1] = { 'Y' }; -static const struct among a_1[4] = +static const struct among a_1[7] = { -/* 0 */ { 0, 0, -1, 4, 0}, -/* 1 */ { 1, s_1_1, 0, 1, 0}, -/* 2 */ { 1, s_1_2, 0, 2, 0}, -/* 3 */ { 1, s_1_3, 0, 3, 0} +/* 0 */ { 0, 0, -1, 7, 0}, +/* 1 */ { 1, s_1_1, 0, 6, 0}, +/* 2 */ { 2, s_1_2, 1, 4, 0}, +/* 3 */ { 2, s_1_3, 1, 5, 0}, +/* 4 */ { 1, s_1_4, 0, 1, 0}, +/* 5 */ { 1, s_1_5, 0, 2, 0}, +/* 6 */ { 1, s_1_6, 0, 3, 0} }; static const symbol s_2_0[3] = { 'i', 'q', 'U' }; @@ -338,17 +344,15 @@ static const symbol s_7_2[5] = { 'i', 0xC3, 0xA8, 'r', 'e' }; static const symbol s_7_3[3] = { 'i', 'o', 'n' }; static const symbol s_7_4[3] = { 'I', 'e', 'r' }; static const symbol s_7_5[3] = { 'i', 'e', 'r' }; -static const symbol s_7_6[2] = { 0xC3, 0xAB }; -static const struct among a_7[7] = +static const struct among a_7[6] = { /* 0 */ { 1, s_7_0, -1, 3, 0}, /* 1 */ { 5, s_7_1, 0, 2, 0}, /* 2 */ { 5, s_7_2, 0, 2, 0}, /* 3 */ { 3, s_7_3, -1, 1, 0}, /* 4 */ { 3, s_7_4, -1, 2, 0}, -/* 5 */ { 3, s_7_5, -1, 2, 0}, -/* 6 */ { 2, s_7_6, -1, 4, 0} +/* 5 */ { 3, s_7_5, -1, 2, 0} }; static const symbol s_8_0[3] = { 'e', 'l', 'l' }; @@ -373,37 +377,43 @@ static const unsigned char g_keep_with_s[] = { 1, 65, 20, 0, 0, 0, 0, 0, 0, 0, 0 static const symbol s_0[] = { 'U' }; static const symbol s_1[] = { 'I' }; static const symbol s_2[] = { 'Y' }; -static const symbol s_3[] = { 'Y' }; -static const symbol s_4[] = { 'U' }; -static const symbol s_5[] = { 'i' }; -static const symbol s_6[] = { 'u' }; -static const symbol s_7[] = { 'y' }; -static const symbol s_8[] = { 'i', 'c' }; -static const symbol s_9[] = { 'i', 'q', 'U' }; -static const symbol s_10[] = { 'l', 'o', 'g' }; -static const symbol s_11[] = { 'u' }; -static const symbol s_12[] = { 'e', 'n', 't' }; -static const symbol s_13[] = { 'a', 't' }; -static const symbol s_14[] = { 'e', 'u', 'x' }; -static const symbol s_15[] = { 'i' }; -static const symbol s_16[] = { 'a', 'b', 'l' }; -static const symbol s_17[] = { 'i', 'q', 'U' }; -static const symbol s_18[] = { 'a', 't' }; -static const symbol s_19[] = { 'i', 'c' }; -static const symbol s_20[] = { 'i', 'q', 'U' }; -static const symbol s_21[] = { 'e', 'a', 'u' }; -static const symbol s_22[] = { 'a', 'l' }; -static const symbol s_23[] = { 'e', 'u', 'x' }; -static const symbol s_24[] = { 'a', 'n', 't' }; -static const symbol s_25[] = { 'e', 'n', 't' }; -static const symbol s_26[] = { 'i' }; -static const symbol s_27[] = { 'g', 'u' }; -static const symbol s_28[] = { 0xC3, 0xA9 }; -static const symbol s_29[] = { 0xC3, 0xA8 }; -static const symbol s_30[] = { 'e' }; -static const symbol s_31[] = { 'i' }; -static const symbol s_32[] = { 0xC3, 0xA7 }; -static const symbol s_33[] = { 'c' }; +static const symbol s_3[] = { 0xC3, 0xAB }; +static const symbol s_4[] = { 'H', 'e' }; +static const symbol s_5[] = { 0xC3, 0xAF }; +static const symbol s_6[] = { 'H', 'i' }; +static const symbol s_7[] = { 'Y' }; +static const symbol s_8[] = { 'U' }; +static const symbol s_9[] = { 'i' }; +static const symbol s_10[] = { 'u' }; +static const symbol s_11[] = { 'y' }; +static const symbol s_12[] = { 0xC3, 0xAB }; +static const symbol s_13[] = { 0xC3, 0xAF }; +static const symbol s_14[] = { 'i', 'c' }; +static const symbol s_15[] = { 'i', 'q', 'U' }; +static const symbol s_16[] = { 'l', 'o', 'g' }; +static const symbol s_17[] = { 'u' }; +static const symbol s_18[] = { 'e', 'n', 't' }; +static const symbol s_19[] = { 'a', 't' }; +static const symbol s_20[] = { 'e', 'u', 'x' }; +static const symbol s_21[] = { 'i' }; +static const symbol s_22[] = { 'a', 'b', 'l' }; +static const symbol s_23[] = { 'i', 'q', 'U' }; +static const symbol s_24[] = { 'a', 't' }; +static const symbol s_25[] = { 'i', 'c' }; +static const symbol s_26[] = { 'i', 'q', 'U' }; +static const symbol s_27[] = { 'e', 'a', 'u' }; +static const symbol s_28[] = { 'a', 'l' }; +static const symbol s_29[] = { 'e', 'u', 'x' }; +static const symbol s_30[] = { 'a', 'n', 't' }; +static const symbol s_31[] = { 'e', 'n', 't' }; +static const symbol s_32[] = { 'H', 'i' }; +static const symbol s_33[] = { 'i' }; +static const symbol s_34[] = { 0xC3, 0xA9 }; +static const symbol s_35[] = { 0xC3, 0xA8 }; +static const symbol s_36[] = { 'e' }; +static const symbol s_37[] = { 'i' }; +static const symbol s_38[] = { 0xC3, 0xA7 }; +static const symbol s_39[] = { 'c' }; static int r_prelude(struct SN_env * z) { /* forwardmode */ while(1) { /* repeat, line 38 */ @@ -446,23 +456,41 @@ static int r_prelude(struct SN_env * z) { /* forwardmode */ lab3: z->c = c3; z->bra = z->c; /* [, line 45 */ - if (z->c == z->l || z->p[z->c] != 'y') goto lab7; /* literal, line 45 */ - z->c++; + if (!(eq_s(z, 2, s_3))) goto lab7; /* literal, line 45 */ z->ket = z->c; /* ], line 45 */ - if (in_grouping_U(z, g_v, 97, 251, 0)) goto lab7; /* grouping v, line 45 */ - { int ret = slice_from_s(z, 1, s_3); /* <-, line 45 */ + { int ret = slice_from_s(z, 2, s_4); /* <-, line 45 */ if (ret < 0) return ret; } goto lab2; lab7: z->c = c3; - if (z->c == z->l || z->p[z->c] != 'q') goto lab1; /* literal, line 47 */ - z->c++; z->bra = z->c; /* [, line 47 */ - if (z->c == z->l || z->p[z->c] != 'u') goto lab1; /* literal, line 47 */ - z->c++; + if (!(eq_s(z, 2, s_5))) goto lab8; /* literal, line 47 */ z->ket = z->c; /* ], line 47 */ - { int ret = slice_from_s(z, 1, s_4); /* <-, line 47 */ + { int ret = slice_from_s(z, 2, s_6); /* <-, line 47 */ + if (ret < 0) return ret; + } + goto lab2; + lab8: + z->c = c3; + z->bra = z->c; /* [, line 49 */ + if (z->c == z->l || z->p[z->c] != 'y') goto lab9; /* literal, line 49 */ + z->c++; + z->ket = z->c; /* ], line 49 */ + if (in_grouping_U(z, g_v, 97, 251, 0)) goto lab9; /* grouping v, line 49 */ + { int ret = slice_from_s(z, 1, s_7); /* <-, line 49 */ + if (ret < 0) return ret; + } + goto lab2; + lab9: + z->c = c3; + if (z->c == z->l || z->p[z->c] != 'q') goto lab1; /* literal, line 51 */ + z->c++; + z->bra = z->c; /* [, line 51 */ + if (z->c == z->l || z->p[z->c] != 'u') goto lab1; /* literal, line 51 */ + z->c++; + z->ket = z->c; /* ], line 51 */ + { int ret = slice_from_s(z, 1, s_8); /* <-, line 51 */ if (ret < 0) return ret; } } @@ -485,63 +513,63 @@ static int r_prelude(struct SN_env * z) { /* forwardmode */ } static int r_mark_regions(struct SN_env * z) { /* forwardmode */ - z->I[0] = z->l; /* $pV = , line 52 */ - z->I[1] = z->l; /* $p1 = , line 53 */ - z->I[2] = z->l; /* $p2 = , line 54 */ - { int c1 = z->c; /* do, line 56 */ - { int c2 = z->c; /* or, line 58 */ - if (in_grouping_U(z, g_v, 97, 251, 0)) goto lab2; /* grouping v, line 57 */ - if (in_grouping_U(z, g_v, 97, 251, 0)) goto lab2; /* grouping v, line 57 */ + z->I[0] = z->l; /* $pV = , line 56 */ + z->I[1] = z->l; /* $p1 = , line 57 */ + z->I[2] = z->l; /* $p2 = , line 58 */ + { int c1 = z->c; /* do, line 60 */ + { int c2 = z->c; /* or, line 62 */ + if (in_grouping_U(z, g_v, 97, 251, 0)) goto lab2; /* grouping v, line 61 */ + if (in_grouping_U(z, g_v, 97, 251, 0)) goto lab2; /* grouping v, line 61 */ { int ret = skip_utf8(z->p, z->c, 0, z->l, 1); if (ret < 0) goto lab2; - z->c = ret; /* next, line 57 */ + z->c = ret; /* next, line 61 */ } goto lab1; lab2: z->c = c2; - if (z->c + 2 >= z->l || z->p[z->c + 2] >> 5 != 3 || !((331776 >> (z->p[z->c + 2] & 0x1f)) & 1)) goto lab3; /* among, line 59 */ + if (z->c + 2 >= z->l || z->p[z->c + 2] >> 5 != 3 || !((331776 >> (z->p[z->c + 2] & 0x1f)) & 1)) goto lab3; /* among, line 63 */ if (!(find_among(z, a_0, 3))) goto lab3; goto lab1; lab3: z->c = c2; { int ret = skip_utf8(z->p, z->c, 0, z->l, 1); if (ret < 0) goto lab0; - z->c = ret; /* next, line 66 */ + z->c = ret; /* next, line 70 */ } - { /* gopast */ /* grouping v, line 66 */ + { /* gopast */ /* grouping v, line 70 */ int ret = out_grouping_U(z, g_v, 97, 251, 1); if (ret < 0) goto lab0; z->c += ret; } } lab1: - z->I[0] = z->c; /* setmark pV, line 67 */ + z->I[0] = z->c; /* setmark pV, line 71 */ lab0: z->c = c1; } - { int c3 = z->c; /* do, line 69 */ - { /* gopast */ /* grouping v, line 70 */ + { int c3 = z->c; /* do, line 73 */ + { /* gopast */ /* grouping v, line 74 */ int ret = out_grouping_U(z, g_v, 97, 251, 1); if (ret < 0) goto lab4; z->c += ret; } - { /* gopast */ /* non v, line 70 */ + { /* gopast */ /* non v, line 74 */ int ret = in_grouping_U(z, g_v, 97, 251, 1); if (ret < 0) goto lab4; z->c += ret; } - z->I[1] = z->c; /* setmark p1, line 70 */ - { /* gopast */ /* grouping v, line 71 */ + z->I[1] = z->c; /* setmark p1, line 74 */ + { /* gopast */ /* grouping v, line 75 */ int ret = out_grouping_U(z, g_v, 97, 251, 1); if (ret < 0) goto lab4; z->c += ret; } - { /* gopast */ /* non v, line 71 */ + { /* gopast */ /* non v, line 75 */ int ret = in_grouping_U(z, g_v, 97, 251, 1); if (ret < 0) goto lab4; z->c += ret; } - z->I[2] = z->c; /* setmark p2, line 71 */ + z->I[2] = z->c; /* setmark p2, line 75 */ lab4: z->c = c3; } @@ -550,33 +578,48 @@ static int r_mark_regions(struct SN_env * z) { /* forwardmode */ static int r_postlude(struct SN_env * z) { /* forwardmode */ int among_var; - while(1) { /* repeat, line 75 */ + while(1) { /* repeat, line 79 */ int c1 = z->c; - z->bra = z->c; /* [, line 77 */ - if (z->c >= z->l || z->p[z->c + 0] >> 5 != 2 || !((35652096 >> (z->p[z->c + 0] & 0x1f)) & 1)) among_var = 4; else /* substring, line 77 */ - among_var = find_among(z, a_1, 4); + z->bra = z->c; /* [, line 81 */ + if (z->c >= z->l || z->p[z->c + 0] >> 5 != 2 || !((35652352 >> (z->p[z->c + 0] & 0x1f)) & 1)) among_var = 7; else /* substring, line 81 */ + among_var = find_among(z, a_1, 7); if (!(among_var)) goto lab0; - z->ket = z->c; /* ], line 77 */ - switch (among_var) { /* among, line 77 */ + z->ket = z->c; /* ], line 81 */ + switch (among_var) { /* among, line 81 */ case 1: - { int ret = slice_from_s(z, 1, s_5); /* <-, line 78 */ + { int ret = slice_from_s(z, 1, s_9); /* <-, line 82 */ if (ret < 0) return ret; } break; case 2: - { int ret = slice_from_s(z, 1, s_6); /* <-, line 79 */ + { int ret = slice_from_s(z, 1, s_10); /* <-, line 83 */ if (ret < 0) return ret; } break; case 3: - { int ret = slice_from_s(z, 1, s_7); /* <-, line 80 */ + { int ret = slice_from_s(z, 1, s_11); /* <-, line 84 */ if (ret < 0) return ret; } break; case 4: + { int ret = slice_from_s(z, 2, s_12); /* <-, line 85 */ + if (ret < 0) return ret; + } + break; + case 5: + { int ret = slice_from_s(z, 2, s_13); /* <-, line 86 */ + if (ret < 0) return ret; + } + break; + case 6: + { int ret = slice_del(z); /* delete, line 87 */ + if (ret < 0) return ret; + } + break; + case 7: { int ret = skip_utf8(z->p, z->c, 0, z->l, 1); if (ret < 0) goto lab0; - z->c = ret; /* next, line 81 */ + z->c = ret; /* next, line 88 */ } break; } @@ -589,58 +632,58 @@ static int r_postlude(struct SN_env * z) { /* forwardmode */ } static int r_RV(struct SN_env * z) { /* backwardmode */ - if (!(z->I[0] <= z->c)) return 0; /* $( <= ), line 87 */ + if (!(z->I[0] <= z->c)) return 0; /* $( <= ), line 94 */ return 1; } static int r_R1(struct SN_env * z) { /* backwardmode */ - if (!(z->I[1] <= z->c)) return 0; /* $( <= ), line 88 */ + if (!(z->I[1] <= z->c)) return 0; /* $( <= ), line 95 */ return 1; } static int r_R2(struct SN_env * z) { /* backwardmode */ - if (!(z->I[2] <= z->c)) return 0; /* $( <= ), line 89 */ + if (!(z->I[2] <= z->c)) return 0; /* $( <= ), line 96 */ return 1; } static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - z->ket = z->c; /* [, line 92 */ - among_var = find_among_b(z, a_4, 43); /* substring, line 92 */ + z->ket = z->c; /* [, line 99 */ + among_var = find_among_b(z, a_4, 43); /* substring, line 99 */ if (!(among_var)) return 0; - z->bra = z->c; /* ], line 92 */ - switch (among_var) { /* among, line 92 */ + z->bra = z->c; /* ], line 99 */ + switch (among_var) { /* among, line 99 */ case 1: - { int ret = r_R2(z); /* call R2, line 96 */ + { int ret = r_R2(z); /* call R2, line 103 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 96 */ + { int ret = slice_del(z); /* delete, line 103 */ if (ret < 0) return ret; } break; case 2: - { int ret = r_R2(z); /* call R2, line 99 */ + { int ret = r_R2(z); /* call R2, line 106 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 99 */ + { int ret = slice_del(z); /* delete, line 106 */ if (ret < 0) return ret; } - { int m1 = z->l - z->c; (void)m1; /* try, line 100 */ - z->ket = z->c; /* [, line 100 */ - if (!(eq_s_b(z, 2, s_8))) { z->c = z->l - m1; goto lab0; } /* literal, line 100 */ - z->bra = z->c; /* ], line 100 */ - { int m2 = z->l - z->c; (void)m2; /* or, line 100 */ - { int ret = r_R2(z); /* call R2, line 100 */ + { int m1 = z->l - z->c; (void)m1; /* try, line 107 */ + z->ket = z->c; /* [, line 107 */ + if (!(eq_s_b(z, 2, s_14))) { z->c = z->l - m1; goto lab0; } /* literal, line 107 */ + z->bra = z->c; /* ], line 107 */ + { int m2 = z->l - z->c; (void)m2; /* or, line 107 */ + { int ret = r_R2(z); /* call R2, line 107 */ if (ret == 0) goto lab2; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 100 */ + { int ret = slice_del(z); /* delete, line 107 */ if (ret < 0) return ret; } goto lab1; lab2: z->c = z->l - m2; - { int ret = slice_from_s(z, 3, s_9); /* <-, line 100 */ + { int ret = slice_from_s(z, 3, s_15); /* <-, line 107 */ if (ret < 0) return ret; } } @@ -650,98 +693,98 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ } break; case 3: - { int ret = r_R2(z); /* call R2, line 104 */ + { int ret = r_R2(z); /* call R2, line 111 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_10); /* <-, line 104 */ + { int ret = slice_from_s(z, 3, s_16); /* <-, line 111 */ if (ret < 0) return ret; } break; case 4: - { int ret = r_R2(z); /* call R2, line 107 */ + { int ret = r_R2(z); /* call R2, line 114 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 1, s_11); /* <-, line 107 */ + { int ret = slice_from_s(z, 1, s_17); /* <-, line 114 */ if (ret < 0) return ret; } break; case 5: - { int ret = r_R2(z); /* call R2, line 110 */ + { int ret = r_R2(z); /* call R2, line 117 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_12); /* <-, line 110 */ + { int ret = slice_from_s(z, 3, s_18); /* <-, line 117 */ if (ret < 0) return ret; } break; case 6: - { int ret = r_RV(z); /* call RV, line 114 */ + { int ret = r_RV(z); /* call RV, line 121 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 114 */ + { int ret = slice_del(z); /* delete, line 121 */ if (ret < 0) return ret; } - { int m3 = z->l - z->c; (void)m3; /* try, line 115 */ - z->ket = z->c; /* [, line 116 */ - among_var = find_among_b(z, a_2, 6); /* substring, line 116 */ + { int m3 = z->l - z->c; (void)m3; /* try, line 122 */ + z->ket = z->c; /* [, line 123 */ + among_var = find_among_b(z, a_2, 6); /* substring, line 123 */ if (!(among_var)) { z->c = z->l - m3; goto lab3; } - z->bra = z->c; /* ], line 116 */ - switch (among_var) { /* among, line 116 */ + z->bra = z->c; /* ], line 123 */ + switch (among_var) { /* among, line 123 */ case 1: - { int ret = r_R2(z); /* call R2, line 117 */ + { int ret = r_R2(z); /* call R2, line 124 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 117 */ + { int ret = slice_del(z); /* delete, line 124 */ if (ret < 0) return ret; } - z->ket = z->c; /* [, line 117 */ - if (!(eq_s_b(z, 2, s_13))) { z->c = z->l - m3; goto lab3; } /* literal, line 117 */ - z->bra = z->c; /* ], line 117 */ - { int ret = r_R2(z); /* call R2, line 117 */ + z->ket = z->c; /* [, line 124 */ + if (!(eq_s_b(z, 2, s_19))) { z->c = z->l - m3; goto lab3; } /* literal, line 124 */ + z->bra = z->c; /* ], line 124 */ + { int ret = r_R2(z); /* call R2, line 124 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 117 */ + { int ret = slice_del(z); /* delete, line 124 */ if (ret < 0) return ret; } break; case 2: - { int m4 = z->l - z->c; (void)m4; /* or, line 118 */ - { int ret = r_R2(z); /* call R2, line 118 */ + { int m4 = z->l - z->c; (void)m4; /* or, line 125 */ + { int ret = r_R2(z); /* call R2, line 125 */ if (ret == 0) goto lab5; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 118 */ + { int ret = slice_del(z); /* delete, line 125 */ if (ret < 0) return ret; } goto lab4; lab5: z->c = z->l - m4; - { int ret = r_R1(z); /* call R1, line 118 */ + { int ret = r_R1(z); /* call R1, line 125 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_from_s(z, 3, s_14); /* <-, line 118 */ + { int ret = slice_from_s(z, 3, s_20); /* <-, line 125 */ if (ret < 0) return ret; } } lab4: break; case 3: - { int ret = r_R2(z); /* call R2, line 120 */ + { int ret = r_R2(z); /* call R2, line 127 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 120 */ + { int ret = slice_del(z); /* delete, line 127 */ if (ret < 0) return ret; } break; case 4: - { int ret = r_RV(z); /* call RV, line 122 */ + { int ret = r_RV(z); /* call RV, line 129 */ if (ret == 0) { z->c = z->l - m3; goto lab3; } if (ret < 0) return ret; } - { int ret = slice_from_s(z, 1, s_15); /* <-, line 122 */ + { int ret = slice_from_s(z, 1, s_21); /* <-, line 129 */ if (ret < 0) return ret; } break; @@ -751,61 +794,61 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ } break; case 7: - { int ret = r_R2(z); /* call R2, line 129 */ + { int ret = r_R2(z); /* call R2, line 136 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 129 */ + { int ret = slice_del(z); /* delete, line 136 */ if (ret < 0) return ret; } - { int m5 = z->l - z->c; (void)m5; /* try, line 130 */ - z->ket = z->c; /* [, line 131 */ - if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((4198408 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->c = z->l - m5; goto lab6; } /* substring, line 131 */ + { int m5 = z->l - z->c; (void)m5; /* try, line 137 */ + z->ket = z->c; /* [, line 138 */ + if (z->c - 1 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((4198408 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->c = z->l - m5; goto lab6; } /* substring, line 138 */ among_var = find_among_b(z, a_3, 3); if (!(among_var)) { z->c = z->l - m5; goto lab6; } - z->bra = z->c; /* ], line 131 */ - switch (among_var) { /* among, line 131 */ + z->bra = z->c; /* ], line 138 */ + switch (among_var) { /* among, line 138 */ case 1: - { int m6 = z->l - z->c; (void)m6; /* or, line 132 */ - { int ret = r_R2(z); /* call R2, line 132 */ + { int m6 = z->l - z->c; (void)m6; /* or, line 139 */ + { int ret = r_R2(z); /* call R2, line 139 */ if (ret == 0) goto lab8; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 132 */ + { int ret = slice_del(z); /* delete, line 139 */ if (ret < 0) return ret; } goto lab7; lab8: z->c = z->l - m6; - { int ret = slice_from_s(z, 3, s_16); /* <-, line 132 */ + { int ret = slice_from_s(z, 3, s_22); /* <-, line 139 */ if (ret < 0) return ret; } } lab7: break; case 2: - { int m7 = z->l - z->c; (void)m7; /* or, line 133 */ - { int ret = r_R2(z); /* call R2, line 133 */ + { int m7 = z->l - z->c; (void)m7; /* or, line 140 */ + { int ret = r_R2(z); /* call R2, line 140 */ if (ret == 0) goto lab10; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 133 */ + { int ret = slice_del(z); /* delete, line 140 */ if (ret < 0) return ret; } goto lab9; lab10: z->c = z->l - m7; - { int ret = slice_from_s(z, 3, s_17); /* <-, line 133 */ + { int ret = slice_from_s(z, 3, s_23); /* <-, line 140 */ if (ret < 0) return ret; } } lab9: break; case 3: - { int ret = r_R2(z); /* call R2, line 134 */ + { int ret = r_R2(z); /* call R2, line 141 */ if (ret == 0) { z->c = z->l - m5; goto lab6; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 134 */ + { int ret = slice_del(z); /* delete, line 141 */ if (ret < 0) return ret; } break; @@ -815,38 +858,38 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ } break; case 8: - { int ret = r_R2(z); /* call R2, line 141 */ + { int ret = r_R2(z); /* call R2, line 148 */ if (ret <= 0) return ret; } - { int ret = slice_del(z); /* delete, line 141 */ + { int ret = slice_del(z); /* delete, line 148 */ if (ret < 0) return ret; } - { int m8 = z->l - z->c; (void)m8; /* try, line 142 */ - z->ket = z->c; /* [, line 142 */ - if (!(eq_s_b(z, 2, s_18))) { z->c = z->l - m8; goto lab11; } /* literal, line 142 */ - z->bra = z->c; /* ], line 142 */ - { int ret = r_R2(z); /* call R2, line 142 */ + { int m8 = z->l - z->c; (void)m8; /* try, line 149 */ + z->ket = z->c; /* [, line 149 */ + if (!(eq_s_b(z, 2, s_24))) { z->c = z->l - m8; goto lab11; } /* literal, line 149 */ + z->bra = z->c; /* ], line 149 */ + { int ret = r_R2(z); /* call R2, line 149 */ if (ret == 0) { z->c = z->l - m8; goto lab11; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 142 */ + { int ret = slice_del(z); /* delete, line 149 */ if (ret < 0) return ret; } - z->ket = z->c; /* [, line 142 */ - if (!(eq_s_b(z, 2, s_19))) { z->c = z->l - m8; goto lab11; } /* literal, line 142 */ - z->bra = z->c; /* ], line 142 */ - { int m9 = z->l - z->c; (void)m9; /* or, line 142 */ - { int ret = r_R2(z); /* call R2, line 142 */ + z->ket = z->c; /* [, line 149 */ + if (!(eq_s_b(z, 2, s_25))) { z->c = z->l - m8; goto lab11; } /* literal, line 149 */ + z->bra = z->c; /* ], line 149 */ + { int m9 = z->l - z->c; (void)m9; /* or, line 149 */ + { int ret = r_R2(z); /* call R2, line 149 */ if (ret == 0) goto lab13; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 142 */ + { int ret = slice_del(z); /* delete, line 149 */ if (ret < 0) return ret; } goto lab12; lab13: z->c = z->l - m9; - { int ret = slice_from_s(z, 3, s_20); /* <-, line 142 */ + { int ret = slice_from_s(z, 3, s_26); /* <-, line 149 */ if (ret < 0) return ret; } } @@ -856,78 +899,78 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ } break; case 9: - { int ret = slice_from_s(z, 3, s_21); /* <-, line 144 */ + { int ret = slice_from_s(z, 3, s_27); /* <-, line 151 */ if (ret < 0) return ret; } break; case 10: - { int ret = r_R1(z); /* call R1, line 145 */ + { int ret = r_R1(z); /* call R1, line 152 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 2, s_22); /* <-, line 145 */ + { int ret = slice_from_s(z, 2, s_28); /* <-, line 152 */ if (ret < 0) return ret; } break; case 11: - { int m10 = z->l - z->c; (void)m10; /* or, line 147 */ - { int ret = r_R2(z); /* call R2, line 147 */ + { int m10 = z->l - z->c; (void)m10; /* or, line 154 */ + { int ret = r_R2(z); /* call R2, line 154 */ if (ret == 0) goto lab15; if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 147 */ + { int ret = slice_del(z); /* delete, line 154 */ if (ret < 0) return ret; } goto lab14; lab15: z->c = z->l - m10; - { int ret = r_R1(z); /* call R1, line 147 */ + { int ret = r_R1(z); /* call R1, line 154 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_23); /* <-, line 147 */ + { int ret = slice_from_s(z, 3, s_29); /* <-, line 154 */ if (ret < 0) return ret; } } lab14: break; case 12: - { int ret = r_R1(z); /* call R1, line 150 */ + { int ret = r_R1(z); /* call R1, line 157 */ if (ret <= 0) return ret; } - if (out_grouping_b_U(z, g_v, 97, 251, 0)) return 0; /* non v, line 150 */ - { int ret = slice_del(z); /* delete, line 150 */ + if (out_grouping_b_U(z, g_v, 97, 251, 0)) return 0; /* non v, line 157 */ + { int ret = slice_del(z); /* delete, line 157 */ if (ret < 0) return ret; } break; case 13: - { int ret = r_RV(z); /* call RV, line 155 */ + { int ret = r_RV(z); /* call RV, line 162 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_24); /* <-, line 155 */ + { int ret = slice_from_s(z, 3, s_30); /* <-, line 162 */ if (ret < 0) return ret; } - return 0; /* fail, line 155 */ + return 0; /* fail, line 162 */ break; case 14: - { int ret = r_RV(z); /* call RV, line 156 */ + { int ret = r_RV(z); /* call RV, line 163 */ if (ret <= 0) return ret; } - { int ret = slice_from_s(z, 3, s_25); /* <-, line 156 */ + { int ret = slice_from_s(z, 3, s_31); /* <-, line 163 */ if (ret < 0) return ret; } - return 0; /* fail, line 156 */ + return 0; /* fail, line 163 */ break; case 15: - { int m_test11 = z->l - z->c; /* test, line 158 */ - if (in_grouping_b_U(z, g_v, 97, 251, 0)) return 0; /* grouping v, line 158 */ - { int ret = r_RV(z); /* call RV, line 158 */ + { int m_test11 = z->l - z->c; /* test, line 165 */ + if (in_grouping_b_U(z, g_v, 97, 251, 0)) return 0; /* grouping v, line 165 */ + { int ret = r_RV(z); /* call RV, line 165 */ if (ret <= 0) return ret; } z->c = z->l - m_test11; } - { int ret = slice_del(z); /* delete, line 158 */ + { int ret = slice_del(z); /* delete, line 165 */ if (ret < 0) return ret; } - return 0; /* fail, line 158 */ + return 0; /* fail, line 165 */ break; } return 1; @@ -935,15 +978,22 @@ static int r_standard_suffix(struct SN_env * z) { /* backwardmode */ static int r_i_verb_suffix(struct SN_env * z) { /* backwardmode */ - { int mlimit1; /* setlimit, line 163 */ + { int mlimit1; /* setlimit, line 170 */ if (z->c < z->I[0]) return 0; mlimit1 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 164 */ - if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((68944418 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit1; return 0; } /* substring, line 164 */ + z->ket = z->c; /* [, line 171 */ + if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((68944418 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit1; return 0; } /* substring, line 171 */ if (!(find_among_b(z, a_5, 35))) { z->lb = mlimit1; return 0; } - z->bra = z->c; /* ], line 164 */ - if (out_grouping_b_U(z, g_v, 97, 251, 0)) { z->lb = mlimit1; return 0; } /* non v, line 170 */ - { int ret = slice_del(z); /* delete, line 170 */ + z->bra = z->c; /* ], line 171 */ + { int m2 = z->l - z->c; (void)m2; /* not, line 177 */ + if (z->c <= z->lb || z->p[z->c - 1] != 'H') goto lab0; /* literal, line 177 */ + z->c--; + { z->lb = mlimit1; return 0; } + lab0: + z->c = z->l - m2; + } + if (out_grouping_b_U(z, g_v, 97, 251, 0)) { z->lb = mlimit1; return 0; } /* non v, line 177 */ + { int ret = slice_del(z); /* delete, line 177 */ if (ret < 0) return ret; } z->lb = mlimit1; @@ -954,38 +1004,38 @@ static int r_i_verb_suffix(struct SN_env * z) { /* backwardmode */ static int r_verb_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - { int mlimit1; /* setlimit, line 174 */ + { int mlimit1; /* setlimit, line 181 */ if (z->c < z->I[0]) return 0; mlimit1 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 175 */ - among_var = find_among_b(z, a_6, 38); /* substring, line 175 */ + z->ket = z->c; /* [, line 182 */ + among_var = find_among_b(z, a_6, 38); /* substring, line 182 */ if (!(among_var)) { z->lb = mlimit1; return 0; } - z->bra = z->c; /* ], line 175 */ - switch (among_var) { /* among, line 175 */ + z->bra = z->c; /* ], line 182 */ + switch (among_var) { /* among, line 182 */ case 1: - { int ret = r_R2(z); /* call R2, line 177 */ + { int ret = r_R2(z); /* call R2, line 184 */ if (ret == 0) { z->lb = mlimit1; return 0; } if (ret < 0) return ret; } - { int ret = slice_del(z); /* delete, line 177 */ + { int ret = slice_del(z); /* delete, line 184 */ if (ret < 0) return ret; } break; case 2: - { int ret = slice_del(z); /* delete, line 185 */ + { int ret = slice_del(z); /* delete, line 192 */ if (ret < 0) return ret; } break; case 3: - { int ret = slice_del(z); /* delete, line 190 */ + { int ret = slice_del(z); /* delete, line 197 */ if (ret < 0) return ret; } - { int m2 = z->l - z->c; (void)m2; /* try, line 191 */ - z->ket = z->c; /* [, line 191 */ - if (z->c <= z->lb || z->p[z->c - 1] != 'e') { z->c = z->l - m2; goto lab0; } /* literal, line 191 */ + { int m2 = z->l - z->c; (void)m2; /* try, line 198 */ + z->ket = z->c; /* [, line 198 */ + if (z->c <= z->lb || z->p[z->c - 1] != 'e') { z->c = z->l - m2; goto lab0; } /* literal, line 198 */ z->c--; - z->bra = z->c; /* ], line 191 */ - { int ret = slice_del(z); /* delete, line 191 */ + z->bra = z->c; /* ], line 198 */ + { int ret = slice_del(z); /* delete, line 198 */ if (ret < 0) return ret; } lab0: @@ -1000,84 +1050,86 @@ static int r_verb_suffix(struct SN_env * z) { /* backwardmode */ static int r_residual_suffix(struct SN_env * z) { /* backwardmode */ int among_var; - { int m1 = z->l - z->c; (void)m1; /* try, line 199 */ - z->ket = z->c; /* [, line 199 */ - if (z->c <= z->lb || z->p[z->c - 1] != 's') { z->c = z->l - m1; goto lab0; } /* literal, line 199 */ + { int m1 = z->l - z->c; (void)m1; /* try, line 206 */ + z->ket = z->c; /* [, line 206 */ + if (z->c <= z->lb || z->p[z->c - 1] != 's') { z->c = z->l - m1; goto lab0; } /* literal, line 206 */ z->c--; - z->bra = z->c; /* ], line 199 */ - { int m_test2 = z->l - z->c; /* test, line 199 */ - if (out_grouping_b_U(z, g_keep_with_s, 97, 232, 0)) { z->c = z->l - m1; goto lab0; } /* non keep_with_s, line 199 */ + z->bra = z->c; /* ], line 206 */ + { int m_test2 = z->l - z->c; /* test, line 206 */ + { int m3 = z->l - z->c; (void)m3; /* or, line 206 */ + if (!(eq_s_b(z, 2, s_32))) goto lab2; /* literal, line 206 */ + goto lab1; + lab2: + z->c = z->l - m3; + if (out_grouping_b_U(z, g_keep_with_s, 97, 232, 0)) { z->c = z->l - m1; goto lab0; } /* non keep_with_s, line 206 */ + } + lab1: z->c = z->l - m_test2; } - { int ret = slice_del(z); /* delete, line 199 */ + { int ret = slice_del(z); /* delete, line 206 */ if (ret < 0) return ret; } lab0: ; } - { int mlimit3; /* setlimit, line 200 */ + { int mlimit4; /* setlimit, line 207 */ if (z->c < z->I[0]) return 0; - mlimit3 = z->lb; z->lb = z->I[0]; - z->ket = z->c; /* [, line 201 */ - among_var = find_among_b(z, a_7, 7); /* substring, line 201 */ - if (!(among_var)) { z->lb = mlimit3; return 0; } - z->bra = z->c; /* ], line 201 */ - switch (among_var) { /* among, line 201 */ + mlimit4 = z->lb; z->lb = z->I[0]; + z->ket = z->c; /* [, line 208 */ + if (z->c <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((278560 >> (z->p[z->c - 1] & 0x1f)) & 1)) { z->lb = mlimit4; return 0; } /* substring, line 208 */ + among_var = find_among_b(z, a_7, 6); + if (!(among_var)) { z->lb = mlimit4; return 0; } + z->bra = z->c; /* ], line 208 */ + switch (among_var) { /* among, line 208 */ case 1: - { int ret = r_R2(z); /* call R2, line 202 */ - if (ret == 0) { z->lb = mlimit3; return 0; } + { int ret = r_R2(z); /* call R2, line 209 */ + if (ret == 0) { z->lb = mlimit4; return 0; } if (ret < 0) return ret; } - { int m4 = z->l - z->c; (void)m4; /* or, line 202 */ - if (z->c <= z->lb || z->p[z->c - 1] != 's') goto lab2; /* literal, line 202 */ + { int m5 = z->l - z->c; (void)m5; /* or, line 209 */ + if (z->c <= z->lb || z->p[z->c - 1] != 's') goto lab4; /* literal, line 209 */ z->c--; - goto lab1; - lab2: - z->c = z->l - m4; - if (z->c <= z->lb || z->p[z->c - 1] != 't') { z->lb = mlimit3; return 0; } /* literal, line 202 */ + goto lab3; + lab4: + z->c = z->l - m5; + if (z->c <= z->lb || z->p[z->c - 1] != 't') { z->lb = mlimit4; return 0; } /* literal, line 209 */ z->c--; } - lab1: - { int ret = slice_del(z); /* delete, line 202 */ + lab3: + { int ret = slice_del(z); /* delete, line 209 */ if (ret < 0) return ret; } break; case 2: - { int ret = slice_from_s(z, 1, s_26); /* <-, line 204 */ + { int ret = slice_from_s(z, 1, s_33); /* <-, line 211 */ if (ret < 0) return ret; } break; case 3: - { int ret = slice_del(z); /* delete, line 205 */ - if (ret < 0) return ret; - } - break; - case 4: - if (!(eq_s_b(z, 2, s_27))) { z->lb = mlimit3; return 0; } /* literal, line 206 */ - { int ret = slice_del(z); /* delete, line 206 */ + { int ret = slice_del(z); /* delete, line 212 */ if (ret < 0) return ret; } break; } - z->lb = mlimit3; + z->lb = mlimit4; } return 1; } static int r_un_double(struct SN_env * z) { /* backwardmode */ - { int m_test1 = z->l - z->c; /* test, line 212 */ - if (z->c - 2 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1069056 >> (z->p[z->c - 1] & 0x1f)) & 1)) return 0; /* among, line 212 */ + { int m_test1 = z->l - z->c; /* test, line 218 */ + if (z->c - 2 <= z->lb || z->p[z->c - 1] >> 5 != 3 || !((1069056 >> (z->p[z->c - 1] & 0x1f)) & 1)) return 0; /* among, line 218 */ if (!(find_among_b(z, a_8, 5))) return 0; z->c = z->l - m_test1; } - z->ket = z->c; /* [, line 212 */ + z->ket = z->c; /* [, line 218 */ { int ret = skip_utf8(z->p, z->c, z->lb, 0, -1); if (ret < 0) return 0; - z->c = ret; /* next, line 212 */ + z->c = ret; /* next, line 218 */ } - z->bra = z->c; /* ], line 212 */ - { int ret = slice_del(z); /* delete, line 212 */ + z->bra = z->c; /* ], line 218 */ + { int ret = slice_del(z); /* delete, line 218 */ if (ret < 0) return ret; } return 1; @@ -1085,8 +1137,8 @@ static int r_un_double(struct SN_env * z) { /* backwardmode */ static int r_un_accent(struct SN_env * z) { /* backwardmode */ { int i = 1; - while(1) { /* atleast, line 216 */ - if (out_grouping_b_U(z, g_v, 97, 251, 0)) goto lab0; /* non v, line 216 */ + while(1) { /* atleast, line 222 */ + if (out_grouping_b_U(z, g_v, 97, 251, 0)) goto lab0; /* non v, line 222 */ i--; continue; lab0: @@ -1094,79 +1146,79 @@ static int r_un_accent(struct SN_env * z) { /* backwardmode */ } if (i > 0) return 0; } - z->ket = z->c; /* [, line 217 */ - { int m1 = z->l - z->c; (void)m1; /* or, line 217 */ - if (!(eq_s_b(z, 2, s_28))) goto lab2; /* literal, line 217 */ + z->ket = z->c; /* [, line 223 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 223 */ + if (!(eq_s_b(z, 2, s_34))) goto lab2; /* literal, line 223 */ goto lab1; lab2: z->c = z->l - m1; - if (!(eq_s_b(z, 2, s_29))) return 0; /* literal, line 217 */ + if (!(eq_s_b(z, 2, s_35))) return 0; /* literal, line 223 */ } lab1: - z->bra = z->c; /* ], line 217 */ - { int ret = slice_from_s(z, 1, s_30); /* <-, line 217 */ + z->bra = z->c; /* ], line 223 */ + { int ret = slice_from_s(z, 1, s_36); /* <-, line 223 */ if (ret < 0) return ret; } return 1; } extern int french_UTF_8_stem(struct SN_env * z) { /* forwardmode */ - { int c1 = z->c; /* do, line 223 */ - { int ret = r_prelude(z); /* call prelude, line 223 */ + { int c1 = z->c; /* do, line 229 */ + { int ret = r_prelude(z); /* call prelude, line 229 */ if (ret == 0) goto lab0; if (ret < 0) return ret; } lab0: z->c = c1; } - /* do, line 224 */ - { int ret = r_mark_regions(z); /* call mark_regions, line 224 */ + /* do, line 230 */ + { int ret = r_mark_regions(z); /* call mark_regions, line 230 */ if (ret == 0) goto lab1; if (ret < 0) return ret; } lab1: - z->lb = z->c; z->c = z->l; /* backwards, line 225 */ + z->lb = z->c; z->c = z->l; /* backwards, line 231 */ - { int m2 = z->l - z->c; (void)m2; /* do, line 227 */ - { int m3 = z->l - z->c; (void)m3; /* or, line 237 */ - { int m4 = z->l - z->c; (void)m4; /* and, line 233 */ - { int m5 = z->l - z->c; (void)m5; /* or, line 229 */ - { int ret = r_standard_suffix(z); /* call standard_suffix, line 229 */ + { int m2 = z->l - z->c; (void)m2; /* do, line 233 */ + { int m3 = z->l - z->c; (void)m3; /* or, line 243 */ + { int m4 = z->l - z->c; (void)m4; /* and, line 239 */ + { int m5 = z->l - z->c; (void)m5; /* or, line 235 */ + { int ret = r_standard_suffix(z); /* call standard_suffix, line 235 */ if (ret == 0) goto lab6; if (ret < 0) return ret; } goto lab5; lab6: z->c = z->l - m5; - { int ret = r_i_verb_suffix(z); /* call i_verb_suffix, line 230 */ + { int ret = r_i_verb_suffix(z); /* call i_verb_suffix, line 236 */ if (ret == 0) goto lab7; if (ret < 0) return ret; } goto lab5; lab7: z->c = z->l - m5; - { int ret = r_verb_suffix(z); /* call verb_suffix, line 231 */ + { int ret = r_verb_suffix(z); /* call verb_suffix, line 237 */ if (ret == 0) goto lab4; if (ret < 0) return ret; } } lab5: z->c = z->l - m4; - { int m6 = z->l - z->c; (void)m6; /* try, line 234 */ - z->ket = z->c; /* [, line 234 */ - { int m7 = z->l - z->c; (void)m7; /* or, line 234 */ - if (z->c <= z->lb || z->p[z->c - 1] != 'Y') goto lab10; /* literal, line 234 */ + { int m6 = z->l - z->c; (void)m6; /* try, line 240 */ + z->ket = z->c; /* [, line 240 */ + { int m7 = z->l - z->c; (void)m7; /* or, line 240 */ + if (z->c <= z->lb || z->p[z->c - 1] != 'Y') goto lab10; /* literal, line 240 */ z->c--; - z->bra = z->c; /* ], line 234 */ - { int ret = slice_from_s(z, 1, s_31); /* <-, line 234 */ + z->bra = z->c; /* ], line 240 */ + { int ret = slice_from_s(z, 1, s_37); /* <-, line 240 */ if (ret < 0) return ret; } goto lab9; lab10: z->c = z->l - m7; - if (!(eq_s_b(z, 2, s_32))) { z->c = z->l - m6; goto lab8; } /* literal, line 235 */ - z->bra = z->c; /* ], line 235 */ - { int ret = slice_from_s(z, 1, s_33); /* <-, line 235 */ + if (!(eq_s_b(z, 2, s_38))) { z->c = z->l - m6; goto lab8; } /* literal, line 241 */ + z->bra = z->c; /* ], line 241 */ + { int ret = slice_from_s(z, 1, s_39); /* <-, line 241 */ if (ret < 0) return ret; } } @@ -1178,7 +1230,7 @@ extern int french_UTF_8_stem(struct SN_env * z) { /* forwardmode */ goto lab3; lab4: z->c = z->l - m3; - { int ret = r_residual_suffix(z); /* call residual_suffix, line 238 */ + { int ret = r_residual_suffix(z); /* call residual_suffix, line 244 */ if (ret == 0) goto lab2; if (ret < 0) return ret; } @@ -1187,16 +1239,16 @@ extern int french_UTF_8_stem(struct SN_env * z) { /* forwardmode */ lab2: z->c = z->l - m2; } - { int m8 = z->l - z->c; (void)m8; /* do, line 243 */ - { int ret = r_un_double(z); /* call un_double, line 243 */ + { int m8 = z->l - z->c; (void)m8; /* do, line 249 */ + { int ret = r_un_double(z); /* call un_double, line 249 */ if (ret == 0) goto lab11; if (ret < 0) return ret; } lab11: z->c = z->l - m8; } - { int m9 = z->l - z->c; (void)m9; /* do, line 244 */ - { int ret = r_un_accent(z); /* call un_accent, line 244 */ + { int m9 = z->l - z->c; (void)m9; /* do, line 250 */ + { int ret = r_un_accent(z); /* call un_accent, line 250 */ if (ret == 0) goto lab12; if (ret < 0) return ret; } @@ -1204,8 +1256,8 @@ extern int french_UTF_8_stem(struct SN_env * z) { /* forwardmode */ z->c = z->l - m9; } z->c = z->lb; - { int c10 = z->c; /* do, line 246 */ - { int ret = r_postlude(z); /* call postlude, line 246 */ + { int c10 = z->c; /* do, line 252 */ + { int ret = r_postlude(z); /* call postlude, line 252 */ if (ret == 0) goto lab13; if (ret < 0) return ret; } diff --git a/src/backend/snowball/libstemmer/stem_UTF_8_greek.c b/src/backend/snowball/libstemmer/stem_UTF_8_greek.c new file mode 100644 index 000000000000..6566d9cafb99 --- /dev/null +++ b/src/backend/snowball/libstemmer/stem_UTF_8_greek.c @@ -0,0 +1,4199 @@ +/* This file was generated automatically by the Snowball to ISO C compiler */ +/* http://snowballstem.org/ */ + +#include "header.h" + +static int r_step7(struct SN_env * z); +static int r_step6(struct SN_env * z); +static int r_step5m(struct SN_env * z); +static int r_step5l(struct SN_env * z); +static int r_step5k(struct SN_env * z); +static int r_step5j(struct SN_env * z); +static int r_step5i(struct SN_env * z); +static int r_step5h(struct SN_env * z); +static int r_step5g(struct SN_env * z); +static int r_step5f(struct SN_env * z); +static int r_step5e(struct SN_env * z); +static int r_step5d(struct SN_env * z); +static int r_step5c(struct SN_env * z); +static int r_step5b(struct SN_env * z); +static int r_step5a(struct SN_env * z); +static int r_step4(struct SN_env * z); +static int r_step3(struct SN_env * z); +static int r_step2d(struct SN_env * z); +static int r_step2c(struct SN_env * z); +static int r_step2b(struct SN_env * z); +static int r_step2a(struct SN_env * z); +static int r_step1(struct SN_env * z); +static int r_steps10(struct SN_env * z); +static int r_steps9(struct SN_env * z); +static int r_steps8(struct SN_env * z); +static int r_steps7(struct SN_env * z); +static int r_steps6(struct SN_env * z); +static int r_steps5(struct SN_env * z); +static int r_steps4(struct SN_env * z); +static int r_steps3(struct SN_env * z); +static int r_steps2(struct SN_env * z); +static int r_steps1(struct SN_env * z); +static int r_has_min_length(struct SN_env * z); +static int r_tolower(struct SN_env * z); +#ifdef __cplusplus +extern "C" { +#endif +extern int greek_UTF_8_stem(struct SN_env * z); +#ifdef __cplusplus +} +#endif +#ifdef __cplusplus +extern "C" { +#endif + + +extern struct SN_env * greek_UTF_8_create_env(void); +extern void greek_UTF_8_close_env(struct SN_env * z); + + +#ifdef __cplusplus +} +#endif +static const symbol s_0_1[2] = { 0xCF, 0x82 }; +static const symbol s_0_2[2] = { 0xCE, 0x86 }; +static const symbol s_0_3[2] = { 0xCE, 0x88 }; +static const symbol s_0_4[2] = { 0xCE, 0x89 }; +static const symbol s_0_5[2] = { 0xCE, 0x8A }; +static const symbol s_0_6[2] = { 0xCF, 0x8A }; +static const symbol s_0_7[2] = { 0xCF, 0x8B }; +static const symbol s_0_8[2] = { 0xCE, 0x8C }; +static const symbol s_0_9[2] = { 0xCF, 0x8C }; +static const symbol s_0_10[2] = { 0xCF, 0x8D }; +static const symbol s_0_11[2] = { 0xCE, 0x8E }; +static const symbol s_0_12[2] = { 0xCF, 0x8E }; +static const symbol s_0_13[2] = { 0xCE, 0x8F }; +static const symbol s_0_14[2] = { 0xCE, 0x90 }; +static const symbol s_0_15[2] = { 0xCE, 0x91 }; +static const symbol s_0_16[2] = { 0xCE, 0x92 }; +static const symbol s_0_17[2] = { 0xCE, 0x93 }; +static const symbol s_0_18[2] = { 0xCE, 0x94 }; +static const symbol s_0_19[2] = { 0xCE, 0x95 }; +static const symbol s_0_20[2] = { 0xCE, 0x96 }; +static const symbol s_0_21[2] = { 0xCE, 0x97 }; +static const symbol s_0_22[2] = { 0xCE, 0x98 }; +static const symbol s_0_23[2] = { 0xCE, 0x99 }; +static const symbol s_0_24[2] = { 0xCE, 0x9A }; +static const symbol s_0_25[2] = { 0xCE, 0x9B }; +static const symbol s_0_26[2] = { 0xCE, 0x9C }; +static const symbol s_0_27[2] = { 0xCE, 0x9D }; +static const symbol s_0_28[2] = { 0xCE, 0x9E }; +static const symbol s_0_29[2] = { 0xCE, 0x9F }; +static const symbol s_0_30[2] = { 0xCE, 0xA0 }; +static const symbol s_0_31[2] = { 0xCE, 0xA1 }; +static const symbol s_0_32[2] = { 0xCE, 0xA3 }; +static const symbol s_0_33[2] = { 0xCE, 0xA4 }; +static const symbol s_0_34[2] = { 0xCE, 0xA5 }; +static const symbol s_0_35[2] = { 0xCE, 0xA6 }; +static const symbol s_0_36[2] = { 0xCE, 0xA7 }; +static const symbol s_0_37[2] = { 0xCE, 0xA8 }; +static const symbol s_0_38[2] = { 0xCE, 0xA9 }; +static const symbol s_0_39[2] = { 0xCE, 0xAA }; +static const symbol s_0_40[2] = { 0xCE, 0xAB }; +static const symbol s_0_41[2] = { 0xCE, 0xAC }; +static const symbol s_0_42[2] = { 0xCE, 0xAD }; +static const symbol s_0_43[2] = { 0xCE, 0xAE }; +static const symbol s_0_44[2] = { 0xCE, 0xAF }; +static const symbol s_0_45[2] = { 0xCE, 0xB0 }; + +static const struct among a_0[46] = +{ +/* 0 */ { 0, 0, -1, 25, 0}, +/* 1 */ { 2, s_0_1, 0, 18, 0}, +/* 2 */ { 2, s_0_2, 0, 1, 0}, +/* 3 */ { 2, s_0_3, 0, 5, 0}, +/* 4 */ { 2, s_0_4, 0, 7, 0}, +/* 5 */ { 2, s_0_5, 0, 9, 0}, +/* 6 */ { 2, s_0_6, 0, 7, 0}, +/* 7 */ { 2, s_0_7, 0, 20, 0}, +/* 8 */ { 2, s_0_8, 0, 15, 0}, +/* 9 */ { 2, s_0_9, 0, 15, 0}, +/* 10 */ { 2, s_0_10, 0, 20, 0}, +/* 11 */ { 2, s_0_11, 0, 20, 0}, +/* 12 */ { 2, s_0_12, 0, 24, 0}, +/* 13 */ { 2, s_0_13, 0, 24, 0}, +/* 14 */ { 2, s_0_14, 0, 7, 0}, +/* 15 */ { 2, s_0_15, 0, 1, 0}, +/* 16 */ { 2, s_0_16, 0, 2, 0}, +/* 17 */ { 2, s_0_17, 0, 3, 0}, +/* 18 */ { 2, s_0_18, 0, 4, 0}, +/* 19 */ { 2, s_0_19, 0, 5, 0}, +/* 20 */ { 2, s_0_20, 0, 6, 0}, +/* 21 */ { 2, s_0_21, 0, 7, 0}, +/* 22 */ { 2, s_0_22, 0, 8, 0}, +/* 23 */ { 2, s_0_23, 0, 9, 0}, +/* 24 */ { 2, s_0_24, 0, 10, 0}, +/* 25 */ { 2, s_0_25, 0, 11, 0}, +/* 26 */ { 2, s_0_26, 0, 12, 0}, +/* 27 */ { 2, s_0_27, 0, 13, 0}, +/* 28 */ { 2, s_0_28, 0, 14, 0}, +/* 29 */ { 2, s_0_29, 0, 15, 0}, +/* 30 */ { 2, s_0_30, 0, 16, 0}, +/* 31 */ { 2, s_0_31, 0, 17, 0}, +/* 32 */ { 2, s_0_32, 0, 18, 0}, +/* 33 */ { 2, s_0_33, 0, 19, 0}, +/* 34 */ { 2, s_0_34, 0, 20, 0}, +/* 35 */ { 2, s_0_35, 0, 21, 0}, +/* 36 */ { 2, s_0_36, 0, 22, 0}, +/* 37 */ { 2, s_0_37, 0, 23, 0}, +/* 38 */ { 2, s_0_38, 0, 24, 0}, +/* 39 */ { 2, s_0_39, 0, 9, 0}, +/* 40 */ { 2, s_0_40, 0, 20, 0}, +/* 41 */ { 2, s_0_41, 0, 1, 0}, +/* 42 */ { 2, s_0_42, 0, 5, 0}, +/* 43 */ { 2, s_0_43, 0, 7, 0}, +/* 44 */ { 2, s_0_44, 0, 9, 0}, +/* 45 */ { 2, s_0_45, 0, 20, 0} +}; + +static const symbol s_1_0[16] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB8, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x89, 0xCF, 0x83 }; +static const symbol s_1_1[6] = { 0xCF, 0x86, 0xCF, 0x89, 0xCF, 0x83 }; +static const symbol s_1_2[10] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_1_3[10] = { 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_1_4[10] = { 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xB5, 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_1_5[20] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB8, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x89, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_1_6[10] = { 0xCF, 0x86, 0xCF, 0x89, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_1_7[14] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_1_8[14] = { 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_1_9[14] = { 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xB5, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_1_10[18] = { 0xCE, 0xB3, 0xCE, 0xB5, 0xCE, 0xB3, 0xCE, 0xBF, 0xCE, 0xBD, 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_1_11[14] = { 0xCE, 0xB3, 0xCE, 0xB5, 0xCE, 0xB3, 0xCE, 0xBF, 0xCE, 0xBD, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_1_12[12] = { 0xCF, 0x86, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_1_13[14] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_1_14[12] = { 0xCF, 0x83, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_1_15[16] = { 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_1_16[14] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_1_17[18] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB8, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x89, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_1_18[8] = { 0xCF, 0x86, 0xCF, 0x89, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_1_19[12] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_1_20[12] = { 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_1_21[12] = { 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xB5, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_1_22[16] = { 0xCE, 0xB3, 0xCE, 0xB5, 0xCE, 0xB3, 0xCE, 0xBF, 0xCE, 0xBD, 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_1_23[10] = { 0xCF, 0x86, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_1_24[12] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_1_25[10] = { 0xCF, 0x83, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_1_26[14] = { 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_1_27[12] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_1_28[12] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB7 }; +static const symbol s_1_29[20] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB8, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x89, 0xCF, 0x84, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_30[10] = { 0xCF, 0x86, 0xCF, 0x89, 0xCF, 0x84, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_31[14] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_32[14] = { 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_33[14] = { 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xB5, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_34[18] = { 0xCE, 0xB3, 0xCE, 0xB5, 0xCE, 0xB3, 0xCE, 0xBF, 0xCE, 0xBD, 0xCE, 0xBF, 0xCF, 0x84, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_35[12] = { 0xCF, 0x86, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB9, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_36[14] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB9, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_37[12] = { 0xCF, 0x83, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_38[16] = { 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_1_39[14] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB9, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_1[40] = +{ +/* 0 */ { 16, s_1_0, -1, 10, 0}, +/* 1 */ { 6, s_1_1, -1, 9, 0}, +/* 2 */ { 10, s_1_2, -1, 7, 0}, +/* 3 */ { 10, s_1_3, -1, 8, 0}, +/* 4 */ { 10, s_1_4, -1, 6, 0}, +/* 5 */ { 20, s_1_5, -1, 10, 0}, +/* 6 */ { 10, s_1_6, -1, 9, 0}, +/* 7 */ { 14, s_1_7, -1, 7, 0}, +/* 8 */ { 14, s_1_8, -1, 8, 0}, +/* 9 */ { 14, s_1_9, -1, 6, 0}, +/* 10 */ { 18, s_1_10, -1, 11, 0}, +/* 11 */ { 14, s_1_11, -1, 11, 0}, +/* 12 */ { 12, s_1_12, -1, 1, 0}, +/* 13 */ { 14, s_1_13, -1, 2, 0}, +/* 14 */ { 12, s_1_14, -1, 4, 0}, +/* 15 */ { 16, s_1_15, -1, 5, 0}, +/* 16 */ { 14, s_1_16, -1, 3, 0}, +/* 17 */ { 18, s_1_17, -1, 10, 0}, +/* 18 */ { 8, s_1_18, -1, 9, 0}, +/* 19 */ { 12, s_1_19, -1, 7, 0}, +/* 20 */ { 12, s_1_20, -1, 8, 0}, +/* 21 */ { 12, s_1_21, -1, 6, 0}, +/* 22 */ { 16, s_1_22, -1, 11, 0}, +/* 23 */ { 10, s_1_23, -1, 1, 0}, +/* 24 */ { 12, s_1_24, -1, 2, 0}, +/* 25 */ { 10, s_1_25, -1, 4, 0}, +/* 26 */ { 14, s_1_26, -1, 5, 0}, +/* 27 */ { 12, s_1_27, -1, 3, 0}, +/* 28 */ { 12, s_1_28, -1, 7, 0}, +/* 29 */ { 20, s_1_29, -1, 10, 0}, +/* 30 */ { 10, s_1_30, -1, 9, 0}, +/* 31 */ { 14, s_1_31, -1, 7, 0}, +/* 32 */ { 14, s_1_32, -1, 8, 0}, +/* 33 */ { 14, s_1_33, -1, 6, 0}, +/* 34 */ { 18, s_1_34, -1, 11, 0}, +/* 35 */ { 12, s_1_35, -1, 1, 0}, +/* 36 */ { 14, s_1_36, -1, 2, 0}, +/* 37 */ { 12, s_1_37, -1, 4, 0}, +/* 38 */ { 16, s_1_38, -1, 5, 0}, +/* 39 */ { 14, s_1_39, -1, 3, 0} +}; + +static const symbol s_2_0[4] = { 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_2_1[12] = { 0xCE, 0xBE, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_2_2[6] = { 0xCE, 0xB5, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_2_3[12] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB9, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_2_4[12] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_2_5[8] = { 0xCE, 0xB5, 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_2_6[8] = { 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_2_7[8] = { 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x81, 0xCE, 0xBF }; +static const symbol s_2_8[14] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x81, 0xCE, 0xBF }; + +static const struct among a_2[9] = +{ +/* 0 */ { 4, s_2_0, -1, 1, 0}, +/* 1 */ { 12, s_2_1, 0, 1, 0}, +/* 2 */ { 6, s_2_2, 0, 1, 0}, +/* 3 */ { 12, s_2_3, 0, 1, 0}, +/* 4 */ { 12, s_2_4, 0, 1, 0}, +/* 5 */ { 8, s_2_5, 0, 1, 0}, +/* 6 */ { 8, s_2_6, -1, 1, 0}, +/* 7 */ { 8, s_2_7, -1, 1, 0}, +/* 8 */ { 14, s_2_8, 7, 1, 0} +}; + +static const symbol s_3_0[2] = { 0xCF, 0x80 }; +static const symbol s_3_1[6] = { 0xCE, 0xB9, 0xCE, 0xBC, 0xCF, 0x80 }; +static const symbol s_3_2[2] = { 0xCF, 0x81 }; +static const symbol s_3_3[4] = { 0xCF, 0x80, 0xCF, 0x81 }; +static const symbol s_3_4[6] = { 0xCE, 0xBC, 0xCF, 0x80, 0xCF, 0x81 }; +static const symbol s_3_5[6] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x81 }; +static const symbol s_3_6[12] = { 0xCE, 0xB3, 0xCE, 0xBB, 0xCF, 0x85, 0xCE, 0xBA, 0xCF, 0x85, 0xCF, 0x81 }; +static const symbol s_3_7[10] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBB, 0xCF, 0x85, 0xCF, 0x81 }; +static const symbol s_3_8[10] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_3_9[6] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_3_10[6] = { 0xCE, 0xB3, 0xCE, 0xBA, 0xCF, 0x81 }; +static const symbol s_3_11[14] = { 0xCF, 0x80, 0xCE, 0xB9, 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_3_12[12] = { 0xCE, 0xB2, 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xB2, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_3_13[12] = { 0xCE, 0xB3, 0xCE, 0xBB, 0xCF, 0x85, 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_3_14[6] = { 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_3_15[2] = { 0xCE, 0xB2 }; +static const symbol s_3_16[12] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x85, 0xCF, 0x81, 0xCE, 0xB9 }; +static const symbol s_3_17[8] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xBA }; +static const symbol s_3_18[8] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xBA }; +static const symbol s_3_19[2] = { 0xCE, 0xBB }; +static const symbol s_3_20[2] = { 0xCE, 0xBC }; +static const symbol s_3_21[8] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x81, 0xCE, 0xBD }; + +static const struct among a_3[22] = +{ +/* 0 */ { 2, s_3_0, -1, 1, 0}, +/* 1 */ { 6, s_3_1, 0, 1, 0}, +/* 2 */ { 2, s_3_2, -1, 1, 0}, +/* 3 */ { 4, s_3_3, 2, 1, 0}, +/* 4 */ { 6, s_3_4, 3, 1, 0}, +/* 5 */ { 6, s_3_5, 2, 1, 0}, +/* 6 */ { 12, s_3_6, 2, 1, 0}, +/* 7 */ { 10, s_3_7, 2, 1, 0}, +/* 8 */ { 10, s_3_8, 2, 1, 0}, +/* 9 */ { 6, s_3_9, 2, 1, 0}, +/* 10 */ { 6, s_3_10, 2, 1, 0}, +/* 11 */ { 14, s_3_11, 2, 1, 0}, +/* 12 */ { 12, s_3_12, 2, 1, 0}, +/* 13 */ { 12, s_3_13, 2, 1, 0}, +/* 14 */ { 6, s_3_14, -1, 1, 0}, +/* 15 */ { 2, s_3_15, -1, 1, 0}, +/* 16 */ { 12, s_3_16, -1, 1, 0}, +/* 17 */ { 8, s_3_17, -1, 1, 0}, +/* 18 */ { 8, s_3_18, -1, 1, 0}, +/* 19 */ { 2, s_3_19, -1, 1, 0}, +/* 20 */ { 2, s_3_20, -1, 1, 0}, +/* 21 */ { 8, s_3_21, -1, 1, 0} +}; + +static const symbol s_4_0[8] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_4_1[10] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB5, 0xCE, 0xB9, 0xCF, 0x83 }; +static const symbol s_4_2[6] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCF, 0x89 }; +static const symbol s_4_3[6] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB1 }; +static const symbol s_4_4[10] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_4_5[10] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_4_6[6] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB5 }; +static const symbol s_4_7[12] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_4_8[10] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_4_9[12] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_4_10[10] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_4_11[8] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB5, 0xCE, 0xB9 }; +static const symbol s_4_12[10] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_4_13[8] = { 0xCE, 0xB9, 0xCE, 0xB6, 0xCE, 0xB1, 0xCE, 0xBD }; + +static const struct among a_4[14] = +{ +/* 0 */ { 8, s_4_0, -1, 1, 0}, +/* 1 */ { 10, s_4_1, -1, 1, 0}, +/* 2 */ { 6, s_4_2, -1, 1, 0}, +/* 3 */ { 6, s_4_3, -1, 1, 0}, +/* 4 */ { 10, s_4_4, -1, 1, 0}, +/* 5 */ { 10, s_4_5, -1, 1, 0}, +/* 6 */ { 6, s_4_6, -1, 1, 0}, +/* 7 */ { 12, s_4_7, -1, 1, 0}, +/* 8 */ { 10, s_4_8, -1, 1, 0}, +/* 9 */ { 12, s_4_9, -1, 1, 0}, +/* 10 */ { 10, s_4_10, -1, 1, 0}, +/* 11 */ { 8, s_4_11, -1, 1, 0}, +/* 12 */ { 10, s_4_12, -1, 1, 0}, +/* 13 */ { 8, s_4_13, -1, 1, 0} +}; + +static const symbol s_5_0[2] = { 0xCF, 0x83 }; +static const symbol s_5_1[2] = { 0xCF, 0x87 }; +static const symbol s_5_2[4] = { 0xCF, 0x85, 0xCF, 0x88 }; +static const symbol s_5_3[4] = { 0xCE, 0xB6, 0xCF, 0x89 }; +static const symbol s_5_4[4] = { 0xCE, 0xB2, 0xCE, 0xB9 }; +static const symbol s_5_5[4] = { 0xCE, 0xBB, 0xCE, 0xB9 }; +static const symbol s_5_6[4] = { 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_5_7[4] = { 0xCE, 0xB5, 0xCE, 0xBD }; + +static const struct among a_5[8] = +{ +/* 0 */ { 2, s_5_0, -1, 1, 0}, +/* 1 */ { 2, s_5_1, -1, 1, 0}, +/* 2 */ { 4, s_5_2, -1, 1, 0}, +/* 3 */ { 4, s_5_3, -1, 1, 0}, +/* 4 */ { 4, s_5_4, -1, 1, 0}, +/* 5 */ { 4, s_5_5, -1, 1, 0}, +/* 6 */ { 4, s_5_6, -1, 1, 0}, +/* 7 */ { 4, s_5_7, -1, 1, 0} +}; + +static const symbol s_6_0[12] = { 0xCF, 0x89, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_6_1[10] = { 0xCF, 0x89, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1 }; +static const symbol s_6_2[14] = { 0xCF, 0x89, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_6_3[10] = { 0xCF, 0x89, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB5 }; +static const symbol s_6_4[14] = { 0xCF, 0x89, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_6_5[14] = { 0xCF, 0x89, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_6_6[12] = { 0xCF, 0x89, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD }; + +static const struct among a_6[7] = +{ +/* 0 */ { 12, s_6_0, -1, 1, 0}, +/* 1 */ { 10, s_6_1, -1, 1, 0}, +/* 2 */ { 14, s_6_2, -1, 1, 0}, +/* 3 */ { 10, s_6_3, -1, 1, 0}, +/* 4 */ { 14, s_6_4, -1, 1, 0}, +/* 5 */ { 14, s_6_5, -1, 1, 0}, +/* 6 */ { 12, s_6_6, -1, 1, 0} +}; + +static const symbol s_7_0[12] = { 0xCE, 0xBE, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_7_1[6] = { 0xCE, 0xB5, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_7_2[12] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB9, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_7_3[12] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_7_4[8] = { 0xCE, 0xB5, 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_7_5[14] = { 0xCF, 0x87, 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_7_6[12] = { 0xCE, 0xB5, 0xCE, 0xBE, 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x87, 0xCE, 0xB1 }; +static const symbol s_7_7[4] = { 0xCF, 0x80, 0xCE, 0xB5 }; +static const symbol s_7_8[6] = { 0xCE, 0xB5, 0xCF, 0x80, 0xCE, 0xB5 }; +static const symbol s_7_9[12] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x80, 0xCE, 0xB5 }; +static const symbol s_7_10[6] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCE, 0xB5 }; +static const symbol s_7_11[6] = { 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_7_12[12] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x89, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_7_13[8] = { 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_7_14[12] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_7_15[12] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_7_16[8] = { 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_7_17[8] = { 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x81, 0xCE, 0xBF }; +static const symbol s_7_18[14] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x81, 0xCE, 0xBF }; + +static const struct among a_7[19] = +{ +/* 0 */ { 12, s_7_0, -1, 1, 0}, +/* 1 */ { 6, s_7_1, -1, 1, 0}, +/* 2 */ { 12, s_7_2, -1, 1, 0}, +/* 3 */ { 12, s_7_3, -1, 1, 0}, +/* 4 */ { 8, s_7_4, -1, 1, 0}, +/* 5 */ { 14, s_7_5, -1, 1, 0}, +/* 6 */ { 12, s_7_6, -1, 1, 0}, +/* 7 */ { 4, s_7_7, -1, 1, 0}, +/* 8 */ { 6, s_7_8, 7, 1, 0}, +/* 9 */ { 12, s_7_9, 8, 1, 0}, +/* 10 */ { 6, s_7_10, -1, 1, 0}, +/* 11 */ { 6, s_7_11, -1, 1, 0}, +/* 12 */ { 12, s_7_12, 11, 1, 0}, +/* 13 */ { 8, s_7_13, 11, 1, 0}, +/* 14 */ { 12, s_7_14, 13, 1, 0}, +/* 15 */ { 12, s_7_15, 11, 1, 0}, +/* 16 */ { 8, s_7_16, -1, 1, 0}, +/* 17 */ { 8, s_7_17, -1, 1, 0}, +/* 18 */ { 14, s_7_18, 17, 1, 0} +}; + +static const symbol s_8_0[2] = { 0xCF, 0x80 }; +static const symbol s_8_1[6] = { 0xCE, 0xBB, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_8_2[16] = { 0xCE, 0xB4, 0xCE, 0xB7, 0xCE, 0xBC, 0xCE, 0xBF, 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_8_3[4] = { 0xCE, 0xB1, 0xCF, 0x86 }; +static const symbol s_8_4[18] = { 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xB1, 0xCF, 0x86 }; +static const symbol s_8_5[4] = { 0xCE, 0xB3, 0xCE, 0xB5 }; +static const symbol s_8_6[6] = { 0xCE, 0xB3, 0xCE, 0xBA, 0xCE, 0xB5 }; +static const symbol s_8_7[4] = { 0xCE, 0xB3, 0xCE, 0xBA }; +static const symbol s_8_8[2] = { 0xCE, 0xBC }; +static const symbol s_8_9[12] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBC }; +static const symbol s_8_10[6] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCE, 0xBC }; +static const symbol s_8_11[4] = { 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_8_12[6] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBF }; + +static const struct among a_8[13] = +{ +/* 0 */ { 2, s_8_0, -1, 1, 0}, +/* 1 */ { 6, s_8_1, -1, 1, 0}, +/* 2 */ { 16, s_8_2, -1, 1, 0}, +/* 3 */ { 4, s_8_3, -1, 1, 0}, +/* 4 */ { 18, s_8_4, 3, 1, 0}, +/* 5 */ { 4, s_8_5, -1, 1, 0}, +/* 6 */ { 6, s_8_6, -1, 1, 0}, +/* 7 */ { 4, s_8_7, -1, 1, 0}, +/* 8 */ { 2, s_8_8, -1, 1, 0}, +/* 9 */ { 12, s_8_9, 8, 1, 0}, +/* 10 */ { 6, s_8_10, 8, 1, 0}, +/* 11 */ { 4, s_8_11, -1, 1, 0}, +/* 12 */ { 6, s_8_12, -1, 1, 0} +}; + +static const symbol s_9_0[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_9_1[6] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB1 }; +static const symbol s_9_2[6] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB5 }; +static const symbol s_9_3[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_9_4[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_9_5[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_9_6[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBD }; + +static const struct among a_9[7] = +{ +/* 0 */ { 8, s_9_0, -1, 1, 0}, +/* 1 */ { 6, s_9_1, -1, 1, 0}, +/* 2 */ { 6, s_9_2, -1, 1, 0}, +/* 3 */ { 10, s_9_3, -1, 1, 0}, +/* 4 */ { 10, s_9_4, -1, 1, 0}, +/* 5 */ { 10, s_9_5, -1, 1, 0}, +/* 6 */ { 8, s_9_6, -1, 1, 0} +}; + +static const symbol s_10_0[12] = { 0xCE, 0xBE, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_10_1[6] = { 0xCE, 0xB5, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_10_2[12] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB9, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_10_3[12] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_10_4[8] = { 0xCE, 0xB5, 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_10_5[14] = { 0xCF, 0x87, 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x80, 0xCE, 0xB1 }; +static const symbol s_10_6[12] = { 0xCE, 0xB5, 0xCE, 0xBE, 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x87, 0xCE, 0xB1 }; +static const symbol s_10_7[4] = { 0xCF, 0x80, 0xCE, 0xB5 }; +static const symbol s_10_8[6] = { 0xCE, 0xB5, 0xCF, 0x80, 0xCE, 0xB5 }; +static const symbol s_10_9[12] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x80, 0xCE, 0xB5 }; +static const symbol s_10_10[6] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCE, 0xB5 }; +static const symbol s_10_11[6] = { 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_10_12[12] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x89, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_10_13[8] = { 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_10_14[12] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_10_15[12] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_10_16[8] = { 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_10_17[8] = { 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x81, 0xCE, 0xBF }; +static const symbol s_10_18[14] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x81, 0xCE, 0xBF }; + +static const struct among a_10[19] = +{ +/* 0 */ { 12, s_10_0, -1, 1, 0}, +/* 1 */ { 6, s_10_1, -1, 1, 0}, +/* 2 */ { 12, s_10_2, -1, 1, 0}, +/* 3 */ { 12, s_10_3, -1, 1, 0}, +/* 4 */ { 8, s_10_4, -1, 1, 0}, +/* 5 */ { 14, s_10_5, -1, 1, 0}, +/* 6 */ { 12, s_10_6, -1, 1, 0}, +/* 7 */ { 4, s_10_7, -1, 1, 0}, +/* 8 */ { 6, s_10_8, 7, 1, 0}, +/* 9 */ { 12, s_10_9, 8, 1, 0}, +/* 10 */ { 6, s_10_10, -1, 1, 0}, +/* 11 */ { 6, s_10_11, -1, 1, 0}, +/* 12 */ { 12, s_10_12, 11, 1, 0}, +/* 13 */ { 8, s_10_13, 11, 1, 0}, +/* 14 */ { 12, s_10_14, 13, 1, 0}, +/* 15 */ { 12, s_10_15, 11, 1, 0}, +/* 16 */ { 8, s_10_16, -1, 1, 0}, +/* 17 */ { 8, s_10_17, -1, 1, 0}, +/* 18 */ { 14, s_10_18, 17, 1, 0} +}; + +static const symbol s_11_0[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB5, 0xCE, 0xB9, 0xCF, 0x83 }; +static const symbol s_11_1[6] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x89 }; +static const symbol s_11_2[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_11_3[12] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_11_4[12] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_11_5[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB5, 0xCE, 0xB9 }; +static const symbol s_11_6[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; + +static const struct among a_11[7] = +{ +/* 0 */ { 10, s_11_0, -1, 1, 0}, +/* 1 */ { 6, s_11_1, -1, 1, 0}, +/* 2 */ { 10, s_11_2, -1, 1, 0}, +/* 3 */ { 12, s_11_3, -1, 1, 0}, +/* 4 */ { 12, s_11_4, -1, 1, 0}, +/* 5 */ { 8, s_11_5, -1, 1, 0}, +/* 6 */ { 10, s_11_6, -1, 1, 0} +}; + +static const symbol s_12_0[4] = { 0xCF, 0x83, 0xCE, 0xB5 }; +static const symbol s_12_1[6] = { 0xCE, 0xB1, 0xCF, 0x83, 0xCE, 0xB5 }; +static const symbol s_12_2[6] = { 0xCF, 0x80, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_12_3[6] = { 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_12_4[12] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x89, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_12_5[8] = { 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_12_6[14] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x81, 0xCE, 0xBF }; + +static const struct among a_12[7] = +{ +/* 0 */ { 4, s_12_0, -1, 1, 0}, +/* 1 */ { 6, s_12_1, 0, 1, 0}, +/* 2 */ { 6, s_12_2, -1, 1, 0}, +/* 3 */ { 6, s_12_3, -1, 1, 0}, +/* 4 */ { 12, s_12_4, 3, 1, 0}, +/* 5 */ { 8, s_12_5, -1, 1, 0}, +/* 6 */ { 14, s_12_6, -1, 1, 0} +}; + +static const symbol s_13_0[2] = { 0xCF, 0x80 }; +static const symbol s_13_1[6] = { 0xCE, 0xB5, 0xCF, 0x85, 0xCF, 0x80 }; +static const symbol s_13_2[4] = { 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_13_3[6] = { 0xCE, 0xB5, 0xCE, 0xBC, 0xCF, 0x80 }; +static const symbol s_13_4[6] = { 0xCE, 0xB3, 0xCF, 0x85, 0xCF, 0x81 }; +static const symbol s_13_5[4] = { 0xCF, 0x87, 0xCF, 0x81 }; +static const symbol s_13_6[6] = { 0xCF, 0x87, 0xCF, 0x89, 0xCF, 0x81 }; +static const symbol s_13_7[4] = { 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_13_8[6] = { 0xCE, 0xB1, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_13_9[4] = { 0xCF, 0x87, 0xCF, 0x84 }; +static const symbol s_13_10[6] = { 0xCE, 0xB1, 0xCF, 0x87, 0xCF, 0x84 }; +static const symbol s_13_11[4] = { 0xCE, 0xBA, 0xCF, 0x84 }; +static const symbol s_13_12[6] = { 0xCE, 0xB1, 0xCE, 0xBA, 0xCF, 0x84 }; +static const symbol s_13_13[4] = { 0xCF, 0x83, 0xCF, 0x87 }; +static const symbol s_13_14[6] = { 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x87 }; +static const symbol s_13_15[6] = { 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x87 }; +static const symbol s_13_16[4] = { 0xCF, 0x85, 0xCF, 0x88 }; +static const symbol s_13_17[6] = { 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_13_18[4] = { 0xCF, 0x86, 0xCE, 0xB1 }; +static const symbol s_13_19[6] = { 0xCE, 0xB7, 0xCF, 0x86, 0xCE, 0xB1 }; +static const symbol s_13_20[6] = { 0xCE, 0xBB, 0xCF, 0x85, 0xCE, 0xB3 }; +static const symbol s_13_21[6] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCE, 0xB3 }; +static const symbol s_13_22[4] = { 0xCE, 0xB7, 0xCE, 0xB4 }; +static const symbol s_13_23[6] = { 0xCE, 0xB5, 0xCF, 0x87, 0xCE, 0xB8 }; +static const symbol s_13_24[6] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB8 }; +static const symbol s_13_25[4] = { 0xCF, 0x83, 0xCE, 0xBA }; +static const symbol s_13_26[6] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBA }; +static const symbol s_13_27[6] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBA }; +static const symbol s_13_28[6] = { 0xCE, 0xBA, 0xCF, 0x85, 0xCE, 0xBB }; +static const symbol s_13_29[6] = { 0xCF, 0x86, 0xCE, 0xB9, 0xCE, 0xBB }; +static const symbol s_13_30[2] = { 0xCE, 0xBC }; +static const symbol s_13_31[6] = { 0xCE, 0xB3, 0xCE, 0xB5, 0xCE, 0xBC }; +static const symbol s_13_32[6] = { 0xCE, 0xB1, 0xCF, 0x87, 0xCE, 0xBD }; + +static const struct among a_13[33] = +{ +/* 0 */ { 2, s_13_0, -1, 1, 0}, +/* 1 */ { 6, s_13_1, 0, 1, 0}, +/* 2 */ { 4, s_13_2, 0, 1, 0}, +/* 3 */ { 6, s_13_3, 0, 1, 0}, +/* 4 */ { 6, s_13_4, -1, 1, 0}, +/* 5 */ { 4, s_13_5, -1, 1, 0}, +/* 6 */ { 6, s_13_6, -1, 1, 0}, +/* 7 */ { 4, s_13_7, -1, 1, 0}, +/* 8 */ { 6, s_13_8, -1, 1, 0}, +/* 9 */ { 4, s_13_9, -1, 1, 0}, +/* 10 */ { 6, s_13_10, 9, 1, 0}, +/* 11 */ { 4, s_13_11, -1, 1, 0}, +/* 12 */ { 6, s_13_12, 11, 1, 0}, +/* 13 */ { 4, s_13_13, -1, 1, 0}, +/* 14 */ { 6, s_13_14, 13, 1, 0}, +/* 15 */ { 6, s_13_15, -1, 1, 0}, +/* 16 */ { 4, s_13_16, -1, 1, 0}, +/* 17 */ { 6, s_13_17, -1, 1, 0}, +/* 18 */ { 4, s_13_18, -1, 1, 0}, +/* 19 */ { 6, s_13_19, 18, 1, 0}, +/* 20 */ { 6, s_13_20, -1, 1, 0}, +/* 21 */ { 6, s_13_21, -1, 1, 0}, +/* 22 */ { 4, s_13_22, -1, 1, 0}, +/* 23 */ { 6, s_13_23, -1, 1, 0}, +/* 24 */ { 6, s_13_24, -1, 1, 0}, +/* 25 */ { 4, s_13_25, -1, 1, 0}, +/* 26 */ { 6, s_13_26, -1, 1, 0}, +/* 27 */ { 6, s_13_27, -1, 1, 0}, +/* 28 */ { 6, s_13_28, -1, 1, 0}, +/* 29 */ { 6, s_13_29, -1, 1, 0}, +/* 30 */ { 2, s_13_30, -1, 1, 0}, +/* 31 */ { 6, s_13_31, 30, 1, 0}, +/* 32 */ { 6, s_13_32, -1, 1, 0} +}; + +static const symbol s_14_0[12] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_14_1[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_14_2[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB7, 0xCF, 0x83 }; +static const symbol s_14_3[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_14_4[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_14_5[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_14_6[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_14_7[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB7 }; +static const symbol s_14_8[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xB9 }; +static const symbol s_14_9[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_14_10[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xBF }; + +static const struct among a_14[11] = +{ +/* 0 */ { 12, s_14_0, -1, 1, 0}, +/* 1 */ { 10, s_14_1, -1, 1, 0}, +/* 2 */ { 10, s_14_2, -1, 1, 0}, +/* 3 */ { 10, s_14_3, -1, 1, 0}, +/* 4 */ { 10, s_14_4, -1, 1, 0}, +/* 5 */ { 8, s_14_5, -1, 1, 0}, +/* 6 */ { 8, s_14_6, -1, 1, 0}, +/* 7 */ { 8, s_14_7, -1, 1, 0}, +/* 8 */ { 10, s_14_8, -1, 1, 0}, +/* 9 */ { 10, s_14_9, -1, 1, 0}, +/* 10 */ { 8, s_14_10, -1, 1, 0} +}; + +static const symbol s_15_0[4] = { 0xCF, 0x83, 0xCE, 0xB5 }; +static const symbol s_15_1[12] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x83, 0xCE, 0xB5 }; +static const symbol s_15_2[14] = { 0xCE, 0xBC, 0xCE, 0xB9, 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xB5 }; +static const symbol s_15_3[10] = { 0xCE, 0xB5, 0xCE, 0xB3, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_15_4[12] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5 }; + +static const struct among a_15[5] = +{ +/* 0 */ { 4, s_15_0, -1, 1, 0}, +/* 1 */ { 12, s_15_1, 0, 1, 0}, +/* 2 */ { 14, s_15_2, 0, 1, 0}, +/* 3 */ { 10, s_15_3, -1, 1, 0}, +/* 4 */ { 12, s_15_4, -1, 1, 0} +}; + +static const symbol s_16_0[8] = { 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_16_1[16] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; + +static const struct among a_16[2] = +{ +/* 0 */ { 8, s_16_0, -1, 1, 0}, +/* 1 */ { 16, s_16_1, 0, 1, 0} +}; + +static const symbol s_17_0[10] = { 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x80, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_17_1[14] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xB5, 0xCF, 0x80, 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_17_2[14] = { 0xCE, 0xB3, 0xCE, 0xBD, 0xCF, 0x89, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_17_3[16] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBD, 0xCF, 0x89, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_17_4[16] = { 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5, 0xCE, 0xBA, 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_17_5[12] = { 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_17_6[10] = { 0xCE, 0xB5, 0xCE, 0xB8, 0xCE, 0xBD, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_17_7[14] = { 0xCE, 0xB8, 0xCE, 0xB5, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x81, 0xCE, 0xB9, 0xCE, 0xBD }; +static const symbol s_17_8[20] = { 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xB5, 0xCE, 0xBE, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB4, 0xCF, 0x81, 0xCE, 0xB9, 0xCE, 0xBD }; +static const symbol s_17_9[16] = { 0xCE, 0xB2, 0xCF, 0x85, 0xCE, 0xB6, 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xBD }; + +static const struct among a_17[10] = +{ +/* 0 */ { 10, s_17_0, -1, 7, 0}, +/* 1 */ { 14, s_17_1, -1, 6, 0}, +/* 2 */ { 14, s_17_2, -1, 3, 0}, +/* 3 */ { 16, s_17_3, 2, 1, 0}, +/* 4 */ { 16, s_17_4, -1, 5, 0}, +/* 5 */ { 12, s_17_5, -1, 2, 0}, +/* 6 */ { 10, s_17_6, -1, 4, 0}, +/* 7 */ { 14, s_17_7, -1, 10, 0}, +/* 8 */ { 20, s_17_8, -1, 8, 0}, +/* 9 */ { 16, s_17_9, -1, 9, 0} +}; + +static const symbol s_18_0[12] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_18_1[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_18_2[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_18_3[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0xBF, 0xCE, 0xB9 }; +static const symbol s_18_4[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_18_5[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0xBF }; + +static const struct among a_18[6] = +{ +/* 0 */ { 12, s_18_0, -1, 1, 0}, +/* 1 */ { 10, s_18_1, -1, 1, 0}, +/* 2 */ { 10, s_18_2, -1, 1, 0}, +/* 3 */ { 10, s_18_3, -1, 1, 0}, +/* 4 */ { 10, s_18_4, -1, 1, 0}, +/* 5 */ { 8, s_18_5, -1, 1, 0} +}; + +static const symbol s_19_0[2] = { 0xCF, 0x83 }; +static const symbol s_19_1[2] = { 0xCF, 0x87 }; + +static const struct among a_19[2] = +{ +/* 0 */ { 2, s_19_0, -1, 1, 0}, +/* 1 */ { 2, s_19_1, -1, 1, 0} +}; + +static const symbol s_20_0[12] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_20_1[14] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_20_2[10] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB9 }; +static const symbol s_20_3[12] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB9 }; + +static const struct among a_20[4] = +{ +/* 0 */ { 12, s_20_0, -1, 1, 0}, +/* 1 */ { 14, s_20_1, -1, 1, 0}, +/* 2 */ { 10, s_20_2, -1, 1, 0}, +/* 3 */ { 12, s_20_3, -1, 1, 0} +}; + +static const symbol s_21_0[12] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_21_1[2] = { 0xCF, 0x81 }; +static const symbol s_21_2[4] = { 0xCE, 0xB2, 0xCF, 0x81 }; +static const symbol s_21_3[8] = { 0xCE, 0xBB, 0xCE, 0xB1, 0xCE, 0xB2, 0xCF, 0x81 }; +static const symbol s_21_4[8] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB2, 0xCF, 0x81 }; +static const symbol s_21_5[6] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_21_6[8] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB8, 0xCF, 0x81 }; +static const symbol s_21_7[6] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_21_8[2] = { 0xCF, 0x83 }; +static const symbol s_21_9[12] = { 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_21_10[10] = { 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_21_11[4] = { 0xCF, 0x81, 0xCF, 0x85 }; +static const symbol s_21_12[2] = { 0xCF, 0x86 }; +static const symbol s_21_13[4] = { 0xCF, 0x83, 0xCF, 0x86 }; +static const symbol s_21_14[10] = { 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x86 }; +static const symbol s_21_15[2] = { 0xCF, 0x87 }; +static const symbol s_21_16[8] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB2 }; +static const symbol s_21_17[8] = { 0xCF, 0x83, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB2 }; +static const symbol s_21_18[18] = { 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x87, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB2 }; +static const symbol s_21_19[4] = { 0xCF, 0x84, 0xCE, 0xB6 }; +static const symbol s_21_20[2] = { 0xCE, 0xBA }; +static const symbol s_21_21[4] = { 0xCF, 0x83, 0xCE, 0xBA }; +static const symbol s_21_22[10] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xBA }; +static const symbol s_21_23[6] = { 0xCF, 0x83, 0xCE, 0xBF, 0xCE, 0xBA }; +static const symbol s_21_24[4] = { 0xCF, 0x80, 0xCE, 0xBB }; +static const symbol s_21_25[6] = { 0xCF, 0x86, 0xCF, 0x85, 0xCE, 0xBB }; +static const symbol s_21_26[8] = { 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB }; +static const symbol s_21_27[6] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_21_28[8] = { 0xCF, 0x86, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xBC }; +static const symbol s_21_29[8] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB9, 0xCE, 0xBC }; +static const symbol s_21_30[8] = { 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xBC }; +static const symbol s_21_31[8] = { 0xCF, 0x83, 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_21_32[6] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCE, 0xBD }; + +static const struct among a_21[33] = +{ +/* 0 */ { 12, s_21_0, -1, 1, 0}, +/* 1 */ { 2, s_21_1, -1, 1, 0}, +/* 2 */ { 4, s_21_2, 1, 1, 0}, +/* 3 */ { 8, s_21_3, 2, 1, 0}, +/* 4 */ { 8, s_21_4, 2, 1, 0}, +/* 5 */ { 6, s_21_5, 1, 1, 0}, +/* 6 */ { 8, s_21_6, 1, 1, 0}, +/* 7 */ { 6, s_21_7, 1, 1, 0}, +/* 8 */ { 2, s_21_8, -1, 1, 0}, +/* 9 */ { 12, s_21_9, 8, 1, 0}, +/* 10 */ { 10, s_21_10, -1, 1, 0}, +/* 11 */ { 4, s_21_11, -1, 1, 0}, +/* 12 */ { 2, s_21_12, -1, 1, 0}, +/* 13 */ { 4, s_21_13, 12, 1, 0}, +/* 14 */ { 10, s_21_14, 13, 1, 0}, +/* 15 */ { 2, s_21_15, -1, 1, 0}, +/* 16 */ { 8, s_21_16, -1, 1, 0}, +/* 17 */ { 8, s_21_17, -1, 1, 0}, +/* 18 */ { 18, s_21_18, 17, 1, 0}, +/* 19 */ { 4, s_21_19, -1, 1, 0}, +/* 20 */ { 2, s_21_20, -1, 1, 0}, +/* 21 */ { 4, s_21_21, 20, 1, 0}, +/* 22 */ { 10, s_21_22, 20, 1, 0}, +/* 23 */ { 6, s_21_23, 20, 1, 0}, +/* 24 */ { 4, s_21_24, -1, 1, 0}, +/* 25 */ { 6, s_21_25, -1, 1, 0}, +/* 26 */ { 8, s_21_26, -1, 1, 0}, +/* 27 */ { 6, s_21_27, -1, 1, 0}, +/* 28 */ { 8, s_21_28, -1, 1, 0}, +/* 29 */ { 8, s_21_29, -1, 1, 0}, +/* 30 */ { 8, s_21_30, -1, 1, 0}, +/* 31 */ { 8, s_21_31, -1, 1, 0}, +/* 32 */ { 6, s_21_32, -1, 1, 0} +}; + +static const symbol s_22_0[2] = { 0xCF, 0x80 }; +static const symbol s_22_1[10] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_22_2[6] = { 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_22_3[6] = { 0xCE, 0xBD, 0xCF, 0x85, 0xCF, 0x86 }; +static const symbol s_22_4[2] = { 0xCE, 0xB2 }; +static const symbol s_22_5[8] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB4 }; +static const symbol s_22_6[2] = { 0xCE, 0xB6 }; +static const symbol s_22_7[4] = { 0xCF, 0x83, 0xCE, 0xBA }; +static const symbol s_22_8[6] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_22_9[4] = { 0xCE, 0xB3, 0xCE, 0xBB }; +static const symbol s_22_10[12] = { 0xCF, 0x84, 0xCF, 0x81, 0xCE, 0xB9, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBB }; +static const symbol s_22_11[12] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBA, 0xCF, 0x81, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_22_12[8] = { 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_22_13[14] = { 0xCE, 0xB7, 0xCE, 0xB3, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB5, 0xCE, 0xBD }; +static const symbol s_22_14[6] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCE, 0xBD }; + +static const struct among a_22[15] = +{ +/* 0 */ { 2, s_22_0, -1, 1, 0}, +/* 1 */ { 10, s_22_1, -1, 1, 0}, +/* 2 */ { 6, s_22_2, -1, 1, 0}, +/* 3 */ { 6, s_22_3, -1, 1, 0}, +/* 4 */ { 2, s_22_4, -1, 1, 0}, +/* 5 */ { 8, s_22_5, -1, 1, 0}, +/* 6 */ { 2, s_22_6, -1, 1, 0}, +/* 7 */ { 4, s_22_7, -1, 1, 0}, +/* 8 */ { 6, s_22_8, -1, 1, 0}, +/* 9 */ { 4, s_22_9, -1, 1, 0}, +/* 10 */ { 12, s_22_10, -1, 1, 0}, +/* 11 */ { 12, s_22_11, -1, 1, 0}, +/* 12 */ { 8, s_22_12, -1, 1, 0}, +/* 13 */ { 14, s_22_13, -1, 1, 0}, +/* 14 */ { 6, s_22_14, -1, 1, 0} +}; + +static const symbol s_23_0[10] = { 0xCE, 0xB9, 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_23_1[10] = { 0xCE, 0xB9, 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_23_2[8] = { 0xCE, 0xB9, 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB1 }; +static const symbol s_23_3[8] = { 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_23_4[12] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_23_5[6] = { 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB9 }; +static const symbol s_23_6[10] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB9 }; +static const symbol s_23_7[10] = { 0xCE, 0xB9, 0xCF, 0x84, 0xCF, 0x83, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_23[8] = +{ +/* 0 */ { 10, s_23_0, -1, 1, 0}, +/* 1 */ { 10, s_23_1, -1, 1, 0}, +/* 2 */ { 8, s_23_2, -1, 1, 0}, +/* 3 */ { 8, s_23_3, -1, 1, 0}, +/* 4 */ { 12, s_23_4, 3, 1, 0}, +/* 5 */ { 6, s_23_5, -1, 1, 0}, +/* 6 */ { 10, s_23_6, 5, 1, 0}, +/* 7 */ { 10, s_23_7, -1, 1, 0} +}; + +static const symbol s_24_0[4] = { 0xCE, 0xB9, 0xCF, 0x81 }; +static const symbol s_24_1[6] = { 0xCF, 0x88, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_24_2[8] = { 0xCE, 0xB1, 0xCE, 0xB9, 0xCF, 0x86, 0xCE, 0xBD }; +static const symbol s_24_3[6] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBF }; + +static const struct among a_24[4] = +{ +/* 0 */ { 4, s_24_0, -1, 1, 0}, +/* 1 */ { 6, s_24_1, -1, 1, 0}, +/* 2 */ { 8, s_24_2, -1, 1, 0}, +/* 3 */ { 6, s_24_3, -1, 1, 0} +}; + +static const symbol s_25_0[2] = { 0xCE, 0xB5 }; +static const symbol s_25_1[10] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xB9, 0xCF, 0x87, 0xCE, 0xBD }; + +static const struct among a_25[2] = +{ +/* 0 */ { 2, s_25_0, -1, 1, 0}, +/* 1 */ { 10, s_25_1, -1, 1, 0} +}; + +static const symbol s_26_0[8] = { 0xCE, 0xB9, 0xCE, 0xB4, 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_26_1[10] = { 0xCE, 0xB9, 0xCE, 0xB4, 0xCE, 0xB9, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_26_2[8] = { 0xCE, 0xB9, 0xCE, 0xB4, 0xCE, 0xB9, 0xCE, 0xBF }; + +static const struct among a_26[3] = +{ +/* 0 */ { 8, s_26_0, -1, 1, 0}, +/* 1 */ { 10, s_26_1, -1, 1, 0}, +/* 2 */ { 8, s_26_2, -1, 1, 0} +}; + +static const symbol s_27_0[2] = { 0xCF, 0x81 }; +static const symbol s_27_1[4] = { 0xCE, 0xB9, 0xCE, 0xB2 }; +static const symbol s_27_2[2] = { 0xCE, 0xB4 }; +static const symbol s_27_3[6] = { 0xCE, 0xBB, 0xCF, 0x85, 0xCE, 0xBA }; +static const symbol s_27_4[10] = { 0xCF, 0x86, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBA }; +static const symbol s_27_5[8] = { 0xCE, 0xBF, 0xCE, 0xB2, 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_27_6[6] = { 0xCE, 0xBC, 0xCE, 0xB7, 0xCE, 0xBD }; + +static const struct among a_27[7] = +{ +/* 0 */ { 2, s_27_0, -1, 1, 0}, +/* 1 */ { 4, s_27_1, -1, 1, 0}, +/* 2 */ { 2, s_27_2, -1, 1, 0}, +/* 3 */ { 6, s_27_3, -1, 1, 0}, +/* 4 */ { 10, s_27_4, -1, 1, 0}, +/* 5 */ { 8, s_27_5, -1, 1, 0}, +/* 6 */ { 6, s_27_6, -1, 1, 0} +}; + +static const symbol s_28_0[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_28_1[10] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_28_2[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xB5 }; +static const symbol s_28_3[8] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xBF }; + +static const struct among a_28[4] = +{ +/* 0 */ { 10, s_28_0, -1, 1, 0}, +/* 1 */ { 10, s_28_1, -1, 1, 0}, +/* 2 */ { 8, s_28_2, -1, 1, 0}, +/* 3 */ { 8, s_28_3, -1, 1, 0} +}; + +static const symbol s_29_0[8] = { 0xCE, 0xB1, 0xCE, 0xB4, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_29_1[8] = { 0xCE, 0xB1, 0xCE, 0xB4, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_29[2] = +{ +/* 0 */ { 8, s_29_0, -1, 1, 0}, +/* 1 */ { 8, s_29_1, -1, 1, 0} +}; + +static const symbol s_30_0[10] = { 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xBC, 0xCF, 0x80 }; +static const symbol s_30_1[6] = { 0xCE, 0xBA, 0xCF, 0x85, 0xCF, 0x81 }; +static const symbol s_30_2[10] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_30_3[10] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCE, 0xB8, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_30_4[10] = { 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84 }; +static const symbol s_30_5[10] = { 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB9 }; +static const symbol s_30_6[6] = { 0xCE, 0xB8, 0xCE, 0xB5, 0xCE, 0xB9 }; +static const symbol s_30_7[4] = { 0xCE, 0xBF, 0xCE, 0xBA }; +static const symbol s_30_8[6] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBC }; +static const symbol s_30_9[6] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBD }; + +static const struct among a_30[10] = +{ +/* 0 */ { 10, s_30_0, -1, -1, 0}, +/* 1 */ { 6, s_30_1, -1, -1, 0}, +/* 2 */ { 10, s_30_2, -1, -1, 0}, +/* 3 */ { 10, s_30_3, -1, -1, 0}, +/* 4 */ { 10, s_30_4, -1, -1, 0}, +/* 5 */ { 10, s_30_5, -1, -1, 0}, +/* 6 */ { 6, s_30_6, -1, -1, 0}, +/* 7 */ { 4, s_30_7, -1, -1, 0}, +/* 8 */ { 6, s_30_8, -1, -1, 0}, +/* 9 */ { 6, s_30_9, -1, -1, 0} +}; + +static const symbol s_31_0[8] = { 0xCE, 0xB5, 0xCE, 0xB4, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_31_1[8] = { 0xCE, 0xB5, 0xCE, 0xB4, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_31[2] = +{ +/* 0 */ { 8, s_31_0, -1, 1, 0}, +/* 1 */ { 8, s_31_1, -1, 1, 0} +}; + +static const symbol s_32_0[10] = { 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x80 }; +static const symbol s_32_1[4] = { 0xCF, 0x85, 0xCF, 0x80 }; +static const symbol s_32_2[6] = { 0xCE, 0xB4, 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_32_3[6] = { 0xCE, 0xB3, 0xCE, 0xB7, 0xCF, 0x80 }; +static const symbol s_32_4[4] = { 0xCE, 0xB9, 0xCF, 0x80 }; +static const symbol s_32_5[6] = { 0xCE, 0xB5, 0xCE, 0xBC, 0xCF, 0x80 }; +static const symbol s_32_6[4] = { 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_32_7[6] = { 0xCE, 0xBC, 0xCE, 0xB9, 0xCE, 0xBB }; + +static const struct among a_32[8] = +{ +/* 0 */ { 10, s_32_0, -1, 1, 0}, +/* 1 */ { 4, s_32_1, -1, 1, 0}, +/* 2 */ { 6, s_32_2, -1, 1, 0}, +/* 3 */ { 6, s_32_3, -1, 1, 0}, +/* 4 */ { 4, s_32_4, -1, 1, 0}, +/* 5 */ { 6, s_32_5, -1, 1, 0}, +/* 6 */ { 4, s_32_6, -1, 1, 0}, +/* 7 */ { 6, s_32_7, -1, 1, 0} +}; + +static const symbol s_33_0[10] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xB4, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_33_1[10] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xB4, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_33[2] = +{ +/* 0 */ { 10, s_33_0, -1, 1, 0}, +/* 1 */ { 10, s_33_1, -1, 1, 0} +}; + +static const symbol s_34_0[4] = { 0xCF, 0x83, 0xCF, 0x80 }; +static const symbol s_34_1[4] = { 0xCF, 0x86, 0xCF, 0x81 }; +static const symbol s_34_2[2] = { 0xCF, 0x83 }; +static const symbol s_34_3[6] = { 0xCE, 0xBB, 0xCE, 0xB9, 0xCF, 0x87 }; +static const symbol s_34_4[8] = { 0xCF, 0x84, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_34_5[4] = { 0xCF, 0x86, 0xCE, 0xB5 }; +static const symbol s_34_6[6] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xBA }; +static const symbol s_34_7[4] = { 0xCF, 0x83, 0xCE, 0xBA }; +static const symbol s_34_8[12] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xB1, 0xCE, 0xBA }; +static const symbol s_34_9[8] = { 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB }; +static const symbol s_34_10[4] = { 0xCF, 0x86, 0xCE, 0xBB }; +static const symbol s_34_11[10] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_34_12[6] = { 0xCE, 0xB2, 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_34_13[4] = { 0xCF, 0x87, 0xCE, 0xBD }; +static const symbol s_34_14[8] = { 0xCF, 0x80, 0xCE, 0xBB, 0xCE, 0xB5, 0xCE, 0xBE }; + +static const struct among a_34[15] = +{ +/* 0 */ { 4, s_34_0, -1, 1, 0}, +/* 1 */ { 4, s_34_1, -1, 1, 0}, +/* 2 */ { 2, s_34_2, -1, 1, 0}, +/* 3 */ { 6, s_34_3, -1, 1, 0}, +/* 4 */ { 8, s_34_4, -1, 1, 0}, +/* 5 */ { 4, s_34_5, -1, 1, 0}, +/* 6 */ { 6, s_34_6, -1, 1, 0}, +/* 7 */ { 4, s_34_7, -1, 1, 0}, +/* 8 */ { 12, s_34_8, -1, 1, 0}, +/* 9 */ { 8, s_34_9, -1, 1, 0}, +/* 10 */ { 4, s_34_10, -1, 1, 0}, +/* 11 */ { 10, s_34_11, -1, 1, 0}, +/* 12 */ { 6, s_34_12, -1, 1, 0}, +/* 13 */ { 4, s_34_13, -1, 1, 0}, +/* 14 */ { 8, s_34_14, -1, 1, 0} +}; + +static const symbol s_35_0[6] = { 0xCE, 0xB5, 0xCF, 0x89, 0xCF, 0x83 }; +static const symbol s_35_1[6] = { 0xCE, 0xB5, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_35[2] = +{ +/* 0 */ { 6, s_35_0, -1, 1, 0}, +/* 1 */ { 6, s_35_1, -1, 1, 0} +}; + +static const symbol s_36_0[2] = { 0xCF, 0x80 }; +static const symbol s_36_1[6] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_36_2[2] = { 0xCE, 0xB4 }; +static const symbol s_36_3[4] = { 0xCE, 0xB9, 0xCE, 0xB4 }; +static const symbol s_36_4[2] = { 0xCE, 0xB8 }; +static const symbol s_36_5[6] = { 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_36_6[4] = { 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_36_7[2] = { 0xCE, 0xBD }; + +static const struct among a_36[8] = +{ +/* 0 */ { 2, s_36_0, -1, 1, 0}, +/* 1 */ { 6, s_36_1, -1, 1, 0}, +/* 2 */ { 2, s_36_2, -1, 1, 0}, +/* 3 */ { 4, s_36_3, 2, 1, 0}, +/* 4 */ { 2, s_36_4, -1, 1, 0}, +/* 5 */ { 6, s_36_5, -1, 1, 0}, +/* 6 */ { 4, s_36_6, -1, 1, 0}, +/* 7 */ { 2, s_36_7, -1, 1, 0} +}; + +static const symbol s_37_0[6] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_37_1[4] = { 0xCE, 0xB9, 0xCE, 0xB1 }; +static const symbol s_37_2[6] = { 0xCE, 0xB9, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_37[3] = +{ +/* 0 */ { 6, s_37_0, -1, 1, 0}, +/* 1 */ { 4, s_37_1, -1, 1, 0}, +/* 2 */ { 6, s_37_2, -1, 1, 0} +}; + +static const symbol s_38_0[8] = { 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_38_1[6] = { 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xB1 }; +static const symbol s_38_2[8] = { 0xCE, 0xB9, 0xCE, 0xBA, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_38_3[6] = { 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xBF }; + +static const struct among a_38[4] = +{ +/* 0 */ { 8, s_38_0, -1, 1, 0}, +/* 1 */ { 6, s_38_1, -1, 1, 0}, +/* 2 */ { 8, s_38_2, -1, 1, 0}, +/* 3 */ { 6, s_38_3, -1, 1, 0} +}; + +static const symbol s_39_0[8] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBB, 0xCF, 0x80 }; +static const symbol s_39_1[6] = { 0xCE, 0xB3, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_39_2[12] = { 0xCF, 0x80, 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x83 }; +static const symbol s_39_3[8] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x84, 0xCF, 0x83 }; +static const symbol s_39_4[8] = { 0xCF, 0x80, 0xCE, 0xB9, 0xCF, 0x84, 0xCF, 0x83 }; +static const symbol s_39_5[6] = { 0xCF, 0x86, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_39_6[6] = { 0xCF, 0x87, 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_39_7[8] = { 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_39_8[8] = { 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x81, 0xCF, 0x84 }; +static const symbol s_39_9[14] = { 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB9, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_39_10[6] = { 0xCE, 0xBD, 0xCE, 0xB9, 0xCF, 0x84 }; +static const symbol s_39_11[12] = { 0xCF, 0x80, 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84 }; +static const symbol s_39_12[8] = { 0xCE, 0xB5, 0xCE, 0xBE, 0xCF, 0x89, 0xCE, 0xB4 }; +static const symbol s_39_13[4] = { 0xCE, 0xB1, 0xCE, 0xB4 }; +static const symbol s_39_14[10] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB4 }; +static const symbol s_39_15[10] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1, 0xCE, 0xB4 }; +static const symbol s_39_16[10] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xB4 }; +static const symbol s_39_17[6] = { 0xCE, 0xB5, 0xCE, 0xBD, 0xCE, 0xB4 }; +static const symbol s_39_18[8] = { 0xCF, 0x85, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xB4 }; +static const symbol s_39_19[12] = { 0xCF, 0x80, 0xCF, 0x81, 0xCF, 0x89, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xB4 }; +static const symbol s_39_20[10] = { 0xCF, 0x86, 0xCF, 0x85, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB4 }; +static const symbol s_39_21[4] = { 0xCE, 0xB7, 0xCE, 0xB8 }; +static const symbol s_39_22[8] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB7, 0xCE, 0xB8 }; +static const symbol s_39_23[6] = { 0xCE, 0xBE, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_39_24[8] = { 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB }; +static const symbol s_39_25[4] = { 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_39_26[14] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x87, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_39_27[14] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB7, 0xCE, 0xBB }; +static const symbol s_39_28[8] = { 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBB }; +static const symbol s_39_29[8] = { 0xCE, 0xB2, 0xCF, 0x81, 0xCF, 0x89, 0xCE, 0xBC }; +static const symbol s_39_30[8] = { 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBC }; +static const symbol s_39_31[8] = { 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_39_32[8] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_39_33[12] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xBD }; +static const symbol s_39_34[14] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5, 0xCE, 0xBB, 0xCE, 0xBD }; +static const symbol s_39_35[10] = { 0xCF, 0x86, 0xCE, 0xB9, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xBD }; + +static const struct among a_39[36] = +{ +/* 0 */ { 8, s_39_0, -1, 1, 0}, +/* 1 */ { 6, s_39_1, -1, 1, 0}, +/* 2 */ { 12, s_39_2, -1, 1, 0}, +/* 3 */ { 8, s_39_3, -1, 1, 0}, +/* 4 */ { 8, s_39_4, -1, 1, 0}, +/* 5 */ { 6, s_39_5, -1, 1, 0}, +/* 6 */ { 6, s_39_6, -1, 1, 0}, +/* 7 */ { 8, s_39_7, -1, 1, 0}, +/* 8 */ { 8, s_39_8, -1, 1, 0}, +/* 9 */ { 14, s_39_9, -1, 1, 0}, +/* 10 */ { 6, s_39_10, -1, 1, 0}, +/* 11 */ { 12, s_39_11, -1, 1, 0}, +/* 12 */ { 8, s_39_12, -1, 1, 0}, +/* 13 */ { 4, s_39_13, -1, 1, 0}, +/* 14 */ { 10, s_39_14, 13, 1, 0}, +/* 15 */ { 10, s_39_15, 13, 1, 0}, +/* 16 */ { 10, s_39_16, -1, 1, 0}, +/* 17 */ { 6, s_39_17, -1, 1, 0}, +/* 18 */ { 8, s_39_18, -1, 1, 0}, +/* 19 */ { 12, s_39_19, -1, 1, 0}, +/* 20 */ { 10, s_39_20, -1, 1, 0}, +/* 21 */ { 4, s_39_21, -1, 1, 0}, +/* 22 */ { 8, s_39_22, 21, 1, 0}, +/* 23 */ { 6, s_39_23, -1, 1, 0}, +/* 24 */ { 8, s_39_24, -1, 1, 0}, +/* 25 */ { 4, s_39_25, -1, 1, 0}, +/* 26 */ { 14, s_39_26, 25, 1, 0}, +/* 27 */ { 14, s_39_27, -1, 1, 0}, +/* 28 */ { 8, s_39_28, -1, 1, 0}, +/* 29 */ { 8, s_39_29, -1, 1, 0}, +/* 30 */ { 8, s_39_30, -1, 1, 0}, +/* 31 */ { 8, s_39_31, -1, 1, 0}, +/* 32 */ { 8, s_39_32, -1, 1, 0}, +/* 33 */ { 12, s_39_33, -1, 1, 0}, +/* 34 */ { 14, s_39_34, -1, 1, 0}, +/* 35 */ { 10, s_39_35, -1, 1, 0} +}; + +static const symbol s_40_0[12] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_40_1[10] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_40_2[10] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_40_3[10] = { 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_40_4[14] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; + +static const struct among a_40[5] = +{ +/* 0 */ { 12, s_40_0, -1, 1, 0}, +/* 1 */ { 10, s_40_1, -1, 1, 0}, +/* 2 */ { 10, s_40_2, -1, 1, 0}, +/* 3 */ { 10, s_40_3, -1, 1, 0}, +/* 4 */ { 14, s_40_4, 3, 1, 0} +}; + +static const symbol s_41_0[8] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_41_1[8] = { 0xCF, 0x80, 0xCE, 0xB9, 0xCE, 0xBA, 0xCF, 0x81 }; +static const symbol s_41_2[10] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_41_3[6] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x84 }; +static const symbol s_41_4[2] = { 0xCF, 0x87 }; +static const symbol s_41_5[6] = { 0xCF, 0x83, 0xCE, 0xB9, 0xCF, 0x87 }; +static const symbol s_41_6[8] = { 0xCE, 0xB2, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xB2 }; +static const symbol s_41_7[6] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCE, 0xB8 }; +static const symbol s_41_8[6] = { 0xCE, 0xBE, 0xCE, 0xB5, 0xCE, 0xB8 }; +static const symbol s_41_9[8] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xB8 }; +static const symbol s_41_10[8] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBA }; +static const symbol s_41_11[6] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB }; + +static const struct among a_41[12] = +{ +/* 0 */ { 8, s_41_0, -1, 1, 0}, +/* 1 */ { 8, s_41_1, -1, 1, 0}, +/* 2 */ { 10, s_41_2, -1, 1, 0}, +/* 3 */ { 6, s_41_3, -1, 1, 0}, +/* 4 */ { 2, s_41_4, -1, 1, 0}, +/* 5 */ { 6, s_41_5, 4, 1, 0}, +/* 6 */ { 8, s_41_6, -1, 1, 0}, +/* 7 */ { 6, s_41_7, -1, 1, 0}, +/* 8 */ { 6, s_41_8, -1, 1, 0}, +/* 9 */ { 8, s_41_9, -1, 1, 0}, +/* 10 */ { 8, s_41_10, -1, 1, 0}, +/* 11 */ { 6, s_41_11, -1, 1, 0} +}; + +static const symbol s_42_0[4] = { 0xCF, 0x84, 0xCF, 0x81 }; +static const symbol s_42_1[4] = { 0xCF, 0x84, 0xCF, 0x83 }; + +static const struct among a_42[2] = +{ +/* 0 */ { 4, s_42_0, -1, 1, 0}, +/* 1 */ { 4, s_42_1, -1, 1, 0} +}; + +static const symbol s_43_0[12] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_1[10] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_2[14] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_3[16] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_4[12] = { 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_5[14] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_6[10] = { 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_7[12] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_8[10] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_9[10] = { 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_43_10[14] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; + +static const struct among a_43[11] = +{ +/* 0 */ { 12, s_43_0, -1, 1, 0}, +/* 1 */ { 10, s_43_1, -1, 1, 0}, +/* 2 */ { 14, s_43_2, -1, 1, 0}, +/* 3 */ { 16, s_43_3, 2, 1, 0}, +/* 4 */ { 12, s_43_4, -1, 1, 0}, +/* 5 */ { 14, s_43_5, 4, 1, 0}, +/* 6 */ { 10, s_43_6, -1, 1, 0}, +/* 7 */ { 12, s_43_7, 6, 1, 0}, +/* 8 */ { 10, s_43_8, -1, 1, 0}, +/* 9 */ { 10, s_43_9, -1, 1, 0}, +/* 10 */ { 14, s_43_10, 9, 1, 0} +}; + +static const symbol s_44_0[2] = { 0xCF, 0x80 }; +static const symbol s_44_1[4] = { 0xCF, 0x83, 0xCF, 0x80 }; +static const symbol s_44_2[14] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBB, 0xCF, 0x85, 0xCE, 0xB4, 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_44_3[8] = { 0xCE, 0xB1, 0xCE, 0xB4, 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_44_4[18] = { 0xCF, 0x87, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB7, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB4, 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_44_5[8] = { 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_44_6[6] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_44_7[12] = { 0xCF, 0x85, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_44_8[12] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB9, 0xCF, 0x84, 0xCF, 0x81 }; +static const symbol s_44_9[6] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x81 }; +static const symbol s_44_10[4] = { 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_44_11[10] = { 0xCE, 0xB2, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_44_12[6] = { 0xCE, 0xB3, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_44_13[12] = { 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xB8, 0xCE, 0xB7, 0xCF, 0x81 }; +static const symbol s_44_14[12] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x81, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_44_15[2] = { 0xCF, 0x83 }; +static const symbol s_44_16[16] = { 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x83 }; +static const symbol s_44_17[6] = { 0xCE, 0xB8, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_44_18[6] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_44_19[10] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xB9, 0xCF, 0x83 }; +static const symbol s_44_20[8] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_44_21[8] = { 0xCE, 0xB4, 0xCE, 0xB9, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_44_22[8] = { 0xCF, 0x80, 0xCE, 0xBB, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_44_23[14] = { 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xBB, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_44_24[6] = { 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x84 }; +static const symbol s_44_25[12] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x81, 0xCE, 0xB9, 0xCF, 0x84 }; +static const symbol s_44_26[10] = { 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB, 0xCF, 0x84 }; +static const symbol s_44_27[8] = { 0xCE, 0xB6, 0xCF, 0x89, 0xCE, 0xBD, 0xCF, 0x84 }; +static const symbol s_44_28[10] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xB9, 0xCE, 0xBD, 0xCF, 0x84 }; +static const symbol s_44_29[2] = { 0xCF, 0x86 }; +static const symbol s_44_30[14] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x86 }; +static const symbol s_44_31[14] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCE, 0xB9, 0xCE, 0xBB, 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x86 }; +static const symbol s_44_32[6] = { 0xCE, 0xBF, 0xCF, 0x81, 0xCF, 0x86 }; +static const symbol s_44_33[8] = { 0xCE, 0xB4, 0xCE, 0xB9, 0xCE, 0xB1, 0xCF, 0x86 }; +static const symbol s_44_34[8] = { 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x86 }; +static const symbol s_44_35[16] = { 0xCF, 0x86, 0xCF, 0x89, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x86 }; +static const symbol s_44_36[10] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB7, 0xCF, 0x86 }; +static const symbol s_44_37[12] = { 0xCF, 0x85, 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB7, 0xCF, 0x86 }; +static const symbol s_44_38[2] = { 0xCF, 0x87 }; +static const symbol s_44_39[14] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBB, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB7, 0xCF, 0x87 }; +static const symbol s_44_40[8] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB7, 0xCF, 0x87 }; +static const symbol s_44_41[12] = { 0xCE, 0xB2, 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB7, 0xCF, 0x87 }; +static const symbol s_44_42[22] = { 0xCE, 0xBC, 0xCE, 0xB9, 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xBF, 0xCE, 0xB2, 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB7, 0xCF, 0x87 }; +static const symbol s_44_43[22] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCE, 0xB3, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB2, 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB7, 0xCF, 0x87 }; +static const symbol s_44_44[22] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBD, 0xCE, 0xBF, 0xCE, 0xB2, 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB7, 0xCF, 0x87 }; +static const symbol s_44_45[6] = { 0xCE, 0xBB, 0xCE, 0xB9, 0xCF, 0x87 }; +static const symbol s_44_46[6] = { 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB2 }; +static const symbol s_44_47[8] = { 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB2 }; +static const symbol s_44_48[14] = { 0xCF, 0x88, 0xCE, 0xB7, 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB2 }; +static const symbol s_44_49[6] = { 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xB2 }; +static const symbol s_44_50[8] = { 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xB2 }; +static const symbol s_44_51[16] = { 0xCE, 0xBE, 0xCE, 0xB7, 0xCF, 0x81, 0xCE, 0xBF, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xB2 }; +static const symbol s_44_52[2] = { 0xCE, 0xB3 }; +static const symbol s_44_53[10] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xBF, 0xCF, 0x81, 0xCE, 0xB3 }; +static const symbol s_44_54[10] = { 0xCE, 0xB5, 0xCE, 0xBD, 0xCE, 0xBF, 0xCF, 0x81, 0xCE, 0xB3 }; +static const symbol s_44_55[4] = { 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_44_56[8] = { 0xCF, 0x84, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_44_57[8] = { 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_44_58[10] = { 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB9, 0xCE, 0xB3, 0xCE, 0xB3 }; +static const symbol s_44_59[12] = { 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB9, 0xCE, 0xB3, 0xCE, 0xB3 }; +static const symbol s_44_60[10] = { 0xCE, 0xB1, 0xCE, 0xB8, 0xCE, 0xB9, 0xCE, 0xB3, 0xCE, 0xB3 }; +static const symbol s_44_61[8] = { 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5, 0xCE, 0xB3 }; +static const symbol s_44_62[8] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB7, 0xCE, 0xB3 }; +static const symbol s_44_63[6] = { 0xCF, 0x83, 0xCE, 0xB9, 0xCE, 0xB3 }; +static const symbol s_44_64[14] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBB, 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xB6 }; +static const symbol s_44_65[2] = { 0xCE, 0xB8 }; +static const symbol s_44_66[12] = { 0xCE, 0xBC, 0xCF, 0x89, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5, 0xCE, 0xB8 }; +static const symbol s_44_67[6] = { 0xCF, 0x80, 0xCE, 0xB9, 0xCE, 0xB8 }; +static const symbol s_44_68[8] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB9, 0xCE, 0xB8 }; +static const symbol s_44_69[8] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCF, 0x83, 0xCE, 0xBA }; +static const symbol s_44_70[12] = { 0xCE, 0xB2, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x87, 0xCF, 0x85, 0xCE, 0xBA }; +static const symbol s_44_71[6] = { 0xCE, 0xB4, 0xCE, 0xB5, 0xCE, 0xBA }; +static const symbol s_44_72[10] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCE, 0xBB, 0xCE, 0xB5, 0xCE, 0xBA }; +static const symbol s_44_73[4] = { 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_44_74[8] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_44_75[10] = { 0xCE, 0xB2, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB, 0xCE, 0xBA }; +static const symbol s_44_76[4] = { 0xCF, 0x80, 0xCE, 0xBB }; +static const symbol s_44_77[8] = { 0xCE, 0xB4, 0xCE, 0xB9, 0xCF, 0x80, 0xCE, 0xBB }; +static const symbol s_44_78[12] = { 0xCF, 0x88, 0xCF, 0x85, 0xCF, 0x87, 0xCE, 0xBF, 0xCF, 0x80, 0xCE, 0xBB }; +static const symbol s_44_79[10] = { 0xCE, 0xBB, 0xCE, 0xB1, 0xCE, 0xBF, 0xCF, 0x80, 0xCE, 0xBB }; +static const symbol s_44_80[6] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB }; +static const symbol s_44_81[6] = { 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_44_82[14] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCE, 0xB8, 0xCF, 0x85, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_44_83[14] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_44_84[12] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_44_85[12] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_44_86[6] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_44_87[12] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x81, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xBB }; +static const symbol s_44_88[2] = { 0xCE, 0xBC }; +static const symbol s_44_89[14] = { 0xCE, 0xB4, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xB4, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC }; +static const symbol s_44_90[10] = { 0xCE, 0xB2, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x87, 0xCE, 0xBC }; +static const symbol s_44_91[16] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xB3, 0xCE, 0xBF, 0xCE, 0xB4, 0xCE, 0xB1, 0xCE, 0xBC }; +static const symbol s_44_92[16] = { 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB, 0xCE, 0xBC }; +static const symbol s_44_93[2] = { 0xCE, 0xBD }; +static const symbol s_44_94[16] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD }; + +static const struct among a_44[95] = +{ +/* 0 */ { 2, s_44_0, -1, 1, 0}, +/* 1 */ { 4, s_44_1, 0, 1, 0}, +/* 2 */ { 14, s_44_2, 0, 1, 0}, +/* 3 */ { 8, s_44_3, 0, 1, 0}, +/* 4 */ { 18, s_44_4, 0, 1, 0}, +/* 5 */ { 8, s_44_5, 0, 1, 0}, +/* 6 */ { 6, s_44_6, 0, 1, 0}, +/* 7 */ { 12, s_44_7, 6, 1, 0}, +/* 8 */ { 12, s_44_8, -1, 1, 0}, +/* 9 */ { 6, s_44_9, -1, 1, 0}, +/* 10 */ { 4, s_44_10, -1, 1, 0}, +/* 11 */ { 10, s_44_11, 10, 1, 0}, +/* 12 */ { 6, s_44_12, 10, 1, 0}, +/* 13 */ { 12, s_44_13, -1, 1, 0}, +/* 14 */ { 12, s_44_14, -1, 1, 0}, +/* 15 */ { 2, s_44_15, -1, 1, 0}, +/* 16 */ { 16, s_44_16, 15, 1, 0}, +/* 17 */ { 6, s_44_17, 15, 1, 0}, +/* 18 */ { 6, s_44_18, 15, 1, 0}, +/* 19 */ { 10, s_44_19, 15, 1, 0}, +/* 20 */ { 8, s_44_20, -1, 1, 0}, +/* 21 */ { 8, s_44_21, -1, 1, 0}, +/* 22 */ { 8, s_44_22, -1, 1, 0}, +/* 23 */ { 14, s_44_23, -1, 1, 0}, +/* 24 */ { 6, s_44_24, -1, 1, 0}, +/* 25 */ { 12, s_44_25, -1, 1, 0}, +/* 26 */ { 10, s_44_26, -1, 1, 0}, +/* 27 */ { 8, s_44_27, -1, 1, 0}, +/* 28 */ { 10, s_44_28, -1, 1, 0}, +/* 29 */ { 2, s_44_29, -1, 1, 0}, +/* 30 */ { 14, s_44_30, 29, 1, 0}, +/* 31 */ { 14, s_44_31, 29, 1, 0}, +/* 32 */ { 6, s_44_32, 29, 1, 0}, +/* 33 */ { 8, s_44_33, 29, 1, 0}, +/* 34 */ { 8, s_44_34, 29, 1, 0}, +/* 35 */ { 16, s_44_35, 34, 1, 0}, +/* 36 */ { 10, s_44_36, 29, 1, 0}, +/* 37 */ { 12, s_44_37, 36, 1, 0}, +/* 38 */ { 2, s_44_38, -1, 1, 0}, +/* 39 */ { 14, s_44_39, 38, 1, 0}, +/* 40 */ { 8, s_44_40, 38, 1, 0}, +/* 41 */ { 12, s_44_41, 38, 1, 0}, +/* 42 */ { 22, s_44_42, 41, 1, 0}, +/* 43 */ { 22, s_44_43, 41, 1, 0}, +/* 44 */ { 22, s_44_44, 41, 1, 0}, +/* 45 */ { 6, s_44_45, 38, 1, 0}, +/* 46 */ { 6, s_44_46, -1, 1, 0}, +/* 47 */ { 8, s_44_47, 46, 1, 0}, +/* 48 */ { 14, s_44_48, 46, 1, 0}, +/* 49 */ { 6, s_44_49, -1, 1, 0}, +/* 50 */ { 8, s_44_50, 49, 1, 0}, +/* 51 */ { 16, s_44_51, 50, 1, 0}, +/* 52 */ { 2, s_44_52, -1, 1, 0}, +/* 53 */ { 10, s_44_53, 52, 1, 0}, +/* 54 */ { 10, s_44_54, 52, 1, 0}, +/* 55 */ { 4, s_44_55, 52, 1, 0}, +/* 56 */ { 8, s_44_56, 55, 1, 0}, +/* 57 */ { 8, s_44_57, 55, 1, 0}, +/* 58 */ { 10, s_44_58, 52, 1, 0}, +/* 59 */ { 12, s_44_59, 58, 1, 0}, +/* 60 */ { 10, s_44_60, 52, 1, 0}, +/* 61 */ { 8, s_44_61, 52, 1, 0}, +/* 62 */ { 8, s_44_62, 52, 1, 0}, +/* 63 */ { 6, s_44_63, 52, 1, 0}, +/* 64 */ { 14, s_44_64, -1, 1, 0}, +/* 65 */ { 2, s_44_65, -1, 1, 0}, +/* 66 */ { 12, s_44_66, 65, 1, 0}, +/* 67 */ { 6, s_44_67, 65, 1, 0}, +/* 68 */ { 8, s_44_68, 67, 1, 0}, +/* 69 */ { 8, s_44_69, -1, 1, 0}, +/* 70 */ { 12, s_44_70, -1, 1, 0}, +/* 71 */ { 6, s_44_71, -1, 1, 0}, +/* 72 */ { 10, s_44_72, -1, 1, 0}, +/* 73 */ { 4, s_44_73, -1, 1, 0}, +/* 74 */ { 8, s_44_74, 73, 1, 0}, +/* 75 */ { 10, s_44_75, -1, 1, 0}, +/* 76 */ { 4, s_44_76, -1, 1, 0}, +/* 77 */ { 8, s_44_77, 76, 1, 0}, +/* 78 */ { 12, s_44_78, 76, 1, 0}, +/* 79 */ { 10, s_44_79, 76, 1, 0}, +/* 80 */ { 6, s_44_80, -1, 1, 0}, +/* 81 */ { 6, s_44_81, -1, 1, 0}, +/* 82 */ { 14, s_44_82, 81, 1, 0}, +/* 83 */ { 14, s_44_83, 81, 1, 0}, +/* 84 */ { 12, s_44_84, 81, 1, 0}, +/* 85 */ { 12, s_44_85, -1, 1, 0}, +/* 86 */ { 6, s_44_86, -1, 1, 0}, +/* 87 */ { 12, s_44_87, -1, 1, 0}, +/* 88 */ { 2, s_44_88, -1, 1, 0}, +/* 89 */ { 14, s_44_89, 88, 1, 0}, +/* 90 */ { 10, s_44_90, 88, 1, 0}, +/* 91 */ { 16, s_44_91, 88, 1, 0}, +/* 92 */ { 16, s_44_92, 88, 1, 0}, +/* 93 */ { 2, s_44_93, -1, 1, 0}, +/* 94 */ { 16, s_44_94, 93, 1, 0} +}; + +static const symbol s_45_0[10] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB5 }; + +static const struct among a_45[1] = +{ +/* 0 */ { 10, s_45_0, -1, 1, 0} +}; + +static const symbol s_46_0[6] = { 0xCF, 0x80, 0xCF, 0x85, 0xCF, 0x81 }; +static const symbol s_46_1[6] = { 0xCE, 0xB5, 0xCF, 0x85, 0xCF, 0x81 }; +static const symbol s_46_2[6] = { 0xCF, 0x87, 0xCF, 0x89, 0xCF, 0x81 }; +static const symbol s_46_3[6] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_46_4[4] = { 0xCE, 0xB2, 0xCF, 0x81 }; +static const symbol s_46_5[6] = { 0xCE, 0xB1, 0xCE, 0xB9, 0xCF, 0x81 }; +static const symbol s_46_6[6] = { 0xCF, 0x86, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_46_7[6] = { 0xCE, 0xBD, 0xCE, 0xB5, 0xCF, 0x84 }; +static const symbol s_46_8[4] = { 0xCF, 0x83, 0xCF, 0x87 }; +static const symbol s_46_9[8] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB4 }; +static const symbol s_46_10[6] = { 0xCE, 0xB5, 0xCE, 0xBD, 0xCE, 0xB4 }; +static const symbol s_46_11[4] = { 0xCE, 0xBF, 0xCE, 0xB4 }; +static const symbol s_46_12[10] = { 0xCF, 0x85, 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB8 }; +static const symbol s_46_13[4] = { 0xCF, 0x83, 0xCE, 0xB8 }; +static const symbol s_46_14[6] = { 0xCE, 0xB5, 0xCF, 0x85, 0xCE, 0xB8 }; +static const symbol s_46_15[6] = { 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xB8 }; +static const symbol s_46_16[6] = { 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB8 }; +static const symbol s_46_17[8] = { 0xCE, 0xB4, 0xCE, 0xB9, 0xCE, 0xB1, 0xCE, 0xB8 }; +static const symbol s_46_18[6] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB8 }; +static const symbol s_46_19[6] = { 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xB8 }; +static const symbol s_46_20[6] = { 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xB8 }; +static const symbol s_46_21[8] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB8 }; +static const symbol s_46_22[6] = { 0xCE, 0xB5, 0xCE, 0xBD, 0xCE, 0xB8 }; +static const symbol s_46_23[6] = { 0xCF, 0x81, 0xCE, 0xBF, 0xCE, 0xB8 }; +static const symbol s_46_24[6] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xBA }; +static const symbol s_46_25[8] = { 0xCF, 0x89, 0xCF, 0x86, 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_46_26[6] = { 0xCE, 0xB2, 0xCE, 0xBF, 0xCE, 0xBB }; +static const symbol s_46_27[6] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_46_28[6] = { 0xCE, 0xB1, 0xCE, 0xB9, 0xCE, 0xBD }; +static const symbol s_46_29[6] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBD }; +static const symbol s_46_30[6] = { 0xCF, 0x81, 0xCE, 0xBF, 0xCE, 0xBD }; + +static const struct among a_46[31] = +{ +/* 0 */ { 6, s_46_0, -1, 1, 0}, +/* 1 */ { 6, s_46_1, -1, 1, 0}, +/* 2 */ { 6, s_46_2, -1, 1, 0}, +/* 3 */ { 6, s_46_3, -1, 1, 0}, +/* 4 */ { 4, s_46_4, -1, 1, 0}, +/* 5 */ { 6, s_46_5, -1, 1, 0}, +/* 6 */ { 6, s_46_6, -1, 1, 0}, +/* 7 */ { 6, s_46_7, -1, 1, 0}, +/* 8 */ { 4, s_46_8, -1, 1, 0}, +/* 9 */ { 8, s_46_9, -1, 1, 0}, +/* 10 */ { 6, s_46_10, -1, 1, 0}, +/* 11 */ { 4, s_46_11, -1, 1, 0}, +/* 12 */ { 10, s_46_12, -1, 1, 0}, +/* 13 */ { 4, s_46_13, -1, 1, 0}, +/* 14 */ { 6, s_46_14, -1, 1, 0}, +/* 15 */ { 6, s_46_15, -1, 1, 0}, +/* 16 */ { 6, s_46_16, -1, 1, 0}, +/* 17 */ { 8, s_46_17, -1, 1, 0}, +/* 18 */ { 6, s_46_18, -1, 1, 0}, +/* 19 */ { 6, s_46_19, -1, 1, 0}, +/* 20 */ { 6, s_46_20, -1, 1, 0}, +/* 21 */ { 8, s_46_21, -1, 1, 0}, +/* 22 */ { 6, s_46_22, -1, 1, 0}, +/* 23 */ { 6, s_46_23, -1, 1, 0}, +/* 24 */ { 6, s_46_24, -1, 1, 0}, +/* 25 */ { 8, s_46_25, -1, 1, 0}, +/* 26 */ { 6, s_46_26, -1, 1, 0}, +/* 27 */ { 6, s_46_27, -1, 1, 0}, +/* 28 */ { 6, s_46_28, -1, 1, 0}, +/* 29 */ { 6, s_46_29, -1, 1, 0}, +/* 30 */ { 6, s_46_30, -1, 1, 0} +}; + +static const symbol s_47_0[8] = { 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x81, 0xCF, 0x80 }; +static const symbol s_47_1[6] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_47_2[8] = { 0xCE, 0xB8, 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x81 }; +static const symbol s_47_3[6] = { 0xCE, 0xBD, 0xCF, 0x84, 0xCF, 0x81 }; +static const symbol s_47_4[8] = { 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_47_5[8] = { 0xCE, 0xB5, 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_47_6[6] = { 0xCE, 0xB1, 0xCE, 0xB2, 0xCF, 0x81 }; +static const symbol s_47_7[8] = { 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_47_8[2] = { 0xCF, 0x85 }; +static const symbol s_47_9[8] = { 0xCF, 0x83, 0xCF, 0x85, 0xCF, 0x81, 0xCF, 0x86 }; +static const symbol s_47_10[6] = { 0xCE, 0xBD, 0xCE, 0xB9, 0xCF, 0x86 }; +static const symbol s_47_11[6] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_47_12[2] = { 0xCE, 0xB4 }; +static const symbol s_47_13[4] = { 0xCE, 0xB1, 0xCE, 0xB4 }; +static const symbol s_47_14[2] = { 0xCE, 0xB8 }; +static const symbol s_47_15[4] = { 0xCE, 0xB1, 0xCE, 0xB8 }; +static const symbol s_47_16[4] = { 0xCF, 0x83, 0xCE, 0xBA }; +static const symbol s_47_17[6] = { 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xBA }; +static const symbol s_47_18[6] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBB }; +static const symbol s_47_19[14] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_47_20[8] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_47_21[4] = { 0xCE, 0xB5, 0xCE, 0xBC }; +static const symbol s_47_22[4] = { 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_47_23[6] = { 0xCE, 0xB2, 0xCE, 0xB5, 0xCE, 0xBD }; +static const symbol s_47_24[10] = { 0xCE, 0xB2, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xBF, 0xCE, 0xBD }; + +static const struct among a_47[25] = +{ +/* 0 */ { 8, s_47_0, -1, 1, 0}, +/* 1 */ { 6, s_47_1, -1, 1, 0}, +/* 2 */ { 8, s_47_2, -1, 1, 0}, +/* 3 */ { 6, s_47_3, -1, 1, 0}, +/* 4 */ { 8, s_47_4, -1, 1, 0}, +/* 5 */ { 8, s_47_5, -1, 1, 0}, +/* 6 */ { 6, s_47_6, -1, 1, 0}, +/* 7 */ { 8, s_47_7, -1, 1, 0}, +/* 8 */ { 2, s_47_8, -1, 1, 0}, +/* 9 */ { 8, s_47_9, -1, 1, 0}, +/* 10 */ { 6, s_47_10, -1, 1, 0}, +/* 11 */ { 6, s_47_11, -1, 1, 0}, +/* 12 */ { 2, s_47_12, -1, 1, 0}, +/* 13 */ { 4, s_47_13, 12, 1, 0}, +/* 14 */ { 2, s_47_14, -1, 1, 0}, +/* 15 */ { 4, s_47_15, 14, 1, 0}, +/* 16 */ { 4, s_47_16, -1, 1, 0}, +/* 17 */ { 6, s_47_17, -1, 1, 0}, +/* 18 */ { 6, s_47_18, -1, 1, 0}, +/* 19 */ { 14, s_47_19, -1, 1, 0}, +/* 20 */ { 8, s_47_20, -1, 1, 0}, +/* 21 */ { 4, s_47_21, -1, 1, 0}, +/* 22 */ { 4, s_47_22, -1, 1, 0}, +/* 23 */ { 6, s_47_23, -1, 1, 0}, +/* 24 */ { 10, s_47_24, -1, 1, 0} +}; + +static const symbol s_48_0[10] = { 0xCF, 0x89, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_48_1[10] = { 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x83 }; + +static const struct among a_48[2] = +{ +/* 0 */ { 10, s_48_0, -1, 1, 0}, +/* 1 */ { 10, s_48_1, -1, 1, 0} +}; + +static const symbol s_49_0[12] = { 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_49_1[14] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; + +static const struct among a_49[2] = +{ +/* 0 */ { 12, s_49_0, -1, 1, 0}, +/* 1 */ { 14, s_49_1, 0, 1, 0} +}; + +static const symbol s_50_0[2] = { 0xCF, 0x80 }; +static const symbol s_50_1[4] = { 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_50_2[12] = { 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x80 }; +static const symbol s_50_3[8] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBC, 0xCF, 0x80 }; +static const symbol s_50_4[10] = { 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBC, 0xCF, 0x80 }; +static const symbol s_50_5[14] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBC, 0xCF, 0x86 }; + +static const struct among a_50[6] = +{ +/* 0 */ { 2, s_50_0, -1, 1, 0}, +/* 1 */ { 4, s_50_1, 0, 1, 0}, +/* 2 */ { 12, s_50_2, 1, 1, 0}, +/* 3 */ { 8, s_50_3, 0, 1, 0}, +/* 4 */ { 10, s_50_4, 3, 1, 0}, +/* 5 */ { 14, s_50_5, -1, 1, 0} +}; + +static const symbol s_51_0[4] = { 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_51_1[6] = { 0xCE, 0xBD, 0xCE, 0xB9, 0xCF, 0x83 }; +static const symbol s_51_2[2] = { 0xCE, 0xB6 }; +static const symbol s_51_3[4] = { 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_51_4[14] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_51_5[10] = { 0xCE, 0xB5, 0xCE, 0xBA, 0xCF, 0x84, 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_51_6[2] = { 0xCE, 0xBC }; +static const symbol s_51_7[2] = { 0xCE, 0xBE }; +static const symbol s_51_8[6] = { 0xCF, 0x80, 0xCF, 0x81, 0xCE, 0xBF }; + +static const struct among a_51[9] = +{ +/* 0 */ { 4, s_51_0, -1, 1, 0}, +/* 1 */ { 6, s_51_1, -1, 1, 0}, +/* 2 */ { 2, s_51_2, -1, 1, 0}, +/* 3 */ { 4, s_51_3, -1, 1, 0}, +/* 4 */ { 14, s_51_4, 3, 1, 0}, +/* 5 */ { 10, s_51_5, -1, 1, 0}, +/* 6 */ { 2, s_51_6, -1, 1, 0}, +/* 7 */ { 2, s_51_7, -1, 1, 0}, +/* 8 */ { 6, s_51_8, -1, 1, 0} +}; + +static const symbol s_52_0[12] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_52_1[10] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1 }; +static const symbol s_52_2[10] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB5 }; + +static const struct among a_52[3] = +{ +/* 0 */ { 12, s_52_0, -1, 1, 0}, +/* 1 */ { 10, s_52_1, -1, 1, 0}, +/* 2 */ { 10, s_52_2, -1, 1, 0} +}; + +static const symbol s_53_0[4] = { 0xCF, 0x83, 0xCF, 0x86 }; +static const symbol s_53_1[8] = { 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB8 }; +static const symbol s_53_2[6] = { 0xCF, 0x80, 0xCE, 0xB9, 0xCE, 0xB8 }; +static const symbol s_53_3[4] = { 0xCE, 0xBF, 0xCE, 0xB8 }; +static const symbol s_53_4[10] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB }; +static const symbol s_53_5[8] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCF, 0x89, 0xCE, 0xBB }; + +static const struct among a_53[6] = +{ +/* 0 */ { 4, s_53_0, -1, 1, 0}, +/* 1 */ { 8, s_53_1, -1, 1, 0}, +/* 2 */ { 6, s_53_2, -1, 1, 0}, +/* 3 */ { 4, s_53_3, -1, 1, 0}, +/* 4 */ { 10, s_53_4, -1, 1, 0}, +/* 5 */ { 8, s_53_5, -1, 1, 0} +}; + +static const symbol s_54_0[2] = { 0xCE, 0xB8 }; +static const symbol s_54_1[10] = { 0xCF, 0x80, 0xCF, 0x81, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xB8 }; +static const symbol s_54_2[18] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB8 }; +static const symbol s_54_3[8] = { 0xCE, 0xB4, 0xCE, 0xB9, 0xCE, 0xB1, 0xCE, 0xB8 }; +static const symbol s_54_4[8] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB8 }; + +static const struct among a_54[5] = +{ +/* 0 */ { 2, s_54_0, -1, 1, 0}, +/* 1 */ { 10, s_54_1, 0, 1, 0}, +/* 2 */ { 18, s_54_2, 0, 1, 0}, +/* 3 */ { 8, s_54_3, 0, 1, 0}, +/* 4 */ { 8, s_54_4, 0, 1, 0} +}; + +static const symbol s_55_0[8] = { 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_55_1[6] = { 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1 }; +static const symbol s_55_2[6] = { 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB5 }; + +static const struct among a_55[3] = +{ +/* 0 */ { 8, s_55_0, -1, 1, 0}, +/* 1 */ { 6, s_55_1, -1, 1, 0}, +/* 2 */ { 6, s_55_2, -1, 1, 0} +}; + +static const symbol s_56_0[8] = { 0xCE, 0xB2, 0xCE, 0xBB, 0xCE, 0xB5, 0xCF, 0x80 }; +static const symbol s_56_1[10] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xB4, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_56_2[8] = { 0xCF, 0x80, 0xCF, 0x81, 0xCF, 0x89, 0xCF, 0x84 }; +static const symbol s_56_3[10] = { 0xCE, 0xBA, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_56_4[12] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x87 }; +static const symbol s_56_5[6] = { 0xCE, 0xBB, 0xCE, 0xB1, 0xCF, 0x87 }; +static const symbol s_56_6[6] = { 0xCF, 0x86, 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_56_7[6] = { 0xCE, 0xBB, 0xCE, 0xB7, 0xCE, 0xB3 }; +static const symbol s_56_8[8] = { 0xCF, 0x86, 0xCF, 0x81, 0xCF, 0x85, 0xCE, 0xB4 }; +static const symbol s_56_9[12] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB9, 0xCE, 0xBB }; +static const symbol s_56_10[8] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xBB }; +static const symbol s_56_11[4] = { 0xCE, 0xBF, 0xCE, 0xBC }; + +static const struct among a_56[12] = +{ +/* 0 */ { 8, s_56_0, -1, 1, 0}, +/* 1 */ { 10, s_56_1, -1, 1, 0}, +/* 2 */ { 8, s_56_2, -1, 1, 0}, +/* 3 */ { 10, s_56_3, -1, 1, 0}, +/* 4 */ { 12, s_56_4, -1, 1, 0}, +/* 5 */ { 6, s_56_5, -1, 1, 0}, +/* 6 */ { 6, s_56_6, -1, 1, 0}, +/* 7 */ { 6, s_56_7, -1, 1, 0}, +/* 8 */ { 8, s_56_8, -1, 1, 0}, +/* 9 */ { 12, s_56_9, -1, 1, 0}, +/* 10 */ { 8, s_56_10, -1, 1, 0}, +/* 11 */ { 4, s_56_11, -1, 1, 0} +}; + +static const symbol s_57_0[10] = { 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB9, 0xCF, 0x80 }; +static const symbol s_57_1[2] = { 0xCF, 0x81 }; +static const symbol s_57_2[10] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x81 }; +static const symbol s_57_3[16] = { 0xCE, 0xB5, 0xCE, 0xBD, 0xCE, 0xB4, 0xCE, 0xB9, 0xCE, 0xB1, 0xCF, 0x86, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_57_4[6] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_57_5[14] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB8, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB5, 0xCF, 0x85 }; +static const symbol s_57_6[16] = { 0xCE, 0xB4, 0xCE, 0xB5, 0xCF, 0x85, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB5, 0xCF, 0x85 }; +static const symbol s_57_7[6] = { 0xCE, 0xBB, 0xCE, 0xB5, 0xCF, 0x87 }; +static const symbol s_57_8[6] = { 0xCF, 0x84, 0xCF, 0x83, 0xCE, 0xB1 }; +static const symbol s_57_9[6] = { 0xCF, 0x87, 0xCE, 0xB1, 0xCE, 0xB4 }; +static const symbol s_57_10[6] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCE, 0xB4 }; +static const symbol s_57_11[12] = { 0xCE, 0xBB, 0xCE, 0xB1, 0xCE, 0xBC, 0xCF, 0x80, 0xCE, 0xB9, 0xCE, 0xB4 }; +static const symbol s_57_12[4] = { 0xCE, 0xB4, 0xCE, 0xB5 }; +static const symbol s_57_13[6] = { 0xCF, 0x80, 0xCE, 0xBB, 0xCE, 0xB5 }; +static const symbol s_57_14[10] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xB6 }; +static const symbol s_57_15[12] = { 0xCE, 0xB4, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xB6 }; +static const symbol s_57_16[6] = { 0xCE, 0xB1, 0xCE, 0xB9, 0xCE, 0xB8 }; +static const symbol s_57_17[12] = { 0xCF, 0x86, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBA }; +static const symbol s_57_18[6] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBA }; +static const symbol s_57_19[8] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB7, 0xCE, 0xBA }; +static const symbol s_57_20[2] = { 0xCE, 0xBB }; +static const symbol s_57_21[2] = { 0xCE, 0xBC }; +static const symbol s_57_22[4] = { 0xCE, 0xB1, 0xCE, 0xBC }; +static const symbol s_57_23[8] = { 0xCE, 0xB2, 0xCF, 0x81, 0xCE, 0xBF, 0xCE, 0xBC }; +static const symbol s_57_24[14] = { 0xCF, 0x85, 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB5, 0xCE, 0xB9, 0xCE, 0xBD }; + +static const struct among a_57[25] = +{ +/* 0 */ { 10, s_57_0, -1, 1, 0}, +/* 1 */ { 2, s_57_1, -1, 1, 0}, +/* 2 */ { 10, s_57_2, 1, 1, 0}, +/* 3 */ { 16, s_57_3, 1, 1, 0}, +/* 4 */ { 6, s_57_4, -1, 1, 0}, +/* 5 */ { 14, s_57_5, -1, 1, 0}, +/* 6 */ { 16, s_57_6, -1, 1, 0}, +/* 7 */ { 6, s_57_7, -1, 1, 0}, +/* 8 */ { 6, s_57_8, -1, 1, 0}, +/* 9 */ { 6, s_57_9, -1, 1, 0}, +/* 10 */ { 6, s_57_10, -1, 1, 0}, +/* 11 */ { 12, s_57_11, -1, 1, 0}, +/* 12 */ { 4, s_57_12, -1, 1, 0}, +/* 13 */ { 6, s_57_13, -1, 1, 0}, +/* 14 */ { 10, s_57_14, -1, 1, 0}, +/* 15 */ { 12, s_57_15, -1, 1, 0}, +/* 16 */ { 6, s_57_16, -1, 1, 0}, +/* 17 */ { 12, s_57_17, -1, 1, 0}, +/* 18 */ { 6, s_57_18, -1, 1, 0}, +/* 19 */ { 8, s_57_19, -1, 1, 0}, +/* 20 */ { 2, s_57_20, -1, 1, 0}, +/* 21 */ { 2, s_57_21, -1, 1, 0}, +/* 22 */ { 4, s_57_22, 21, 1, 0}, +/* 23 */ { 8, s_57_23, 21, 1, 0}, +/* 24 */ { 14, s_57_24, -1, 1, 0} +}; + +static const symbol s_58_0[10] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_58_1[8] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB1 }; +static const symbol s_58_2[8] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB5 }; + +static const struct among a_58[3] = +{ +/* 0 */ { 10, s_58_0, -1, 1, 0}, +/* 1 */ { 8, s_58_1, -1, 1, 0}, +/* 2 */ { 8, s_58_2, -1, 1, 0} +}; + +static const symbol s_59_0[6] = { 0xCF, 0x88, 0xCE, 0xBF, 0xCF, 0x86 }; +static const symbol s_59_1[12] = { 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x85, 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x87 }; + +static const struct among a_59[2] = +{ +/* 0 */ { 6, s_59_0, -1, -1, 0}, +/* 1 */ { 12, s_59_1, -1, -1, 0} +}; + +static const symbol s_60_0[4] = { 0xCF, 0x81, 0xCF, 0x80 }; +static const symbol s_60_1[4] = { 0xCF, 0x80, 0xCF, 0x81 }; +static const symbol s_60_2[4] = { 0xCF, 0x86, 0xCF, 0x81 }; +static const symbol s_60_3[8] = { 0xCF, 0x87, 0xCE, 0xBF, 0xCF, 0x81, 0xCF, 0x84 }; +static const symbol s_60_4[4] = { 0xCF, 0x83, 0xCF, 0x86 }; +static const symbol s_60_5[4] = { 0xCE, 0xBF, 0xCF, 0x86 }; +static const symbol s_60_6[6] = { 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x87 }; +static const symbol s_60_7[6] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCE, 0xBB }; +static const symbol s_60_8[4] = { 0xCE, 0xBB, 0xCE, 0xBB }; +static const symbol s_60_9[8] = { 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0xB7, 0xCE, 0xBD }; + +static const struct among a_60[10] = +{ +/* 0 */ { 4, s_60_0, -1, 1, 0}, +/* 1 */ { 4, s_60_1, -1, 1, 0}, +/* 2 */ { 4, s_60_2, -1, 1, 0}, +/* 3 */ { 8, s_60_3, -1, 1, 0}, +/* 4 */ { 4, s_60_4, -1, 1, 0}, +/* 5 */ { 4, s_60_5, -1, 1, 0}, +/* 6 */ { 6, s_60_6, -1, 1, 0}, +/* 7 */ { 6, s_60_7, -1, 1, 0}, +/* 8 */ { 4, s_60_8, -1, 1, 0}, +/* 9 */ { 8, s_60_9, -1, 1, 0} +}; + +static const symbol s_61_0[2] = { 0xCF, 0x80 }; +static const symbol s_61_1[6] = { 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x80 }; +static const symbol s_61_2[8] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x85, 0xCF, 0x80 }; +static const symbol s_61_3[10] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x84, 0xCE, 0xB9, 0xCF, 0x80 }; +static const symbol s_61_4[8] = { 0xCE, 0xB1, 0xCE, 0xB5, 0xCE, 0xB9, 0xCF, 0x80 }; +static const symbol s_61_5[8] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBC, 0xCF, 0x80 }; +static const symbol s_61_6[16] = { 0xCF, 0x80, 0xCF, 0x81, 0xCE, 0xBF, 0xCF, 0x83, 0xCF, 0x89, 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_61_7[14] = { 0xCF, 0x83, 0xCE, 0xB9, 0xCE, 0xB4, 0xCE, 0xB7, 0xCF, 0x81, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_61_8[12] = { 0xCE, 0xB4, 0xCF, 0x81, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_61_9[8] = { 0xCE, 0xBD, 0xCE, 0xB5, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_61_10[16] = { 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xBF, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_61_11[8] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_61_12[2] = { 0xCF, 0x81 }; +static const symbol s_61_13[4] = { 0xCF, 0x84, 0xCF, 0x81 }; +static const symbol s_61_14[6] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x81 }; +static const symbol s_61_15[10] = { 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_61_16[6] = { 0xCF, 0x87, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_61_17[8] = { 0xCE, 0xB1, 0xCF, 0x87, 0xCE, 0xB1, 0xCF, 0x81 }; +static const symbol s_61_18[8] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_61_19[2] = { 0xCF, 0x84 }; +static const symbol s_61_20[10] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x85, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_61_21[10] = { 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_61_22[10] = { 0xCF, 0x80, 0xCF, 0x81, 0xCE, 0xBF, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_61_23[12] = { 0xCE, 0xB1, 0xCE, 0xB9, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_61_24[8] = { 0xCE, 0xB4, 0xCE, 0xB9, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_61_25[8] = { 0xCE, 0xB5, 0xCF, 0x80, 0xCE, 0xB9, 0xCF, 0x84 }; +static const symbol s_61_26[8] = { 0xCF, 0x83, 0xCF, 0x85, 0xCE, 0xBD, 0xCF, 0x84 }; +static const symbol s_61_27[8] = { 0xCF, 0x85, 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x84 }; +static const symbol s_61_28[8] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBF, 0xCF, 0x84 }; +static const symbol s_61_29[8] = { 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x84 }; +static const symbol s_61_30[10] = { 0xCE, 0xBD, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x84 }; +static const symbol s_61_31[6] = { 0xCE, 0xBD, 0xCE, 0xB1, 0xCF, 0x85 }; +static const symbol s_61_32[10] = { 0xCF, 0x80, 0xCE, 0xBF, 0xCE, 0xBB, 0xCF, 0x85, 0xCF, 0x86 }; +static const symbol s_61_33[4] = { 0xCE, 0xB1, 0xCF, 0x86 }; +static const symbol s_61_34[6] = { 0xCE, 0xBE, 0xCE, 0xB5, 0xCF, 0x86 }; +static const symbol s_61_35[8] = { 0xCE, 0xB1, 0xCE, 0xB4, 0xCE, 0xB7, 0xCF, 0x86 }; +static const symbol s_61_36[8] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xBC, 0xCF, 0x86 }; +static const symbol s_61_37[12] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xBB, 0xCE, 0xB9 }; +static const symbol s_61_38[2] = { 0xCE, 0xBB }; +static const symbol s_61_39[8] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xBB }; +static const symbol s_61_40[2] = { 0xCE, 0xBC }; +static const symbol s_61_41[10] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBB, 0xCE, 0xB1, 0xCE, 0xBC }; +static const symbol s_61_42[4] = { 0xCE, 0xB5, 0xCE, 0xBD }; +static const symbol s_61_43[12] = { 0xCE, 0xB4, 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB2, 0xCE, 0xB5, 0xCE, 0xBD }; + +static const struct among a_61[44] = +{ +/* 0 */ { 2, s_61_0, -1, 1, 0}, +/* 1 */ { 6, s_61_1, 0, 1, 0}, +/* 2 */ { 8, s_61_2, 0, 1, 0}, +/* 3 */ { 10, s_61_3, 0, 1, 0}, +/* 4 */ { 8, s_61_4, 0, 1, 0}, +/* 5 */ { 8, s_61_5, 0, 1, 0}, +/* 6 */ { 16, s_61_6, 0, 1, 0}, +/* 7 */ { 14, s_61_7, 0, 1, 0}, +/* 8 */ { 12, s_61_8, 0, 1, 0}, +/* 9 */ { 8, s_61_9, 0, 1, 0}, +/* 10 */ { 16, s_61_10, 0, 1, 0}, +/* 11 */ { 8, s_61_11, 0, 1, 0}, +/* 12 */ { 2, s_61_12, -1, 1, 0}, +/* 13 */ { 4, s_61_13, 12, 1, 0}, +/* 14 */ { 6, s_61_14, 12, 1, 0}, +/* 15 */ { 10, s_61_15, 12, 1, 0}, +/* 16 */ { 6, s_61_16, 12, 1, 0}, +/* 17 */ { 8, s_61_17, 16, 1, 0}, +/* 18 */ { 8, s_61_18, 12, 1, 0}, +/* 19 */ { 2, s_61_19, -1, 1, 0}, +/* 20 */ { 10, s_61_20, 19, 1, 0}, +/* 21 */ { 10, s_61_21, 19, 1, 0}, +/* 22 */ { 10, s_61_22, 19, 1, 0}, +/* 23 */ { 12, s_61_23, 19, 1, 0}, +/* 24 */ { 8, s_61_24, 19, 1, 0}, +/* 25 */ { 8, s_61_25, 19, 1, 0}, +/* 26 */ { 8, s_61_26, 19, 1, 0}, +/* 27 */ { 8, s_61_27, 19, 1, 0}, +/* 28 */ { 8, s_61_28, 19, 1, 0}, +/* 29 */ { 8, s_61_29, 19, 1, 0}, +/* 30 */ { 10, s_61_30, 29, 1, 0}, +/* 31 */ { 6, s_61_31, -1, 1, 0}, +/* 32 */ { 10, s_61_32, -1, 1, 0}, +/* 33 */ { 4, s_61_33, -1, 1, 0}, +/* 34 */ { 6, s_61_34, -1, 1, 0}, +/* 35 */ { 8, s_61_35, -1, 1, 0}, +/* 36 */ { 8, s_61_36, -1, 1, 0}, +/* 37 */ { 12, s_61_37, -1, 1, 0}, +/* 38 */ { 2, s_61_38, -1, 1, 0}, +/* 39 */ { 8, s_61_39, 38, 1, 0}, +/* 40 */ { 2, s_61_40, -1, 1, 0}, +/* 41 */ { 10, s_61_41, 40, 1, 0}, +/* 42 */ { 4, s_61_42, -1, 1, 0}, +/* 43 */ { 12, s_61_43, 42, 1, 0} +}; + +static const symbol s_62_0[8] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_62_1[6] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1 }; +static const symbol s_62_2[6] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB5 }; + +static const struct among a_62[3] = +{ +/* 0 */ { 8, s_62_0, -1, 1, 0}, +/* 1 */ { 6, s_62_1, -1, 1, 0}, +/* 2 */ { 6, s_62_2, -1, 1, 0} +}; + +static const symbol s_63_0[8] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_63_1[6] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB1 }; +static const symbol s_63_2[6] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB5 }; + +static const struct among a_63[3] = +{ +/* 0 */ { 8, s_63_0, -1, 1, 0}, +/* 1 */ { 6, s_63_1, -1, 1, 0}, +/* 2 */ { 6, s_63_2, -1, 1, 0} +}; + +static const symbol s_64_0[2] = { 0xCE, 0xBD }; +static const symbol s_64_1[10] = { 0xCE, 0xB5, 0xCF, 0x80, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_64_2[14] = { 0xCE, 0xB4, 0xCF, 0x89, 0xCE, 0xB4, 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_64_3[12] = { 0xCF, 0x87, 0xCE, 0xB5, 0xCF, 0x81, 0xCF, 0x83, 0xCE, 0xBF, 0xCE, 0xBD }; +static const symbol s_64_4[14] = { 0xCE, 0xBC, 0xCE, 0xB5, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xBF, 0xCE, 0xBD }; +static const symbol s_64_5[12] = { 0xCE, 0xB5, 0xCF, 0x81, 0xCE, 0xB7, 0xCE, 0xBC, 0xCE, 0xBF, 0xCE, 0xBD }; + +static const struct among a_64[6] = +{ +/* 0 */ { 2, s_64_0, -1, 1, 0}, +/* 1 */ { 10, s_64_1, 0, 1, 0}, +/* 2 */ { 14, s_64_2, 0, 1, 0}, +/* 3 */ { 12, s_64_3, 0, 1, 0}, +/* 4 */ { 14, s_64_4, 0, 1, 0}, +/* 5 */ { 12, s_64_5, 0, 1, 0} +}; + +static const symbol s_65_0[8] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; + +static const struct among a_65[1] = +{ +/* 0 */ { 8, s_65_0, -1, 1, 0} +}; + +static const symbol s_66_0[4] = { 0xCF, 0x87, 0xCF, 0x81 }; +static const symbol s_66_1[10] = { 0xCE, 0xB4, 0xCF, 0x85, 0xCF, 0x83, 0xCF, 0x87, 0xCF, 0x81 }; +static const symbol s_66_2[8] = { 0xCE, 0xB5, 0xCF, 0x85, 0xCF, 0x87, 0xCF, 0x81 }; +static const symbol s_66_3[6] = { 0xCE, 0xB1, 0xCF, 0x87, 0xCF, 0x81 }; +static const symbol s_66_4[14] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCE, 0xB9, 0xCE, 0xBD, 0xCE, 0xBF, 0xCF, 0x87, 0xCF, 0x81 }; +static const symbol s_66_5[12] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xB9, 0xCE, 0xBC, 0xCF, 0x88 }; +static const symbol s_66_6[4] = { 0xCF, 0x83, 0xCE, 0xB2 }; +static const symbol s_66_7[6] = { 0xCE, 0xB1, 0xCF, 0x83, 0xCE, 0xB2 }; +static const symbol s_66_8[6] = { 0xCE, 0xB1, 0xCF, 0x80, 0xCE, 0xBB }; +static const symbol s_66_9[10] = { 0xCE, 0xB1, 0xCE, 0xB5, 0xCE, 0xB9, 0xCE, 0xBC, 0xCE, 0xBD }; + +static const struct among a_66[10] = +{ +/* 0 */ { 4, s_66_0, -1, 1, 0}, +/* 1 */ { 10, s_66_1, 0, 1, 0}, +/* 2 */ { 8, s_66_2, 0, 1, 0}, +/* 3 */ { 6, s_66_3, 0, 1, 0}, +/* 4 */ { 14, s_66_4, 0, 1, 0}, +/* 5 */ { 12, s_66_5, -1, 1, 0}, +/* 6 */ { 4, s_66_6, -1, 1, 0}, +/* 7 */ { 6, s_66_7, 6, 1, 0}, +/* 8 */ { 6, s_66_8, -1, 1, 0}, +/* 9 */ { 10, s_66_9, -1, 1, 0} +}; + +static const symbol s_67_0[8] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_67_1[12] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_67_2[12] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB5 }; + +static const struct among a_67[3] = +{ +/* 0 */ { 8, s_67_0, -1, 1, 0}, +/* 1 */ { 12, s_67_1, 0, 1, 0}, +/* 2 */ { 12, s_67_2, 0, 1, 0} +}; + +static const symbol s_68_0[2] = { 0xCF, 0x81 }; +static const symbol s_68_1[22] = { 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x84, 0xCF, 0x83 }; +static const symbol s_68_2[18] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBA, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x84, 0xCF, 0x83 }; +static const symbol s_68_3[6] = { 0xCF, 0x83, 0xCF, 0x80, 0xCE, 0xB9 }; +static const symbol s_68_4[2] = { 0xCE, 0xBD }; +static const symbol s_68_5[8] = { 0xCE, 0xB5, 0xCE, 0xBE, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_68[6] = +{ +/* 0 */ { 2, s_68_0, -1, 1, 0}, +/* 1 */ { 22, s_68_1, -1, 1, 0}, +/* 2 */ { 18, s_68_2, -1, 1, 0}, +/* 3 */ { 6, s_68_3, -1, 1, 0}, +/* 4 */ { 2, s_68_4, -1, 1, 0}, +/* 5 */ { 8, s_68_5, 4, 1, 0} +}; + +static const symbol s_69_0[8] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_69_1[12] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_69_2[12] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB5 }; + +static const struct among a_69[3] = +{ +/* 0 */ { 8, s_69_0, -1, 1, 0}, +/* 1 */ { 12, s_69_1, 0, 1, 0}, +/* 2 */ { 12, s_69_2, 0, 1, 0} +}; + +static const symbol s_70_0[10] = { 0xCE, 0xB1, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_70_1[16] = { 0xCF, 0x80, 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_70_2[16] = { 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xBB, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_70_3[2] = { 0xCF, 0x86 }; +static const symbol s_70_4[2] = { 0xCF, 0x87 }; +static const symbol s_70_5[4] = { 0xCE, 0xB1, 0xCE, 0xB6 }; +static const symbol s_70_6[12] = { 0xCF, 0x89, 0xCF, 0x81, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x80, 0xCE, 0xBB }; + +static const struct among a_70[7] = +{ +/* 0 */ { 10, s_70_0, -1, 1, 0}, +/* 1 */ { 16, s_70_1, 0, 1, 0}, +/* 2 */ { 16, s_70_2, -1, 1, 0}, +/* 3 */ { 2, s_70_3, -1, 1, 0}, +/* 4 */ { 2, s_70_4, -1, 1, 0}, +/* 5 */ { 4, s_70_5, -1, 1, 0}, +/* 6 */ { 12, s_70_6, -1, 1, 0} +}; + +static const symbol s_71_0[10] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_71_1[8] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1 }; +static const symbol s_71_2[10] = { 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x89, 0xCE, 0xBD }; + +static const struct among a_71[3] = +{ +/* 0 */ { 10, s_71_0, -1, 1, 0}, +/* 1 */ { 8, s_71_1, -1, 1, 0}, +/* 2 */ { 10, s_71_2, -1, 1, 0} +}; + +static const symbol s_72_0[4] = { 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_72_1[6] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_72_2[4] = { 0xCE, 0xB1, 0xCF, 0x83 }; +static const symbol s_72_3[4] = { 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_72_4[8] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_72_5[8] = { 0xCE, 0xB7, 0xCE, 0xB4, 0xCE, 0xB5, 0xCF, 0x83 }; +static const symbol s_72_6[4] = { 0xCE, 0xB7, 0xCF, 0x83 }; +static const symbol s_72_7[6] = { 0xCE, 0xB5, 0xCE, 0xB9, 0xCF, 0x83 }; +static const symbol s_72_8[10] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB5, 0xCE, 0xB9, 0xCF, 0x83 }; +static const symbol s_72_9[4] = { 0xCE, 0xBF, 0xCF, 0x83 }; +static const symbol s_72_10[2] = { 0xCF, 0x85 }; +static const symbol s_72_11[4] = { 0xCE, 0xBF, 0xCF, 0x85 }; +static const symbol s_72_12[2] = { 0xCF, 0x89 }; +static const symbol s_72_13[6] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCF, 0x89 }; +static const symbol s_72_14[4] = { 0xCE, 0xB1, 0xCF, 0x89 }; +static const symbol s_72_15[6] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCF, 0x89 }; +static const symbol s_72_16[2] = { 0xCE, 0xB1 }; +static const symbol s_72_17[10] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB1 }; +static const symbol s_72_18[12] = { 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1 }; +static const symbol s_72_19[14] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1 }; +static const symbol s_72_20[12] = { 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1 }; +static const symbol s_72_21[14] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCE, 0xB1 }; +static const symbol s_72_22[2] = { 0xCE, 0xB5 }; +static const symbol s_72_23[14] = { 0xCE, 0xB9, 0xCE, 0xB5, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_24[12] = { 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_25[14] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_26[14] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_27[16] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_28[14] = { 0xCE, 0xB9, 0xCE, 0xB5, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_29[12] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_30[10] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_31[10] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_32[10] = { 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_33[14] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_34[8] = { 0xCE, 0xB5, 0xCE, 0xB9, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_35[12] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB5, 0xCE, 0xB9, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_72_36[2] = { 0xCE, 0xB7 }; +static const symbol s_72_37[2] = { 0xCE, 0xB9 }; +static const symbol s_72_38[8] = { 0xCE, 0xB1, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_39[8] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_40[10] = { 0xCE, 0xB9, 0xCE, 0xB5, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_41[8] = { 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_42[8] = { 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_43[10] = { 0xCE, 0xB9, 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_44[12] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_45[14] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_46[10] = { 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_47[10] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_48[8] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_49[10] = { 0xCE, 0xB9, 0xCE, 0xB5, 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_50[8] = { 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB1, 0xCE, 0xB9 }; +static const symbol s_72_51[4] = { 0xCE, 0xB5, 0xCE, 0xB9 }; +static const symbol s_72_52[8] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB5, 0xCE, 0xB9 }; +static const symbol s_72_53[6] = { 0xCE, 0xB1, 0xCE, 0xB5, 0xCE, 0xB9 }; +static const symbol s_72_54[8] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB5, 0xCE, 0xB9 }; +static const symbol s_72_55[4] = { 0xCE, 0xBF, 0xCE, 0xB9 }; +static const symbol s_72_56[6] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_72_57[10] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_72_58[10] = { 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_72_59[12] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_72_60[10] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_72_61[10] = { 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_72_62[12] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_72_63[4] = { 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_72_64[8] = { 0xCE, 0xB7, 0xCE, 0xB4, 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_72_65[4] = { 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_66[10] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_67[16] = { 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_68[18] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_69[8] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_70[14] = { 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_71[16] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x83, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_72[14] = { 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_73[16] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_74[12] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_75[14] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_76[10] = { 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_77[12] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_78[8] = { 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_79[10] = { 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_80[8] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_81[8] = { 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_82[12] = { 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB7, 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_72_83[2] = { 0xCE, 0xBF }; + +static const struct among a_72[84] = +{ +/* 0 */ { 4, s_72_0, -1, 1, 0}, +/* 1 */ { 6, s_72_1, 0, 1, 0}, +/* 2 */ { 4, s_72_2, -1, 1, 0}, +/* 3 */ { 4, s_72_3, -1, 1, 0}, +/* 4 */ { 8, s_72_4, 3, 1, 0}, +/* 5 */ { 8, s_72_5, 3, 1, 0}, +/* 6 */ { 4, s_72_6, -1, 1, 0}, +/* 7 */ { 6, s_72_7, -1, 1, 0}, +/* 8 */ { 10, s_72_8, 7, 1, 0}, +/* 9 */ { 4, s_72_9, -1, 1, 0}, +/* 10 */ { 2, s_72_10, -1, 1, 0}, +/* 11 */ { 4, s_72_11, 10, 1, 0}, +/* 12 */ { 2, s_72_12, -1, 1, 0}, +/* 13 */ { 6, s_72_13, 12, 1, 0}, +/* 14 */ { 4, s_72_14, 12, 1, 0}, +/* 15 */ { 6, s_72_15, 12, 1, 0}, +/* 16 */ { 2, s_72_16, -1, 1, 0}, +/* 17 */ { 10, s_72_17, 16, 1, 0}, +/* 18 */ { 12, s_72_18, 16, 1, 0}, +/* 19 */ { 14, s_72_19, 18, 1, 0}, +/* 20 */ { 12, s_72_20, 16, 1, 0}, +/* 21 */ { 14, s_72_21, 20, 1, 0}, +/* 22 */ { 2, s_72_22, -1, 1, 0}, +/* 23 */ { 14, s_72_23, 22, 1, 0}, +/* 24 */ { 12, s_72_24, 22, 1, 0}, +/* 25 */ { 14, s_72_25, 24, 1, 0}, +/* 26 */ { 14, s_72_26, 22, 1, 0}, +/* 27 */ { 16, s_72_27, 26, 1, 0}, +/* 28 */ { 14, s_72_28, 22, 1, 0}, +/* 29 */ { 12, s_72_29, 22, 1, 0}, +/* 30 */ { 10, s_72_30, 22, 1, 0}, +/* 31 */ { 10, s_72_31, 22, 1, 0}, +/* 32 */ { 10, s_72_32, 22, 1, 0}, +/* 33 */ { 14, s_72_33, 32, 1, 0}, +/* 34 */ { 8, s_72_34, 22, 1, 0}, +/* 35 */ { 12, s_72_35, 34, 1, 0}, +/* 36 */ { 2, s_72_36, -1, 1, 0}, +/* 37 */ { 2, s_72_37, -1, 1, 0}, +/* 38 */ { 8, s_72_38, 37, 1, 0}, +/* 39 */ { 8, s_72_39, 37, 1, 0}, +/* 40 */ { 10, s_72_40, 39, 1, 0}, +/* 41 */ { 8, s_72_41, 37, 1, 0}, +/* 42 */ { 8, s_72_42, 37, 1, 0}, +/* 43 */ { 10, s_72_43, 42, 1, 0}, +/* 44 */ { 12, s_72_44, 37, 1, 0}, +/* 45 */ { 14, s_72_45, 44, 1, 0}, +/* 46 */ { 10, s_72_46, 37, 1, 0}, +/* 47 */ { 10, s_72_47, 37, 1, 0}, +/* 48 */ { 8, s_72_48, 37, 1, 0}, +/* 49 */ { 10, s_72_49, 37, 1, 0}, +/* 50 */ { 8, s_72_50, 37, 1, 0}, +/* 51 */ { 4, s_72_51, 37, 1, 0}, +/* 52 */ { 8, s_72_52, 51, 1, 0}, +/* 53 */ { 6, s_72_53, 51, 1, 0}, +/* 54 */ { 8, s_72_54, 51, 1, 0}, +/* 55 */ { 4, s_72_55, 37, 1, 0}, +/* 56 */ { 6, s_72_56, -1, 1, 0}, +/* 57 */ { 10, s_72_57, 56, 1, 0}, +/* 58 */ { 10, s_72_58, 56, 1, 0}, +/* 59 */ { 12, s_72_59, 58, 1, 0}, +/* 60 */ { 10, s_72_60, 56, 1, 0}, +/* 61 */ { 10, s_72_61, 56, 1, 0}, +/* 62 */ { 12, s_72_62, 61, 1, 0}, +/* 63 */ { 4, s_72_63, -1, 1, 0}, +/* 64 */ { 8, s_72_64, 63, 1, 0}, +/* 65 */ { 4, s_72_65, -1, 1, 0}, +/* 66 */ { 10, s_72_66, 65, 1, 0}, +/* 67 */ { 16, s_72_67, 66, 1, 0}, +/* 68 */ { 18, s_72_68, 67, 1, 0}, +/* 69 */ { 8, s_72_69, 65, 1, 0}, +/* 70 */ { 14, s_72_70, 65, 1, 0}, +/* 71 */ { 16, s_72_71, 70, 1, 0}, +/* 72 */ { 14, s_72_72, 65, 1, 0}, +/* 73 */ { 16, s_72_73, 72, 1, 0}, +/* 74 */ { 12, s_72_74, 65, 1, 0}, +/* 75 */ { 14, s_72_75, 74, 1, 0}, +/* 76 */ { 10, s_72_76, 65, 1, 0}, +/* 77 */ { 12, s_72_77, 76, 1, 0}, +/* 78 */ { 8, s_72_78, 65, 1, 0}, +/* 79 */ { 10, s_72_79, 78, 1, 0}, +/* 80 */ { 8, s_72_80, 65, 1, 0}, +/* 81 */ { 8, s_72_81, 65, 1, 0}, +/* 82 */ { 12, s_72_82, 81, 1, 0}, +/* 83 */ { 2, s_72_83, -1, 1, 0} +}; + +static const symbol s_73_0[10] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_73_1[8] = { 0xCF, 0x85, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_73_2[8] = { 0xCF, 0x89, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_73_3[8] = { 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_73_4[10] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_73_5[8] = { 0xCF, 0x85, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_73_6[8] = { 0xCF, 0x89, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x84 }; +static const symbol s_73_7[8] = { 0xCE, 0xBF, 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x84 }; + +static const struct among a_73[8] = +{ +/* 0 */ { 10, s_73_0, -1, 1, 0}, +/* 1 */ { 8, s_73_1, -1, 1, 0}, +/* 2 */ { 8, s_73_2, -1, 1, 0}, +/* 3 */ { 8, s_73_3, -1, 1, 0}, +/* 4 */ { 10, s_73_4, -1, 1, 0}, +/* 5 */ { 8, s_73_5, -1, 1, 0}, +/* 6 */ { 8, s_73_6, -1, 1, 0}, +/* 7 */ { 8, s_73_7, -1, 1, 0} +}; + +static const unsigned char g_v[] = { 81, 65, 16, 1 }; + +static const unsigned char g_v2[] = { 81, 65, 0, 1 }; + +static const symbol s_0[] = { 0xCE, 0xB1 }; +static const symbol s_1[] = { 0xCE, 0xB2 }; +static const symbol s_2[] = { 0xCE, 0xB3 }; +static const symbol s_3[] = { 0xCE, 0xB4 }; +static const symbol s_4[] = { 0xCE, 0xB5 }; +static const symbol s_5[] = { 0xCE, 0xB6 }; +static const symbol s_6[] = { 0xCE, 0xB7 }; +static const symbol s_7[] = { 0xCE, 0xB8 }; +static const symbol s_8[] = { 0xCE, 0xB9 }; +static const symbol s_9[] = { 0xCE, 0xBA }; +static const symbol s_10[] = { 0xCE, 0xBB }; +static const symbol s_11[] = { 0xCE, 0xBC }; +static const symbol s_12[] = { 0xCE, 0xBD }; +static const symbol s_13[] = { 0xCE, 0xBE }; +static const symbol s_14[] = { 0xCE, 0xBF }; +static const symbol s_15[] = { 0xCF, 0x80 }; +static const symbol s_16[] = { 0xCF, 0x81 }; +static const symbol s_17[] = { 0xCF, 0x83 }; +static const symbol s_18[] = { 0xCF, 0x84 }; +static const symbol s_19[] = { 0xCF, 0x85 }; +static const symbol s_20[] = { 0xCF, 0x86 }; +static const symbol s_21[] = { 0xCF, 0x87 }; +static const symbol s_22[] = { 0xCF, 0x88 }; +static const symbol s_23[] = { 0xCF, 0x89 }; +static const symbol s_24[] = { 0xCF, 0x86, 0xCE, 0xB1 }; +static const symbol s_25[] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xB1 }; +static const symbol s_26[] = { 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBF }; +static const symbol s_27[] = { 0xCF, 0x83, 0xCE, 0xBF }; +static const symbol s_28[] = { 0xCF, 0x84, 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF }; +static const symbol s_29[] = { 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xB5 }; +static const symbol s_30[] = { 0xCF, 0x80, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_31[] = { 0xCF, 0x84, 0xCE, 0xB5, 0xCF, 0x81 }; +static const symbol s_32[] = { 0xCF, 0x86, 0xCF, 0x89 }; +static const symbol s_33[] = { 0xCE, 0xBA, 0xCE, 0xB1, 0xCE, 0xB8, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_34[] = { 0xCE, 0xB3, 0xCE, 0xB5, 0xCE, 0xB3, 0xCE, 0xBF, 0xCE, 0xBD }; +static const symbol s_35[] = { 0xCE, 0xB9 }; +static const symbol s_36[] = { 0xCE, 0xB9, 0xCE, 0xB6 }; +static const symbol s_37[] = { 0xCF, 0x89, 0xCE, 0xBD }; +static const symbol s_38[] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xB1 }; +static const symbol s_39[] = { 0xCE, 0xB9, 0xCF, 0x83 }; +static const symbol s_40[] = { 0xCE, 0xB9 }; +static const symbol s_41[] = { 0xCE, 0xB9, 0xCF, 0x83 }; +static const symbol s_42[] = { 0xCE, 0xB9 }; +static const symbol s_43[] = { 0xCE, 0xB9 }; +static const symbol s_44[] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_45[] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBC }; +static const symbol s_46[] = { 0xCE, 0xB9 }; +static const symbol s_47[] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBD, 0xCF, 0x89, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_48[] = { 0xCE, 0xB1, 0xCF, 0x84, 0xCE, 0xBF, 0xCE, 0xBC }; +static const symbol s_49[] = { 0xCE, 0xB3, 0xCE, 0xBD, 0xCF, 0x89, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_50[] = { 0xCE, 0xB5, 0xCE, 0xB8, 0xCE, 0xBD }; +static const symbol s_51[] = { 0xCE, 0xB5, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xB5, 0xCE, 0xBA, 0xCF, 0x84 }; +static const symbol s_52[] = { 0xCF, 0x83, 0xCE, 0xBA, 0xCE, 0xB5, 0xCF, 0x80, 0xCF, 0x84 }; +static const symbol s_53[] = { 0xCF, 0x84, 0xCE, 0xBF, 0xCF, 0x80 }; +static const symbol s_54[] = { 0xCE, 0xB1, 0xCE, 0xBB, 0xCE, 0xB5, 0xCE, 0xBE, 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB4, 0xCF, 0x81 }; +static const symbol s_55[] = { 0xCE, 0xB2, 0xCF, 0x85, 0xCE, 0xB6, 0xCE, 0xB1, 0xCE, 0xBD, 0xCF, 0x84 }; +static const symbol s_56[] = { 0xCE, 0xB8, 0xCE, 0xB5, 0xCE, 0xB1, 0xCF, 0x84, 0xCF, 0x81 }; +static const symbol s_57[] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCE, 0xB1, 0xCE, 0xBA }; +static const symbol s_58[] = { 0xCE, 0xB1, 0xCE, 0xBA }; +static const symbol s_59[] = { 0xCE, 0xB9, 0xCF, 0x84, 0xCF, 0x83 }; +static const symbol s_60[] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCF, 0x81 }; +static const symbol s_61[] = { 0xCE, 0xB9, 0xCF, 0x84, 0xCF, 0x83 }; +static const symbol s_62[] = { 0xCE, 0xB9, 0xCE, 0xB4 }; +static const symbol s_63[] = { 0xCE, 0xB9, 0xCE, 0xB4 }; +static const symbol s_64[] = { 0xCE, 0xB9, 0xCF, 0x83, 0xCE, 0xBA }; +static const symbol s_65[] = { 0xCE, 0xB1, 0xCE, 0xB4 }; +static const symbol s_66[] = { 0xCE, 0xB5, 0xCE, 0xB4 }; +static const symbol s_67[] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xB4 }; +static const symbol s_68[] = { 0xCE, 0xB5 }; +static const symbol s_69[] = { 0xCE, 0xB9 }; +static const symbol s_70[] = { 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_71[] = { 0xCE, 0xB9, 0xCE, 0xBA }; +static const symbol s_72[] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_73[] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBC }; +static const symbol s_74[] = { 0xCE, 0xB1, 0xCE, 0xBC, 0xCE, 0xB5 }; +static const symbol s_75[] = { 0xCE, 0xB1, 0xCE, 0xBC }; +static const symbol s_76[] = { 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_77[] = { 0xCE, 0xB1, 0xCE, 0xBD, 0xCE, 0xB5 }; +static const symbol s_78[] = { 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_79[] = { 0xCE, 0xB1, 0xCE, 0xBD }; +static const symbol s_80[] = { 0xCE, 0xB5, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_81[] = { 0xCE, 0xB5, 0xCF, 0x84 }; +static const symbol s_82[] = { 0xCE, 0xB5, 0xCF, 0x84 }; +static const symbol s_83[] = { 0xCE, 0xB5, 0xCF, 0x84 }; +static const symbol s_84[] = { 0xCE, 0xB1, 0xCF, 0x81, 0xCF, 0x87 }; +static const symbol s_85[] = { 0xCE, 0xBF, 0xCE, 0xBD, 0xCF, 0x84 }; +static const symbol s_86[] = { 0xCE, 0xBA, 0xCF, 0x81, 0xCE, 0xB5 }; +static const symbol s_87[] = { 0xCF, 0x89, 0xCE, 0xBD, 0xCF, 0x84 }; +static const symbol s_88[] = { 0xCE, 0xBF, 0xCE, 0xBD }; +static const symbol s_89[] = { 0xCE, 0xBF, 0xCE, 0xBC, 0xCE, 0xB1, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_90[] = { 0xCE, 0xB9, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_91[] = { 0xCE, 0xB9, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_92[] = { 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84, 0xCE, 0xB5 }; +static const symbol s_93[] = { 0xCE, 0xB9, 0xCE, 0xB5, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_94[] = { 0xCE, 0xB7, 0xCE, 0xBA }; +static const symbol s_95[] = { 0xCE, 0xB7, 0xCE, 0xBA }; +static const symbol s_96[] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_97[] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCF, 0x83 }; +static const symbol s_98[] = { 0xCE, 0xBA, 0xCE, 0xBF, 0xCE, 0xBB, 0xCE, 0xBB }; +static const symbol s_99[] = { 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_100[] = { 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_101[] = { 0xCE, 0xB1, 0xCE, 0xB3 }; +static const symbol s_102[] = { 0xCE, 0xB7, 0xCF, 0x83 }; +static const symbol s_103[] = { 0xCE, 0xB7, 0xCF, 0x83, 0xCF, 0x84 }; +static const symbol s_104[] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBD }; +static const symbol s_105[] = { 0xCE, 0xBF, 0xCF, 0x85, 0xCE, 0xBC }; +static const symbol s_106[] = { 0xCE, 0xBC, 0xCE, 0xB1 }; + +static int r_has_min_length(struct SN_env * z) { /* backwardmode */ + if (!(len_utf8(z->p) >= 3)) return 0; /* $( >= ), line 111 */ + return 1; +} + +static int r_tolower(struct SN_env * z) { /* backwardmode */ + int among_var; + while(1) { /* repeat, line 115 */ + int m1 = z->l - z->c; (void)m1; + z->ket = z->c; /* [, line 116 */ + among_var = find_among_b(z, a_0, 46); /* substring, line 116 */ + if (!(among_var)) goto lab0; + z->bra = z->c; /* ], line 116 */ + switch (among_var) { /* among, line 116 */ + case 1: + { int ret = slice_from_s(z, 2, s_0); /* <-, line 117 */ + if (ret < 0) return ret; + } + break; + case 2: + { int ret = slice_from_s(z, 2, s_1); /* <-, line 118 */ + if (ret < 0) return ret; + } + break; + case 3: + { int ret = slice_from_s(z, 2, s_2); /* <-, line 119 */ + if (ret < 0) return ret; + } + break; + case 4: + { int ret = slice_from_s(z, 2, s_3); /* <-, line 120 */ + if (ret < 0) return ret; + } + break; + case 5: + { int ret = slice_from_s(z, 2, s_4); /* <-, line 121 */ + if (ret < 0) return ret; + } + break; + case 6: + { int ret = slice_from_s(z, 2, s_5); /* <-, line 122 */ + if (ret < 0) return ret; + } + break; + case 7: + { int ret = slice_from_s(z, 2, s_6); /* <-, line 123 */ + if (ret < 0) return ret; + } + break; + case 8: + { int ret = slice_from_s(z, 2, s_7); /* <-, line 124 */ + if (ret < 0) return ret; + } + break; + case 9: + { int ret = slice_from_s(z, 2, s_8); /* <-, line 125 */ + if (ret < 0) return ret; + } + break; + case 10: + { int ret = slice_from_s(z, 2, s_9); /* <-, line 126 */ + if (ret < 0) return ret; + } + break; + case 11: + { int ret = slice_from_s(z, 2, s_10); /* <-, line 127 */ + if (ret < 0) return ret; + } + break; + case 12: + { int ret = slice_from_s(z, 2, s_11); /* <-, line 128 */ + if (ret < 0) return ret; + } + break; + case 13: + { int ret = slice_from_s(z, 2, s_12); /* <-, line 129 */ + if (ret < 0) return ret; + } + break; + case 14: + { int ret = slice_from_s(z, 2, s_13); /* <-, line 130 */ + if (ret < 0) return ret; + } + break; + case 15: + { int ret = slice_from_s(z, 2, s_14); /* <-, line 131 */ + if (ret < 0) return ret; + } + break; + case 16: + { int ret = slice_from_s(z, 2, s_15); /* <-, line 132 */ + if (ret < 0) return ret; + } + break; + case 17: + { int ret = slice_from_s(z, 2, s_16); /* <-, line 133 */ + if (ret < 0) return ret; + } + break; + case 18: + { int ret = slice_from_s(z, 2, s_17); /* <-, line 134 */ + if (ret < 0) return ret; + } + break; + case 19: + { int ret = slice_from_s(z, 2, s_18); /* <-, line 135 */ + if (ret < 0) return ret; + } + break; + case 20: + { int ret = slice_from_s(z, 2, s_19); /* <-, line 136 */ + if (ret < 0) return ret; + } + break; + case 21: + { int ret = slice_from_s(z, 2, s_20); /* <-, line 137 */ + if (ret < 0) return ret; + } + break; + case 22: + { int ret = slice_from_s(z, 2, s_21); /* <-, line 138 */ + if (ret < 0) return ret; + } + break; + case 23: + { int ret = slice_from_s(z, 2, s_22); /* <-, line 139 */ + if (ret < 0) return ret; + } + break; + case 24: + { int ret = slice_from_s(z, 2, s_23); /* <-, line 140 */ + if (ret < 0) return ret; + } + break; + case 25: + { int ret = skip_utf8(z->p, z->c, z->lb, 0, -1); + if (ret < 0) goto lab0; + z->c = ret; /* next, line 162 */ + } + break; + } + continue; + lab0: + z->c = z->l - m1; + break; + } + return 1; +} + +static int r_step1(struct SN_env * z) { /* backwardmode */ + int among_var; + z->ket = z->c; /* [, line 168 */ + among_var = find_among_b(z, a_1, 40); /* substring, line 168 */ + if (!(among_var)) return 0; + z->bra = z->c; /* ], line 168 */ + switch (among_var) { /* among, line 168 */ + case 1: + { int ret = slice_from_s(z, 4, s_24); /* <-, line 169 */ + if (ret < 0) return ret; + } + break; + case 2: + { int ret = slice_from_s(z, 6, s_25); /* <-, line 170 */ + if (ret < 0) return ret; + } + break; + case 3: + { int ret = slice_from_s(z, 6, s_26); /* <-, line 171 */ + if (ret < 0) return ret; + } + break; + case 4: + { int ret = slice_from_s(z, 4, s_27); /* <-, line 172 */ + if (ret < 0) return ret; + } + break; + case 5: + { int ret = slice_from_s(z, 8, s_28); /* <-, line 173 */ + if (ret < 0) return ret; + } + break; + case 6: + { int ret = slice_from_s(z, 6, s_29); /* <-, line 174 */ + if (ret < 0) return ret; + } + break; + case 7: + { int ret = slice_from_s(z, 6, s_30); /* <-, line 175 */ + if (ret < 0) return ret; + } + break; + case 8: + { int ret = slice_from_s(z, 6, s_31); /* <-, line 176 */ + if (ret < 0) return ret; + } + break; + case 9: + { int ret = slice_from_s(z, 4, s_32); /* <-, line 177 */ + if (ret < 0) return ret; + } + break; + case 10: + { int ret = slice_from_s(z, 12, s_33); /* <-, line 178 */ + if (ret < 0) return ret; + } + break; + case 11: + { int ret = slice_from_s(z, 10, s_34); /* <-, line 179 */ + if (ret < 0) return ret; + } + break; + } + z->B[0] = 0; /* unset test1, line 181 */ + return 1; +} + +static int r_steps1(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 185 */ + if (!(find_among_b(z, a_4, 14))) return 0; /* substring, line 185 */ + z->bra = z->c; /* ], line 185 */ + { int ret = slice_del(z); /* delete, line 188 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 189 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 193 */ + z->ket = z->c; /* [, line 190 */ + if (z->c - 3 <= z->lb || z->p[z->c - 1] >> 5 != 5 || !((-2145255424 >> (z->p[z->c - 1] & 0x1f)) & 1)) goto lab1; /* substring, line 190 */ + if (!(find_among_b(z, a_2, 9))) goto lab1; + z->bra = z->c; /* ], line 190 */ + if (z->c > z->lb) goto lab1; /* atlimit, line 190 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 192 */ + if (z->S[0] == 0) return -1; /* -> s, line 192 */ + { int ret = slice_from_s(z, 2, s_35); /* <-, line 192 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 192 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 194 */ + if (!(find_among_b(z, a_3, 22))) return 0; /* substring, line 194 */ + z->bra = z->c; /* ], line 194 */ + if (z->c > z->lb) return 0; /* atlimit, line 194 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 198 */ + if (z->S[0] == 0) return -1; /* -> s, line 198 */ + { int ret = slice_from_s(z, 4, s_36); /* <-, line 198 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 198 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } +lab0: + return 1; +} + +static int r_steps2(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 205 */ + if (!(find_among_b(z, a_6, 7))) return 0; /* substring, line 205 */ + z->bra = z->c; /* ], line 205 */ + { int ret = slice_del(z); /* delete, line 207 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 208 */ + z->ket = z->c; /* [, line 209 */ + if (!(find_among_b(z, a_5, 8))) return 0; /* substring, line 209 */ + z->bra = z->c; /* ], line 209 */ + if (z->c > z->lb) return 0; /* atlimit, line 209 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 210 */ + if (z->S[0] == 0) return -1; /* -> s, line 210 */ + { int ret = slice_from_s(z, 4, s_37); /* <-, line 210 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 210 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_steps3(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 217 */ + if (!(find_among_b(z, a_9, 7))) return 0; /* substring, line 217 */ + z->bra = z->c; /* ], line 217 */ + { int ret = slice_del(z); /* delete, line 219 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 220 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 221 */ + if (!(eq_s_b(z, 6, s_38))) goto lab1; /* literal, line 221 */ + if (z->c > z->lb) goto lab1; /* atlimit, line 221 */ + { int ret = slice_from_s(z, 4, s_39); /* <-, line 221 */ + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 222 */ + if (z->c - 3 <= z->lb || z->p[z->c - 1] >> 5 != 5 || !((-2145255424 >> (z->p[z->c - 1] & 0x1f)) & 1)) goto lab2; /* substring, line 222 */ + if (!(find_among_b(z, a_7, 19))) goto lab2; + z->bra = z->c; /* ], line 222 */ + if (z->c > z->lb) goto lab2; /* atlimit, line 222 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 226 */ + if (z->S[0] == 0) return -1; /* -> s, line 226 */ + { int ret = slice_from_s(z, 2, s_40); /* <-, line 226 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 226 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab2: + z->c = z->l - m1; + z->ket = z->c; /* [, line 228 */ + if (!(find_among_b(z, a_8, 13))) return 0; /* substring, line 228 */ + z->bra = z->c; /* ], line 228 */ + if (z->c > z->lb) return 0; /* atlimit, line 228 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 231 */ + if (z->S[0] == 0) return -1; /* -> s, line 231 */ + { int ret = slice_from_s(z, 4, s_41); /* <-, line 231 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 231 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } +lab0: + return 1; +} + +static int r_steps4(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 238 */ + if (!(find_among_b(z, a_11, 7))) return 0; /* substring, line 238 */ + z->bra = z->c; /* ], line 238 */ + { int ret = slice_del(z); /* delete, line 240 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 241 */ + z->ket = z->c; /* [, line 242 */ + if (z->c - 3 <= z->lb || z->p[z->c - 1] >> 5 != 5 || !((-2145255424 >> (z->p[z->c - 1] & 0x1f)) & 1)) return 0; /* substring, line 242 */ + if (!(find_among_b(z, a_10, 19))) return 0; + z->bra = z->c; /* ], line 242 */ + if (z->c > z->lb) return 0; /* atlimit, line 242 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 246 */ + if (z->S[0] == 0) return -1; /* -> s, line 246 */ + { int ret = slice_from_s(z, 2, s_42); /* <-, line 246 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 246 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_steps5(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 253 */ + if (!(find_among_b(z, a_14, 11))) return 0; /* substring, line 253 */ + z->bra = z->c; /* ], line 253 */ + { int ret = slice_del(z); /* delete, line 256 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 257 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 261 */ + z->ket = z->c; /* [, line 258 */ + if (z->c - 3 <= z->lb || (z->p[z->c - 1] != 181 && z->p[z->c - 1] != 191)) goto lab1; /* substring, line 258 */ + if (!(find_among_b(z, a_12, 7))) goto lab1; + z->bra = z->c; /* ], line 258 */ + if (z->c > z->lb) goto lab1; /* atlimit, line 258 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 260 */ + if (z->S[0] == 0) return -1; /* -> s, line 260 */ + { int ret = slice_from_s(z, 2, s_43); /* <-, line 260 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 260 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 262 */ + if (!(find_among_b(z, a_13, 33))) return 0; /* substring, line 262 */ + z->bra = z->c; /* ], line 262 */ + if (z->c > z->lb) return 0; /* atlimit, line 262 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 266 */ + if (z->S[0] == 0) return -1; /* -> s, line 266 */ + { int ret = slice_from_s(z, 6, s_44); /* <-, line 266 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 266 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } +lab0: + return 1; +} + +static int r_steps6(struct SN_env * z) { /* backwardmode */ + int among_var; + z->ket = z->c; /* [, line 273 */ + if (!(find_among_b(z, a_18, 6))) return 0; /* substring, line 273 */ + z->bra = z->c; /* ], line 273 */ + { int ret = slice_del(z); /* delete, line 275 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 276 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 280 */ + z->ket = z->c; /* [, line 277 */ + if (z->c - 3 <= z->lb || z->p[z->c - 1] != 181) goto lab1; /* substring, line 277 */ + if (!(find_among_b(z, a_15, 5))) goto lab1; + z->bra = z->c; /* ], line 277 */ + if (z->c > z->lb) goto lab1; /* atlimit, line 277 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 279 */ + if (z->S[0] == 0) return -1; /* -> s, line 279 */ + { int ret = slice_from_s(z, 6, s_45); /* <-, line 279 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 279 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 281 */ + if (z->c - 7 <= z->lb || z->p[z->c - 1] != 181) goto lab2; /* substring, line 281 */ + if (!(find_among_b(z, a_16, 2))) goto lab2; + z->bra = z->c; /* ], line 281 */ + if (z->c > z->lb) goto lab2; /* atlimit, line 281 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 283 */ + if (z->S[0] == 0) return -1; /* -> s, line 283 */ + { int ret = slice_from_s(z, 2, s_46); /* <-, line 283 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 283 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab2: + z->c = z->l - m1; + z->ket = z->c; /* [, line 285 */ + if (z->c - 9 <= z->lb || (z->p[z->c - 1] != 186 && z->p[z->c - 1] != 189)) return 0; /* substring, line 285 */ + among_var = find_among_b(z, a_17, 10); + if (!(among_var)) return 0; + z->bra = z->c; /* ], line 285 */ + switch (among_var) { /* among, line 285 */ + case 1: + { int ret = slice_from_s(z, 12, s_47); /* <-, line 286 */ + if (ret < 0) return ret; + } + break; + case 2: + { int ret = slice_from_s(z, 8, s_48); /* <-, line 287 */ + if (ret < 0) return ret; + } + break; + case 3: + { int ret = slice_from_s(z, 10, s_49); /* <-, line 288 */ + if (ret < 0) return ret; + } + break; + case 4: + { int ret = slice_from_s(z, 6, s_50); /* <-, line 289 */ + if (ret < 0) return ret; + } + break; + case 5: + { int ret = slice_from_s(z, 12, s_51); /* <-, line 290 */ + if (ret < 0) return ret; + } + break; + case 6: + { int ret = slice_from_s(z, 10, s_52); /* <-, line 291 */ + if (ret < 0) return ret; + } + break; + case 7: + { int ret = slice_from_s(z, 6, s_53); /* <-, line 292 */ + if (ret < 0) return ret; + } + break; + case 8: + { int ret = slice_from_s(z, 16, s_54); /* <-, line 293 */ + if (ret < 0) return ret; + } + break; + case 9: + { int ret = slice_from_s(z, 12, s_55); /* <-, line 294 */ + if (ret < 0) return ret; + } + break; + case 10: + { int ret = slice_from_s(z, 10, s_56); /* <-, line 295 */ + if (ret < 0) return ret; + } + break; + } + } +lab0: + return 1; +} + +static int r_steps7(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 302 */ + if (z->c - 9 <= z->lb || (z->p[z->c - 1] != 177 && z->p[z->c - 1] != 185)) return 0; /* substring, line 302 */ + if (!(find_among_b(z, a_20, 4))) return 0; + z->bra = z->c; /* ], line 302 */ + { int ret = slice_del(z); /* delete, line 304 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 305 */ + z->ket = z->c; /* [, line 306 */ + if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 131 && z->p[z->c - 1] != 135)) return 0; /* substring, line 306 */ + if (!(find_among_b(z, a_19, 2))) return 0; + z->bra = z->c; /* ], line 306 */ + if (z->c > z->lb) return 0; /* atlimit, line 306 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 308 */ + if (z->S[0] == 0) return -1; /* -> s, line 308 */ + { int ret = slice_from_s(z, 8, s_57); /* <-, line 308 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 308 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_steps8(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 315 */ + if (!(find_among_b(z, a_23, 8))) return 0; /* substring, line 315 */ + z->bra = z->c; /* ], line 315 */ + { int ret = slice_del(z); /* delete, line 317 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 318 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 325 */ + z->ket = z->c; /* [, line 319 */ + if (!(find_among_b(z, a_21, 33))) goto lab1; /* substring, line 319 */ + z->bra = z->c; /* ], line 319 */ + if (z->c > z->lb) goto lab1; /* atlimit, line 319 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 324 */ + if (z->S[0] == 0) return -1; /* -> s, line 324 */ + { int ret = slice_from_s(z, 4, s_58); /* <-, line 324 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 324 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 326 */ + if (!(find_among_b(z, a_22, 15))) goto lab2; /* substring, line 326 */ + z->bra = z->c; /* ], line 326 */ + if (z->c > z->lb) goto lab2; /* atlimit, line 326 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 329 */ + if (z->S[0] == 0) return -1; /* -> s, line 329 */ + { int ret = slice_from_s(z, 6, s_59); /* <-, line 329 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 329 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab2: + z->c = z->l - m1; + z->ket = z->c; /* [, line 331 */ + if (!(eq_s_b(z, 6, s_60))) return 0; /* literal, line 331 */ + z->bra = z->c; /* ], line 331 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 331 */ + if (z->S[0] == 0) return -1; /* -> s, line 331 */ + { int ret = slice_from_s(z, 6, s_61); /* <-, line 331 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 331 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } +lab0: + return 1; +} + +static int r_steps9(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 337 */ + if (z->c - 7 <= z->lb || z->p[z->c - 1] >> 5 != 5 || !((-1610481664 >> (z->p[z->c - 1] & 0x1f)) & 1)) return 0; /* substring, line 337 */ + if (!(find_among_b(z, a_26, 3))) return 0; + z->bra = z->c; /* ], line 337 */ + { int ret = slice_del(z); /* delete, line 339 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 340 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 343 */ + z->ket = z->c; /* [, line 341 */ + if (!(find_among_b(z, a_24, 4))) goto lab1; /* substring, line 341 */ + z->bra = z->c; /* ], line 341 */ + if (z->c > z->lb) goto lab1; /* atlimit, line 341 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 342 */ + if (z->S[0] == 0) return -1; /* -> s, line 342 */ + { int ret = slice_from_s(z, 4, s_62); /* <-, line 342 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 342 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 344 */ + if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 181 && z->p[z->c - 1] != 189)) return 0; /* substring, line 344 */ + if (!(find_among_b(z, a_25, 2))) return 0; + z->bra = z->c; /* ], line 344 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 345 */ + if (z->S[0] == 0) return -1; /* -> s, line 345 */ + { int ret = slice_from_s(z, 4, s_63); /* <-, line 345 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 345 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } +lab0: + return 1; +} + +static int r_steps10(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 352 */ + if (!(find_among_b(z, a_28, 4))) return 0; /* substring, line 352 */ + z->bra = z->c; /* ], line 352 */ + { int ret = slice_del(z); /* delete, line 354 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 355 */ + z->ket = z->c; /* [, line 356 */ + if (!(find_among_b(z, a_27, 7))) return 0; /* substring, line 356 */ + z->bra = z->c; /* ], line 356 */ + if (z->c > z->lb) return 0; /* atlimit, line 356 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 358 */ + if (z->S[0] == 0) return -1; /* -> s, line 358 */ + { int ret = slice_from_s(z, 6, s_64); /* <-, line 358 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 358 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step2a(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 365 */ + if (z->c - 7 <= z->lb || (z->p[z->c - 1] != 131 && z->p[z->c - 1] != 189)) return 0; /* substring, line 365 */ + if (!(find_among_b(z, a_29, 2))) return 0; + z->bra = z->c; /* ], line 365 */ + { int ret = slice_del(z); /* delete, line 366 */ + if (ret < 0) return ret; + } + { int m1 = z->l - z->c; (void)m1; /* not, line 368 */ + z->ket = z->c; /* [, line 368 */ + if (!(find_among_b(z, a_30, 10))) goto lab0; /* substring, line 368 */ + z->bra = z->c; /* ], line 368 */ + return 0; + lab0: + z->c = z->l - m1; + } + { int ret; + { int saved_c = z->c; + ret = insert_s(z, z->c, z->c, 4, s_65); /* <+, line 371 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step2b(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 375 */ + if (z->c - 7 <= z->lb || (z->p[z->c - 1] != 131 && z->p[z->c - 1] != 189)) return 0; /* substring, line 375 */ + if (!(find_among_b(z, a_31, 2))) return 0; + z->bra = z->c; /* ], line 375 */ + { int ret = slice_del(z); /* delete, line 376 */ + if (ret < 0) return ret; + } + z->ket = z->c; /* [, line 378 */ + if (z->c - 3 <= z->lb || (z->p[z->c - 1] != 128 && z->p[z->c - 1] != 187)) return 0; /* substring, line 378 */ + if (!(find_among_b(z, a_32, 8))) return 0; + z->bra = z->c; /* ], line 378 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 379 */ + if (z->S[0] == 0) return -1; /* -> s, line 379 */ + { int ret = slice_from_s(z, 4, s_66); /* <-, line 379 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 379 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step2c(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 384 */ + if (z->c - 9 <= z->lb || (z->p[z->c - 1] != 131 && z->p[z->c - 1] != 189)) return 0; /* substring, line 384 */ + if (!(find_among_b(z, a_33, 2))) return 0; + z->bra = z->c; /* ], line 384 */ + { int ret = slice_del(z); /* delete, line 385 */ + if (ret < 0) return ret; + } + z->ket = z->c; /* [, line 387 */ + if (!(find_among_b(z, a_34, 15))) return 0; /* substring, line 387 */ + z->bra = z->c; /* ], line 387 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 389 */ + if (z->S[0] == 0) return -1; /* -> s, line 389 */ + { int ret = slice_from_s(z, 6, s_67); /* <-, line 389 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 389 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step2d(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 394 */ + if (z->c - 5 <= z->lb || (z->p[z->c - 1] != 131 && z->p[z->c - 1] != 189)) return 0; /* substring, line 394 */ + if (!(find_among_b(z, a_35, 2))) return 0; + z->bra = z->c; /* ], line 394 */ + { int ret = slice_del(z); /* delete, line 395 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 395 */ + z->ket = z->c; /* [, line 397 */ + if (!(find_among_b(z, a_36, 8))) return 0; /* substring, line 397 */ + z->bra = z->c; /* ], line 397 */ + if (z->c > z->lb) return 0; /* atlimit, line 397 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 398 */ + if (z->S[0] == 0) return -1; /* -> s, line 398 */ + { int ret = slice_from_s(z, 2, s_68); /* <-, line 398 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 398 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step3(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 403 */ + if (!(find_among_b(z, a_37, 3))) return 0; /* substring, line 403 */ + z->bra = z->c; /* ], line 403 */ + { int ret = slice_del(z); /* delete, line 404 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 404 */ + z->ket = z->c; /* [, line 406 */ + if (in_grouping_b_U(z, g_v, 945, 969, 0)) return 0; /* grouping v, line 406 */ + z->bra = z->c; /* ], line 406 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 406 */ + if (z->S[0] == 0) return -1; /* -> s, line 406 */ + { int ret = slice_from_s(z, 2, s_69); /* <-, line 406 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 406 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step4(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 410 */ + if (!(find_among_b(z, a_38, 4))) return 0; /* substring, line 410 */ + z->bra = z->c; /* ], line 410 */ + { int ret = slice_del(z); /* delete, line 411 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 411 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 413 */ + z->ket = z->c; /* [, line 413 */ + if (in_grouping_b_U(z, g_v, 945, 969, 0)) goto lab1; /* grouping v, line 413 */ + z->bra = z->c; /* ], line 413 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 413 */ + if (z->S[0] == 0) return -1; /* -> s, line 413 */ + { int ret = slice_from_s(z, 4, s_70); /* <-, line 413 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 413 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 414 */ + } +lab0: + if (!(find_among_b(z, a_39, 36))) return 0; /* substring, line 414 */ + z->bra = z->c; /* ], line 414 */ + if (z->c > z->lb) return 0; /* atlimit, line 414 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 419 */ + if (z->S[0] == 0) return -1; /* -> s, line 419 */ + { int ret = slice_from_s(z, 4, s_71); /* <-, line 419 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 419 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5a(struct SN_env * z) { /* backwardmode */ + { int m1 = z->l - z->c; (void)m1; /* do, line 424 */ + if (!(eq_s_b(z, 10, s_72))) goto lab0; /* literal, line 424 */ + if (z->c > z->lb) goto lab0; /* atlimit, line 424 */ + { int ret = slice_from_s(z, 8, s_73); /* <-, line 424 */ + if (ret < 0) return ret; + } + lab0: + z->c = z->l - m1; + } + { int m2 = z->l - z->c; (void)m2; /* do, line 425 */ + z->ket = z->c; /* [, line 426 */ + if (z->c - 9 <= z->lb || z->p[z->c - 1] != 181) goto lab1; /* substring, line 426 */ + if (!(find_among_b(z, a_40, 5))) goto lab1; + z->bra = z->c; /* ], line 426 */ + { int ret = slice_del(z); /* delete, line 427 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 427 */ + lab1: + z->c = z->l - m2; + } + z->ket = z->c; /* [, line 430 */ + if (!(eq_s_b(z, 6, s_74))) return 0; /* literal, line 430 */ + z->bra = z->c; /* ], line 430 */ + { int ret = slice_del(z); /* delete, line 431 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 432 */ + z->ket = z->c; /* [, line 433 */ + if (!(find_among_b(z, a_41, 12))) return 0; /* substring, line 433 */ + z->bra = z->c; /* ], line 433 */ + if (z->c > z->lb) return 0; /* atlimit, line 433 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 435 */ + if (z->S[0] == 0) return -1; /* -> s, line 435 */ + { int ret = slice_from_s(z, 4, s_75); /* <-, line 435 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 435 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5b(struct SN_env * z) { /* backwardmode */ + { int m1 = z->l - z->c; (void)m1; /* do, line 440 */ + z->ket = z->c; /* [, line 441 */ + if (z->c - 9 <= z->lb || z->p[z->c - 1] != 181) goto lab0; /* substring, line 441 */ + if (!(find_among_b(z, a_43, 11))) goto lab0; + z->bra = z->c; /* ], line 441 */ + { int ret = slice_del(z); /* delete, line 444 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 445 */ + z->ket = z->c; /* [, line 446 */ + if (z->c - 3 <= z->lb || (z->p[z->c - 1] != 129 && z->p[z->c - 1] != 131)) goto lab0; /* substring, line 446 */ + if (!(find_among_b(z, a_42, 2))) goto lab0; + z->bra = z->c; /* ], line 446 */ + if (z->c > z->lb) goto lab0; /* atlimit, line 446 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 447 */ + if (z->S[0] == 0) return -1; /* -> s, line 447 */ + { int ret = slice_from_s(z, 8, s_76); /* <-, line 447 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 447 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + lab0: + z->c = z->l - m1; + } + z->ket = z->c; /* [, line 452 */ + if (!(eq_s_b(z, 6, s_77))) return 0; /* literal, line 452 */ + z->bra = z->c; /* ], line 452 */ + { int ret = slice_del(z); /* delete, line 453 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 454 */ + { int m2 = z->l - z->c; (void)m2; /* or, line 455 */ + z->ket = z->c; /* [, line 455 */ + if (in_grouping_b_U(z, g_v2, 945, 969, 0)) goto lab2; /* grouping v2, line 455 */ + z->bra = z->c; /* ], line 455 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 455 */ + if (z->S[0] == 0) return -1; /* -> s, line 455 */ + { int ret = slice_from_s(z, 4, s_78); /* <-, line 455 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 455 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab1; + lab2: + z->c = z->l - m2; + z->ket = z->c; /* [, line 456 */ + } +lab1: + if (!(find_among_b(z, a_44, 95))) return 0; /* substring, line 456 */ + z->bra = z->c; /* ], line 456 */ + if (z->c > z->lb) return 0; /* atlimit, line 456 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 473 */ + if (z->S[0] == 0) return -1; /* -> s, line 473 */ + { int ret = slice_from_s(z, 4, s_79); /* <-, line 473 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 473 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5c(struct SN_env * z) { /* backwardmode */ + { int m1 = z->l - z->c; (void)m1; /* do, line 478 */ + z->ket = z->c; /* [, line 479 */ + if (z->c - 9 <= z->lb || z->p[z->c - 1] != 181) goto lab0; /* substring, line 479 */ + if (!(find_among_b(z, a_45, 1))) goto lab0; + z->bra = z->c; /* ], line 479 */ + { int ret = slice_del(z); /* delete, line 480 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 480 */ + lab0: + z->c = z->l - m1; + } + z->ket = z->c; /* [, line 483 */ + if (!(eq_s_b(z, 6, s_80))) return 0; /* literal, line 483 */ + z->bra = z->c; /* ], line 483 */ + { int ret = slice_del(z); /* delete, line 484 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 485 */ + { int m2 = z->l - z->c; (void)m2; /* or, line 486 */ + z->ket = z->c; /* [, line 486 */ + if (in_grouping_b_U(z, g_v2, 945, 969, 0)) goto lab2; /* grouping v2, line 486 */ + z->bra = z->c; /* ], line 486 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 486 */ + if (z->S[0] == 0) return -1; /* -> s, line 486 */ + { int ret = slice_from_s(z, 4, s_81); /* <-, line 486 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 486 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab1; + lab2: + z->c = z->l - m2; + z->ket = z->c; /* [, line 487 */ + if (!(find_among_b(z, a_46, 31))) goto lab3; /* substring, line 487 */ + z->bra = z->c; /* ], line 487 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 491 */ + if (z->S[0] == 0) return -1; /* -> s, line 491 */ + { int ret = slice_from_s(z, 4, s_82); /* <-, line 491 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 491 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab1; + lab3: + z->c = z->l - m2; + z->ket = z->c; /* [, line 493 */ + } +lab1: + if (!(find_among_b(z, a_47, 25))) return 0; /* substring, line 493 */ + z->bra = z->c; /* ], line 493 */ + if (z->c > z->lb) return 0; /* atlimit, line 493 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 497 */ + if (z->S[0] == 0) return -1; /* -> s, line 497 */ + { int ret = slice_from_s(z, 4, s_83); /* <-, line 497 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 497 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5d(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 502 */ + if (z->c - 9 <= z->lb || z->p[z->c - 1] != 131) return 0; /* substring, line 502 */ + if (!(find_among_b(z, a_48, 2))) return 0; + z->bra = z->c; /* ], line 502 */ + { int ret = slice_del(z); /* delete, line 504 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 505 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 506 */ + z->ket = z->c; /* [, line 506 */ + if (!(eq_s_b(z, 6, s_84))) goto lab1; /* literal, line 506 */ + z->bra = z->c; /* ], line 506 */ + if (z->c > z->lb) goto lab1; /* atlimit, line 506 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 506 */ + if (z->S[0] == 0) return -1; /* -> s, line 506 */ + { int ret = slice_from_s(z, 6, s_85); /* <-, line 506 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 506 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 507 */ + if (!(eq_s_b(z, 6, s_86))) return 0; /* literal, line 507 */ + z->bra = z->c; /* ], line 507 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 507 */ + if (z->S[0] == 0) return -1; /* -> s, line 507 */ + { int ret = slice_from_s(z, 6, s_87); /* <-, line 507 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 507 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } +lab0: + return 1; +} + +static int r_step5e(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 513 */ + if (z->c - 11 <= z->lb || z->p[z->c - 1] != 181) return 0; /* substring, line 513 */ + if (!(find_among_b(z, a_49, 2))) return 0; + z->bra = z->c; /* ], line 513 */ + { int ret = slice_del(z); /* delete, line 515 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 516 */ + z->ket = z->c; /* [, line 517 */ + if (!(eq_s_b(z, 4, s_88))) return 0; /* literal, line 517 */ + z->bra = z->c; /* ], line 517 */ + if (z->c > z->lb) return 0; /* atlimit, line 517 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 517 */ + if (z->S[0] == 0) return -1; /* -> s, line 517 */ + { int ret = slice_from_s(z, 10, s_89); /* <-, line 517 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 517 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5f(struct SN_env * z) { /* backwardmode */ + { int m1 = z->l - z->c; (void)m1; /* do, line 523 */ + z->ket = z->c; /* [, line 524 */ + if (!(eq_s_b(z, 10, s_90))) goto lab0; /* literal, line 524 */ + z->bra = z->c; /* ], line 524 */ + { int ret = slice_del(z); /* delete, line 525 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 526 */ + z->ket = z->c; /* [, line 527 */ + if (z->c - 1 <= z->lb || (z->p[z->c - 1] != 128 && z->p[z->c - 1] != 134)) goto lab0; /* substring, line 527 */ + if (!(find_among_b(z, a_50, 6))) goto lab0; + z->bra = z->c; /* ], line 527 */ + if (z->c > z->lb) goto lab0; /* atlimit, line 527 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 528 */ + if (z->S[0] == 0) return -1; /* -> s, line 528 */ + { int ret = slice_from_s(z, 8, s_91); /* <-, line 528 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 528 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + lab0: + z->c = z->l - m1; + } + z->ket = z->c; /* [, line 531 */ + if (!(eq_s_b(z, 8, s_92))) return 0; /* literal, line 531 */ + z->bra = z->c; /* ], line 531 */ + { int ret = slice_del(z); /* delete, line 532 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 533 */ + z->ket = z->c; /* [, line 534 */ + if (!(find_among_b(z, a_51, 9))) return 0; /* substring, line 534 */ + z->bra = z->c; /* ], line 534 */ + if (z->c > z->lb) return 0; /* atlimit, line 534 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 536 */ + if (z->S[0] == 0) return -1; /* -> s, line 536 */ + { int ret = slice_from_s(z, 8, s_93); /* <-, line 536 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 536 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5g(struct SN_env * z) { /* backwardmode */ + { int m1 = z->l - z->c; (void)m1; /* do, line 541 */ + z->ket = z->c; /* [, line 542 */ + if (!(find_among_b(z, a_52, 3))) goto lab0; /* substring, line 542 */ + z->bra = z->c; /* ], line 542 */ + { int ret = slice_del(z); /* delete, line 543 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 543 */ + lab0: + z->c = z->l - m1; + } + z->ket = z->c; /* [, line 546 */ + if (!(find_among_b(z, a_55, 3))) return 0; /* substring, line 546 */ + z->bra = z->c; /* ], line 546 */ + { int ret = slice_del(z); /* delete, line 548 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 549 */ + { int m2 = z->l - z->c; (void)m2; /* or, line 552 */ + z->ket = z->c; /* [, line 550 */ + if (!(find_among_b(z, a_53, 6))) goto lab2; /* substring, line 550 */ + z->bra = z->c; /* ], line 550 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 551 */ + if (z->S[0] == 0) return -1; /* -> s, line 551 */ + { int ret = slice_from_s(z, 4, s_94); /* <-, line 551 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 551 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab1; + lab2: + z->c = z->l - m2; + z->ket = z->c; /* [, line 553 */ + if (z->c - 1 <= z->lb || z->p[z->c - 1] != 184) return 0; /* substring, line 553 */ + if (!(find_among_b(z, a_54, 5))) return 0; + z->bra = z->c; /* ], line 553 */ + if (z->c > z->lb) return 0; /* atlimit, line 553 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 554 */ + if (z->S[0] == 0) return -1; /* -> s, line 554 */ + { int ret = slice_from_s(z, 4, s_95); /* <-, line 554 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 554 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } +lab1: + return 1; +} + +static int r_step5h(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 561 */ + if (!(find_among_b(z, a_58, 3))) return 0; /* substring, line 561 */ + z->bra = z->c; /* ], line 561 */ + { int ret = slice_del(z); /* delete, line 563 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 564 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 569 */ + z->ket = z->c; /* [, line 565 */ + if (!(find_among_b(z, a_56, 12))) goto lab1; /* substring, line 565 */ + z->bra = z->c; /* ], line 565 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 567 */ + if (z->S[0] == 0) return -1; /* -> s, line 567 */ + { int ret = slice_from_s(z, 6, s_96); /* <-, line 567 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 567 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + z->ket = z->c; /* [, line 570 */ + if (!(find_among_b(z, a_57, 25))) return 0; /* substring, line 570 */ + z->bra = z->c; /* ], line 570 */ + if (z->c > z->lb) return 0; /* atlimit, line 570 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 574 */ + if (z->S[0] == 0) return -1; /* -> s, line 574 */ + { int ret = slice_from_s(z, 6, s_97); /* <-, line 574 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 574 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } +lab0: + return 1; +} + +static int r_step5i(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 581 */ + if (!(find_among_b(z, a_62, 3))) return 0; /* substring, line 581 */ + z->bra = z->c; /* ], line 581 */ + { int ret = slice_del(z); /* delete, line 583 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 584 */ + { int m1 = z->l - z->c; (void)m1; /* or, line 585 */ + z->ket = z->c; /* [, line 585 */ + if (!(eq_s_b(z, 8, s_98))) goto lab1; /* literal, line 585 */ + z->bra = z->c; /* ], line 585 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 585 */ + if (z->S[0] == 0) return -1; /* -> s, line 585 */ + { int ret = slice_from_s(z, 4, s_99); /* <-, line 585 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 585 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab0; + lab1: + z->c = z->l - m1; + { int m2 = z->l - z->c; (void)m2; /* not, line 586 */ + z->ket = z->c; /* [, line 586 */ + if (z->c - 5 <= z->lb || (z->p[z->c - 1] != 134 && z->p[z->c - 1] != 135)) goto lab2; /* substring, line 586 */ + if (!(find_among_b(z, a_59, 2))) goto lab2; + z->bra = z->c; /* ], line 586 */ + return 0; + lab2: + z->c = z->l - m2; + } + { int m3 = z->l - z->c; (void)m3; /* or, line 590 */ + z->ket = z->c; /* [, line 587 */ + if (!(find_among_b(z, a_60, 10))) goto lab4; /* substring, line 587 */ + z->bra = z->c; /* ], line 587 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 589 */ + if (z->S[0] == 0) return -1; /* -> s, line 589 */ + { int ret = slice_from_s(z, 4, s_100); /* <-, line 589 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 589 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + goto lab3; + lab4: + z->c = z->l - m3; + z->ket = z->c; /* [, line 591 */ + if (!(find_among_b(z, a_61, 44))) return 0; /* substring, line 591 */ + z->bra = z->c; /* ], line 591 */ + if (z->c > z->lb) return 0; /* atlimit, line 591 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 597 */ + if (z->S[0] == 0) return -1; /* -> s, line 597 */ + { int ret = slice_from_s(z, 4, s_101); /* <-, line 597 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 597 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + } + lab3: + ; + } +lab0: + return 1; +} + +static int r_step5j(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 605 */ + if (!(find_among_b(z, a_63, 3))) return 0; /* substring, line 605 */ + z->bra = z->c; /* ], line 605 */ + { int ret = slice_del(z); /* delete, line 606 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 606 */ + z->ket = z->c; /* [, line 608 */ + if (z->c - 1 <= z->lb || z->p[z->c - 1] != 189) return 0; /* substring, line 608 */ + if (!(find_among_b(z, a_64, 6))) return 0; + z->bra = z->c; /* ], line 608 */ + if (z->c > z->lb) return 0; /* atlimit, line 608 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 609 */ + if (z->S[0] == 0) return -1; /* -> s, line 609 */ + { int ret = slice_from_s(z, 4, s_102); /* <-, line 609 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 609 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5k(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 614 */ + if (z->c - 7 <= z->lb || z->p[z->c - 1] != 181) return 0; /* substring, line 614 */ + if (!(find_among_b(z, a_65, 1))) return 0; + z->bra = z->c; /* ], line 614 */ + { int ret = slice_del(z); /* delete, line 615 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 615 */ + z->ket = z->c; /* [, line 617 */ + if (!(find_among_b(z, a_66, 10))) return 0; /* substring, line 617 */ + z->bra = z->c; /* ], line 617 */ + if (z->c > z->lb) return 0; /* atlimit, line 617 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 619 */ + if (z->S[0] == 0) return -1; /* -> s, line 619 */ + { int ret = slice_from_s(z, 6, s_103); /* <-, line 619 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 619 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5l(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 624 */ + if (z->c - 7 <= z->lb || z->p[z->c - 1] != 181) return 0; /* substring, line 624 */ + if (!(find_among_b(z, a_67, 3))) return 0; + z->bra = z->c; /* ], line 624 */ + { int ret = slice_del(z); /* delete, line 625 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 625 */ + z->ket = z->c; /* [, line 627 */ + if (!(find_among_b(z, a_68, 6))) return 0; /* substring, line 627 */ + z->bra = z->c; /* ], line 627 */ + if (z->c > z->lb) return 0; /* atlimit, line 627 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 628 */ + if (z->S[0] == 0) return -1; /* -> s, line 628 */ + { int ret = slice_from_s(z, 6, s_104); /* <-, line 628 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 628 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step5m(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 633 */ + if (z->c - 7 <= z->lb || z->p[z->c - 1] != 181) return 0; /* substring, line 633 */ + if (!(find_among_b(z, a_69, 3))) return 0; + z->bra = z->c; /* ], line 633 */ + { int ret = slice_del(z); /* delete, line 634 */ + if (ret < 0) return ret; + } + z->B[0] = 0; /* unset test1, line 634 */ + z->ket = z->c; /* [, line 636 */ + if (!(find_among_b(z, a_70, 7))) return 0; /* substring, line 636 */ + z->bra = z->c; /* ], line 636 */ + if (z->c > z->lb) return 0; /* atlimit, line 636 */ + z->S[0] = slice_to(z, z->S[0]); /* -> s, line 638 */ + if (z->S[0] == 0) return -1; /* -> s, line 638 */ + { int ret = slice_from_s(z, 6, s_105); /* <-, line 638 */ + if (ret < 0) return ret; + } + { int ret; + { int saved_c = z->c; + ret = insert_v(z, z->c, z->c, z->S[0]); /* <+ s, line 638 */ + z->c = saved_c; + } + if (ret < 0) return ret; + } + return 1; +} + +static int r_step6(struct SN_env * z) { /* backwardmode */ + { int m1 = z->l - z->c; (void)m1; /* do, line 643 */ + z->ket = z->c; /* [, line 644 */ + if (!(find_among_b(z, a_71, 3))) goto lab0; /* substring, line 644 */ + z->bra = z->c; /* ], line 644 */ + { int ret = slice_from_s(z, 4, s_106); /* <-, line 645 */ + if (ret < 0) return ret; + } + lab0: + z->c = z->l - m1; + } + if (!(z->B[0])) return 0; /* Boolean test test1, line 648 */ + z->ket = z->c; /* [, line 649 */ + if (!(find_among_b(z, a_72, 84))) return 0; /* substring, line 649 */ + z->bra = z->c; /* ], line 649 */ + { int ret = slice_del(z); /* delete, line 659 */ + if (ret < 0) return ret; + } + return 1; +} + +static int r_step7(struct SN_env * z) { /* backwardmode */ + z->ket = z->c; /* [, line 664 */ + if (z->c - 7 <= z->lb || (z->p[z->c - 1] != 129 && z->p[z->c - 1] != 132)) return 0; /* substring, line 664 */ + if (!(find_among_b(z, a_73, 8))) return 0; + z->bra = z->c; /* ], line 664 */ + { int ret = slice_del(z); /* delete, line 665 */ + if (ret < 0) return ret; + } + return 1; +} + +extern int greek_UTF_8_stem(struct SN_env * z) { /* forwardmode */ + z->lb = z->c; z->c = z->l; /* backwards, line 671 */ + + { int m1 = z->l - z->c; (void)m1; /* do, line 672 */ + { int ret = r_tolower(z); /* call tolower, line 672 */ + if (ret == 0) goto lab0; + if (ret < 0) return ret; + } + lab0: + z->c = z->l - m1; + } + { int ret = r_has_min_length(z); /* call has_min_length, line 673 */ + if (ret <= 0) return ret; + } + z->B[0] = 1; /* set test1, line 674 */ + { int m2 = z->l - z->c; (void)m2; /* do, line 675 */ + { int ret = r_step1(z); /* call step1, line 675 */ + if (ret == 0) goto lab1; + if (ret < 0) return ret; + } + lab1: + z->c = z->l - m2; + } + { int m3 = z->l - z->c; (void)m3; /* do, line 676 */ + { int ret = r_steps1(z); /* call steps1, line 676 */ + if (ret == 0) goto lab2; + if (ret < 0) return ret; + } + lab2: + z->c = z->l - m3; + } + { int m4 = z->l - z->c; (void)m4; /* do, line 677 */ + { int ret = r_steps2(z); /* call steps2, line 677 */ + if (ret == 0) goto lab3; + if (ret < 0) return ret; + } + lab3: + z->c = z->l - m4; + } + { int m5 = z->l - z->c; (void)m5; /* do, line 678 */ + { int ret = r_steps3(z); /* call steps3, line 678 */ + if (ret == 0) goto lab4; + if (ret < 0) return ret; + } + lab4: + z->c = z->l - m5; + } + { int m6 = z->l - z->c; (void)m6; /* do, line 679 */ + { int ret = r_steps4(z); /* call steps4, line 679 */ + if (ret == 0) goto lab5; + if (ret < 0) return ret; + } + lab5: + z->c = z->l - m6; + } + { int m7 = z->l - z->c; (void)m7; /* do, line 680 */ + { int ret = r_steps5(z); /* call steps5, line 680 */ + if (ret == 0) goto lab6; + if (ret < 0) return ret; + } + lab6: + z->c = z->l - m7; + } + { int m8 = z->l - z->c; (void)m8; /* do, line 681 */ + { int ret = r_steps6(z); /* call steps6, line 681 */ + if (ret == 0) goto lab7; + if (ret < 0) return ret; + } + lab7: + z->c = z->l - m8; + } + { int m9 = z->l - z->c; (void)m9; /* do, line 682 */ + { int ret = r_steps7(z); /* call steps7, line 682 */ + if (ret == 0) goto lab8; + if (ret < 0) return ret; + } + lab8: + z->c = z->l - m9; + } + { int m10 = z->l - z->c; (void)m10; /* do, line 683 */ + { int ret = r_steps8(z); /* call steps8, line 683 */ + if (ret == 0) goto lab9; + if (ret < 0) return ret; + } + lab9: + z->c = z->l - m10; + } + { int m11 = z->l - z->c; (void)m11; /* do, line 684 */ + { int ret = r_steps9(z); /* call steps9, line 684 */ + if (ret == 0) goto lab10; + if (ret < 0) return ret; + } + lab10: + z->c = z->l - m11; + } + { int m12 = z->l - z->c; (void)m12; /* do, line 685 */ + { int ret = r_steps10(z); /* call steps10, line 685 */ + if (ret == 0) goto lab11; + if (ret < 0) return ret; + } + lab11: + z->c = z->l - m12; + } + { int m13 = z->l - z->c; (void)m13; /* do, line 686 */ + { int ret = r_step2a(z); /* call step2a, line 686 */ + if (ret == 0) goto lab12; + if (ret < 0) return ret; + } + lab12: + z->c = z->l - m13; + } + { int m14 = z->l - z->c; (void)m14; /* do, line 687 */ + { int ret = r_step2b(z); /* call step2b, line 687 */ + if (ret == 0) goto lab13; + if (ret < 0) return ret; + } + lab13: + z->c = z->l - m14; + } + { int m15 = z->l - z->c; (void)m15; /* do, line 688 */ + { int ret = r_step2c(z); /* call step2c, line 688 */ + if (ret == 0) goto lab14; + if (ret < 0) return ret; + } + lab14: + z->c = z->l - m15; + } + { int m16 = z->l - z->c; (void)m16; /* do, line 689 */ + { int ret = r_step2d(z); /* call step2d, line 689 */ + if (ret == 0) goto lab15; + if (ret < 0) return ret; + } + lab15: + z->c = z->l - m16; + } + { int m17 = z->l - z->c; (void)m17; /* do, line 690 */ + { int ret = r_step3(z); /* call step3, line 690 */ + if (ret == 0) goto lab16; + if (ret < 0) return ret; + } + lab16: + z->c = z->l - m17; + } + { int m18 = z->l - z->c; (void)m18; /* do, line 691 */ + { int ret = r_step4(z); /* call step4, line 691 */ + if (ret == 0) goto lab17; + if (ret < 0) return ret; + } + lab17: + z->c = z->l - m18; + } + { int m19 = z->l - z->c; (void)m19; /* do, line 692 */ + { int ret = r_step5a(z); /* call step5a, line 692 */ + if (ret == 0) goto lab18; + if (ret < 0) return ret; + } + lab18: + z->c = z->l - m19; + } + { int m20 = z->l - z->c; (void)m20; /* do, line 693 */ + { int ret = r_step5b(z); /* call step5b, line 693 */ + if (ret == 0) goto lab19; + if (ret < 0) return ret; + } + lab19: + z->c = z->l - m20; + } + { int m21 = z->l - z->c; (void)m21; /* do, line 694 */ + { int ret = r_step5c(z); /* call step5c, line 694 */ + if (ret == 0) goto lab20; + if (ret < 0) return ret; + } + lab20: + z->c = z->l - m21; + } + { int m22 = z->l - z->c; (void)m22; /* do, line 695 */ + { int ret = r_step5d(z); /* call step5d, line 695 */ + if (ret == 0) goto lab21; + if (ret < 0) return ret; + } + lab21: + z->c = z->l - m22; + } + { int m23 = z->l - z->c; (void)m23; /* do, line 696 */ + { int ret = r_step5e(z); /* call step5e, line 696 */ + if (ret == 0) goto lab22; + if (ret < 0) return ret; + } + lab22: + z->c = z->l - m23; + } + { int m24 = z->l - z->c; (void)m24; /* do, line 697 */ + { int ret = r_step5f(z); /* call step5f, line 697 */ + if (ret == 0) goto lab23; + if (ret < 0) return ret; + } + lab23: + z->c = z->l - m24; + } + { int m25 = z->l - z->c; (void)m25; /* do, line 698 */ + { int ret = r_step5g(z); /* call step5g, line 698 */ + if (ret == 0) goto lab24; + if (ret < 0) return ret; + } + lab24: + z->c = z->l - m25; + } + { int m26 = z->l - z->c; (void)m26; /* do, line 699 */ + { int ret = r_step5h(z); /* call step5h, line 699 */ + if (ret == 0) goto lab25; + if (ret < 0) return ret; + } + lab25: + z->c = z->l - m26; + } + { int m27 = z->l - z->c; (void)m27; /* do, line 700 */ + { int ret = r_step5j(z); /* call step5j, line 700 */ + if (ret == 0) goto lab26; + if (ret < 0) return ret; + } + lab26: + z->c = z->l - m27; + } + { int m28 = z->l - z->c; (void)m28; /* do, line 701 */ + { int ret = r_step5i(z); /* call step5i, line 701 */ + if (ret == 0) goto lab27; + if (ret < 0) return ret; + } + lab27: + z->c = z->l - m28; + } + { int m29 = z->l - z->c; (void)m29; /* do, line 702 */ + { int ret = r_step5k(z); /* call step5k, line 702 */ + if (ret == 0) goto lab28; + if (ret < 0) return ret; + } + lab28: + z->c = z->l - m29; + } + { int m30 = z->l - z->c; (void)m30; /* do, line 703 */ + { int ret = r_step5l(z); /* call step5l, line 703 */ + if (ret == 0) goto lab29; + if (ret < 0) return ret; + } + lab29: + z->c = z->l - m30; + } + { int m31 = z->l - z->c; (void)m31; /* do, line 704 */ + { int ret = r_step5m(z); /* call step5m, line 704 */ + if (ret == 0) goto lab30; + if (ret < 0) return ret; + } + lab30: + z->c = z->l - m31; + } + { int m32 = z->l - z->c; (void)m32; /* do, line 705 */ + { int ret = r_step6(z); /* call step6, line 705 */ + if (ret == 0) goto lab31; + if (ret < 0) return ret; + } + lab31: + z->c = z->l - m32; + } + { int m33 = z->l - z->c; (void)m33; /* do, line 706 */ + { int ret = r_step7(z); /* call step7, line 706 */ + if (ret == 0) goto lab32; + if (ret < 0) return ret; + } + lab32: + z->c = z->l - m33; + } + z->c = z->lb; + return 1; +} + +extern struct SN_env * greek_UTF_8_create_env(void) { return SN_create_env(1, 0, 1); } + +extern void greek_UTF_8_close_env(struct SN_env * z) { SN_close_env(z, 1); } + diff --git a/src/backend/snowball/libstemmer/utilities.c b/src/backend/snowball/libstemmer/utilities.c index 920b8dbe6dd4..d64d92231795 100644 --- a/src/backend/snowball/libstemmer/utilities.c +++ b/src/backend/snowball/libstemmer/utilities.c @@ -59,31 +59,49 @@ extern int skip_utf8(const symbol * p, int c, int lb, int l, int n) { /* Code for character groupings: utf8 cases */ static int get_utf8(const symbol * p, int c, int l, int * slot) { - int b0, b1; + int b0, b1, b2; if (c >= l) return 0; b0 = p[c++]; if (b0 < 0xC0 || c == l) { /* 1100 0000 */ - * slot = b0; return 1; + *slot = b0; + return 1; } - b1 = p[c++]; + b1 = p[c++] & 0x3F; if (b0 < 0xE0 || c == l) { /* 1110 0000 */ - * slot = (b0 & 0x1F) << 6 | (b1 & 0x3F); return 2; + *slot = (b0 & 0x1F) << 6 | b1; + return 2; } - * slot = (b0 & 0xF) << 12 | (b1 & 0x3F) << 6 | (p[c] & 0x3F); return 3; + b2 = p[c++] & 0x3F; + if (b0 < 0xF0 || c == l) { /* 1111 0000 */ + *slot = (b0 & 0xF) << 12 | b1 << 6 | b2; + return 3; + } + *slot = (b0 & 0xE) << 18 | b1 << 12 | b2 << 6 | (p[c] & 0x3F); + return 4; } static int get_b_utf8(const symbol * p, int c, int lb, int * slot) { - int b0, b1; + int a, b; if (c <= lb) return 0; - b0 = p[--c]; - if (b0 < 0x80 || c == lb) { /* 1000 0000 */ - * slot = b0; return 1; + b = p[--c]; + if (b < 0x80 || c == lb) { /* 1000 0000 */ + *slot = b; + return 1; + } + a = b & 0x3F; + b = p[--c]; + if (b >= 0xC0 || c == lb) { /* 1100 0000 */ + *slot = (b & 0x1F) << 6 | a; + return 2; } - b1 = p[--c]; - if (b1 >= 0xC0 || c == lb) { /* 1100 0000 */ - * slot = (b1 & 0x1F) << 6 | (b0 & 0x3F); return 2; + a |= (b & 0x3F) << 6; + b = p[--c]; + if (b >= 0xE0 || c == lb) { /* 1110 0000 */ + *slot = (b & 0xF) << 12 | a; + return 3; } - * slot = (p[--c] & 0xF) << 12 | (b1 & 0x3F) << 6 | (b0 & 0x3F); return 3; + *slot = (p[--c] & 0xE) << 18 | (b & 0x3F) << 12 | a; + return 4; } extern int in_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) { @@ -230,8 +248,13 @@ extern int find_among(struct SN_env * z, const struct among * v, int v_size) { common++; } } - if (diff < 0) { j = k; common_j = common; } - else { i = k; common_i = 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 */ @@ -360,9 +383,8 @@ extern int replace_s(struct SN_env * z, int c_bra, int c_ket, int s_size, const z->l += adjustment; if (z->c >= c_ket) z->c += adjustment; - else - if (z->c > c_bra) - z->c = c_bra; + else if (z->c > c_bra) + z->c = c_bra; } if (s_size) memmove(z->p + c_bra, s, s_size * sizeof(symbol)); if (adjptr != NULL) diff --git a/src/backend/statistics/dependencies.c b/src/backend/statistics/dependencies.c index 612b65ac7d2d..2f3f84c091ce 100644 --- a/src/backend/statistics/dependencies.c +++ b/src/backend/statistics/dependencies.c @@ -274,7 +274,7 @@ dependency_degree(int numrows, HeapTuple *rows, int k, AttrNumber *dependency, colstat->attrtypid); /* prepare the sort function for this dimension */ - multi_sort_add_dimension(mss, i, type->lt_opr, type->typcollation); + multi_sort_add_dimension(mss, i, type->lt_opr, colstat->attrcollid); } /* diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index cfaf03849a9b..797c84ccdd60 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -797,21 +797,13 @@ statext_is_compatible_clause_internal(PlannerInfo *root, Node *clause, RangeTblEntry *rte = root->simple_rte_array[relid]; OpExpr *expr = (OpExpr *) clause; Var *var; - bool varonleft = true; - bool ok; /* Only expressions with two arguments are considered compatible. */ if (list_length(expr->args) != 2) return false; - /* see if it actually has the right shape (one Var, one Const) */ - ok = (NumRelids((Node *) expr) == 1) && - (is_pseudo_constant_clause(lsecond(expr->args)) || - (varonleft = false, - is_pseudo_constant_clause(linitial(expr->args)))); - - /* unsupported structure (two variables or so) */ - if (!ok) + /* Check if the expression the right shape (one Var, one Const) */ + if (!examine_opclause_expression(expr, &var, NULL, NULL)) return false; /* @@ -851,8 +843,6 @@ statext_is_compatible_clause_internal(PlannerInfo *root, Node *clause, !get_func_leakproof(get_opcode(expr->opno))) return false; - var = (varonleft) ? linitial(expr->args) : lsecond(expr->args); - return statext_is_compatible_clause_internal(root, (Node *) var, relid, attnums); } @@ -1209,3 +1199,65 @@ statext_clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, return sel; } + +/* + * examine_operator_expression + * Split expression into Var and Const parts. + * + * Attempts to match the arguments to either (Var op Const) or (Const op Var), + * possibly with a RelabelType on top. When the expression matches this form, + * returns true, otherwise returns false. + * + * Optionally returns pointers to the extracted Var/Const nodes, when passed + * non-null pointers (varp, cstp and varonleftp). The varonleftp flag specifies + * on which side of the operator we found the Var node. + */ +bool +examine_opclause_expression(OpExpr *expr, Var **varp, Const **cstp, bool *varonleftp) +{ + Var *var; + Const *cst; + bool varonleft; + Node *leftop, + *rightop; + + /* enforced by statext_is_compatible_clause_internal */ + Assert(list_length(expr->args) == 2); + + leftop = linitial(expr->args); + rightop = lsecond(expr->args); + + /* strip RelabelType from either side of the expression */ + if (IsA(leftop, RelabelType)) + leftop = (Node *) ((RelabelType *) leftop)->arg; + + if (IsA(rightop, RelabelType)) + rightop = (Node *) ((RelabelType *) rightop)->arg; + + if (IsA(leftop, Var) && IsA(rightop, Const)) + { + var = (Var *) leftop; + cst = (Const *) rightop; + varonleft = true; + } + else if (IsA(leftop, Const) && IsA(rightop, Var)) + { + var = (Var *) rightop; + cst = (Const *) leftop; + varonleft = false; + } + else + return false; + + /* return pointers to the extracted parts if requested */ + if (varp) + *varp = var; + + if (cstp) + *cstp = cst; + + if (varonleftp) + *varonleftp = varonleft; + + return true; +} diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c index 5fe61ea0a438..cec06f8c444e 100644 --- a/src/backend/statistics/mcv.c +++ b/src/backend/statistics/mcv.c @@ -40,27 +40,20 @@ * stored in a separate array (deduplicated, to minimize the size), and * so the serialized items only store uint16 indexes into that array. * - * Each serialized item store (in this order): + * Each serialized item stores (in this order): * * - indexes to values (ndim * sizeof(uint16)) * - null flags (ndim * sizeof(bool)) * - frequency (sizeof(double)) * - base_frequency (sizeof(double)) * + * There is no alignment padding within an MCV item. * So in total each MCV item requires this many bytes: * * ndim * (sizeof(uint16) + sizeof(bool)) + 2 * sizeof(double) */ #define ITEM_SIZE(ndims) \ - MAXALIGN((ndims) * (sizeof(uint16) + sizeof(bool)) + 2 * sizeof(double)) - -/* - * Macros for convenient access to parts of a serialized MCV item. - */ -#define ITEM_INDEXES(item) ((uint16 *) (item)) -#define ITEM_NULLS(item,ndims) ((bool *) (ITEM_INDEXES(item) + (ndims))) -#define ITEM_FREQUENCY(item,ndims) ((double *) (ITEM_NULLS(item, ndims) + (ndims))) -#define ITEM_BASE_FREQUENCY(item,ndims) ((double *) (ITEM_FREQUENCY(item, ndims) + 1)) + ((ndims) * (sizeof(uint16) + sizeof(bool)) + 2 * sizeof(double)) /* * Used to compute size of serialized MCV list representation. @@ -68,19 +61,46 @@ #define MinSizeOfMCVList \ (VARHDRSZ + sizeof(uint32) * 3 + sizeof(AttrNumber)) +/* + * Size of the serialized MCV list, excluding the space needed for + * deduplicated per-dimension values. The macro is meant to be used + * when it's not yet safe to access the serialized info about amount + * of data for each column. + */ #define SizeOfMCVList(ndims,nitems) \ - (MAXALIGN(MinSizeOfMCVList + sizeof(Oid) * (ndims)) + \ - MAXALIGN((ndims) * sizeof(DimensionInfo)) + \ - MAXALIGN((nitems) * ITEM_SIZE(ndims))) + ((MinSizeOfMCVList + sizeof(Oid) * (ndims)) + \ + ((ndims) * sizeof(DimensionInfo)) + \ + ((nitems) * ITEM_SIZE(ndims))) static MultiSortSupport build_mss(VacAttrStats **stats, int numattrs); static SortItem *build_distinct_groups(int numrows, SortItem *items, MultiSortSupport mss, int *ndistinct); +static SortItem **build_column_frequencies(SortItem *groups, int ngroups, + MultiSortSupport mss, int *ncounts); + static int count_distinct_groups(int numrows, SortItem *items, MultiSortSupport mss); +/* + * Compute new value for bitmap item, considering whether it's used for + * clauses connected by AND/OR. + */ +#define RESULT_MERGE(value, is_or, match) \ + ((is_or) ? ((value) || (match)) : ((value) && (match))) + +/* + * When processing a list of clauses, the bitmap item may get set to a value + * such that additional clauses can't change it. For example, when processing + * a list of clauses connected to AND, as soon as the item gets set to 'false' + * then it'll remain like that. Similarly clauses connected by OR and 'true'. + * + * Returns true when the value in the bitmap can't change no matter how the + * remaining clauses are evaluated. + */ +#define RESULT_IS_FINAL(value, is_or) ((is_or) ? (value) : (!(value))) + /* * get_mincount_for_mcv_list * Determine the minimum number of times a value needs to appear in @@ -242,6 +262,20 @@ statext_mcv_build(int numrows, HeapTuple *rows, Bitmapset *attrs, if (nitems > 0) { int j; + SortItem key; + MultiSortSupport tmp; + + /* frequencies for values in each attribute */ + SortItem **freqs; + int *nfreqs; + + /* used to search values */ + tmp = (MultiSortSupport) palloc(offsetof(MultiSortSupportData, ssup) + + sizeof(SortSupportData)); + + /* compute frequencies for values in each column */ + nfreqs = (int *) palloc0(sizeof(int) * numattrs); + freqs = build_column_frequencies(groups, ngroups, mss, nfreqs); /* * Allocate the MCV list structure, set the global parameters. @@ -281,18 +315,26 @@ statext_mcv_build(int numrows, HeapTuple *rows, Bitmapset *attrs, item->base_frequency = 1.0; for (j = 0; j < numattrs; j++) { - int count = 0; - int k; + SortItem *freq; - for (k = 0; k < ngroups; k++) - { - if (multi_sort_compare_dim(j, &groups[i], &groups[k], mss) == 0) - count += groups[k].count; - } + /* single dimension */ + tmp->ndims = 1; + tmp->ssup[0] = mss->ssup[j]; + + /* fill search key */ + key.values = &groups[i].values[j]; + key.isnull = &groups[i].isnull[j]; + + freq = (SortItem *) bsearch_arg(&key, freqs[j], nfreqs[j], + sizeof(SortItem), + multi_sort_compare, tmp); - item->base_frequency *= (double) count / numrows; + item->base_frequency *= ((double) freq->count) / numrows; } } + + pfree(nfreqs); + pfree(freqs); } pfree(items); @@ -324,7 +366,7 @@ build_mss(VacAttrStats **stats, int numattrs) elog(ERROR, "cache lookup failed for ordering operator for type %u", colstat->attrtypid); - multi_sort_add_dimension(mss, i, type->lt_opr, type->typcollation); + multi_sort_add_dimension(mss, i, type->lt_opr, colstat->attrcollid); } return mss; @@ -419,6 +461,95 @@ build_distinct_groups(int numrows, SortItem *items, MultiSortSupport mss, return groups; } +/* compare sort items (single dimension) */ +static int +sort_item_compare(const void *a, const void *b, void *arg) +{ + SortSupport ssup = (SortSupport) arg; + SortItem *ia = (SortItem *) a; + SortItem *ib = (SortItem *) b; + + return ApplySortComparator(ia->values[0], ia->isnull[0], + ib->values[0], ib->isnull[0], + ssup); +} + +/* + * build_column_frequencies + * compute frequencies of values in each column + * + * This returns an array of SortItems for each attibute the MCV is built + * on, with a frequency (number of occurrences) for each value. This is + * then used to compute "base" frequency of MCV items. + * + * All the memory is allocated in a single chunk, so that a single pfree + * is enough to release it. We do not allocate space for values/isnull + * arrays in the SortItems, because we can simply point into the input + * groups directly. + */ +static SortItem ** +build_column_frequencies(SortItem *groups, int ngroups, + MultiSortSupport mss, int *ncounts) +{ + int i, + dim; + SortItem **result; + char *ptr; + + Assert(groups); + Assert(ncounts); + + /* allocate arrays for all columns as a single chunk */ + ptr = palloc(MAXALIGN(sizeof(SortItem *) * mss->ndims) + + mss->ndims * MAXALIGN(sizeof(SortItem) * ngroups)); + + /* initial array of pointers */ + result = (SortItem **) ptr; + ptr += MAXALIGN(sizeof(SortItem *) * mss->ndims); + + for (dim = 0; dim < mss->ndims; dim++) + { + SortSupport ssup = &mss->ssup[dim]; + + /* array of values for a single column */ + result[dim] = (SortItem *) ptr; + ptr += MAXALIGN(sizeof(SortItem) * ngroups); + + /* extract data for the dimension */ + for (i = 0; i < ngroups; i++) + { + /* point into the input groups */ + result[dim][i].values = &groups[i].values[dim]; + result[dim][i].isnull = &groups[i].isnull[dim]; + result[dim][i].count = groups[i].count; + } + + /* sort the values, deduplicate */ + qsort_arg((void *) result[dim], ngroups, sizeof(SortItem), + sort_item_compare, ssup); + + /* + * Identify distinct values, compute frequency (there might be + * multiple MCV items containing this value, so we need to sum + * counts from all of them. + */ + ncounts[dim] = 1; + for (i = 1; i < ngroups; i++) + { + if (sort_item_compare(&result[dim][i-1], &result[dim][i], ssup) == 0) + { + result[dim][ncounts[dim]-1].count += result[dim][i].count; + continue; + } + + result[dim][ncounts[dim]] = result[dim][i]; + + ncounts[dim]++; + } + } + + return result; +} /* * statext_mcv_load @@ -491,19 +622,16 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) int i; int dim; int ndims = mcvlist->ndimensions; - int itemsize = ITEM_SIZE(ndims); SortSupport ssup; DimensionInfo *info; Size total_length; - /* allocate the item just once */ - char *item = palloc0(itemsize); - /* serialized items (indexes into arrays, etc.) */ - char *raw; + bytea *raw; char *ptr; + char *endptr PG_USED_FOR_ASSERTS_ONLY; /* values per dimension (and number of non-NULL values) */ Datum **values = (Datum **) palloc0(sizeof(Datum *) * ndims); @@ -558,7 +686,7 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) /* sort and deduplicate the data */ ssup[dim].ssup_cxt = CurrentMemoryContext; - ssup[dim].ssup_collation = DEFAULT_COLLATION_OID; + ssup[dim].ssup_collation = stats[dim]->attrcollid; ssup[dim].ssup_nulls_first = false; PrepareSortSupportFromOrderingOp(typentry->lt_opr, &ssup[dim]); @@ -597,33 +725,80 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) */ info[dim].nvalues = ndistinct; - if (info[dim].typlen > 0) /* fixed-length data types */ + if (info[dim].typbyval) /* by-value data types */ + { + info[dim].nbytes = info[dim].nvalues * info[dim].typlen; + + /* + * We copy the data into the MCV item during deserialization, so + * we don't need to allocate any extra space. + */ + info[dim].nbytes_aligned = 0; + } + else if (info[dim].typlen > 0) /* fixed-length by-ref */ + { + /* + * We don't care about alignment in the serialized data, so we + * pack the data as much as possible. But we also track how much + * data will be needed after deserialization, and in that case + * we need to account for alignment of each item. + * + * Note: As the items are fixed-length, we could easily compute + * this during deserialization, but we do it here anyway. + */ info[dim].nbytes = info[dim].nvalues * info[dim].typlen; + info[dim].nbytes_aligned = info[dim].nvalues * MAXALIGN(info[dim].typlen); + } else if (info[dim].typlen == -1) /* varlena */ { info[dim].nbytes = 0; + info[dim].nbytes_aligned = 0; for (i = 0; i < info[dim].nvalues; i++) { Size len; + /* + * For varlena values, we detoast the values and store the + * length and data separately. We don't bother with alignment + * here, which means that during deserialization we need to + * copy the fields and only access the copies. + */ values[dim][i] = PointerGetDatum(PG_DETOAST_DATUM(values[dim][i])); - len = VARSIZE_ANY(values[dim][i]); - info[dim].nbytes += MAXALIGN(len); + /* serialized length (uint32 length + data) */ + len = VARSIZE_ANY_EXHDR(values[dim][i]); + info[dim].nbytes += sizeof(uint32); /* length */ + info[dim].nbytes += len; /* value (no header) */ + + /* + * During deserialization we'll build regular varlena values + * with full headers, and we need to align them properly. + */ + info[dim].nbytes_aligned += MAXALIGN(VARHDRSZ + len); } } else if (info[dim].typlen == -2) /* cstring */ { info[dim].nbytes = 0; + info[dim].nbytes_aligned = 0; for (i = 0; i < info[dim].nvalues; i++) { Size len; - /* c-strings include terminator, so +1 byte */ - values[dim][i] = PointerGetDatum(PG_DETOAST_DATUM(values[dim][i])); + /* + * For cstring, we do similar thing as for varlena - first we + * store the length as uint32 and then the data. We don't care + * about alignment, which means that during deserialization we + * need to copy the fields and only access the copies. + */ + /* c-strings include terminator, so +1 byte */ len = strlen(DatumGetCString(values[dim][i])) + 1; - info[dim].nbytes += MAXALIGN(len); + info[dim].nbytes += sizeof(uint32); /* length */ + info[dim].nbytes += len; /* value */ + + /* space needed for properly aligned deserialized copies */ + info[dim].nbytes_aligned += MAXALIGN(len); } } @@ -635,34 +810,33 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) * Now we can finally compute how much space we'll actually need for the * whole serialized MCV list (varlena header, MCV header, dimension info * for each attribute, deduplicated values and items). - * - * The header fields are copied one by one, so that we don't need any - * explicit alignment (we copy them while deserializing). All fields after - * this need to be properly aligned, for direct access. */ - total_length = MAXALIGN(VARHDRSZ + (3 * sizeof(uint32)) - + sizeof(AttrNumber) + (ndims * sizeof(Oid))); + total_length = (3 * sizeof(uint32)) /* magic + type + nitems */ + + sizeof(AttrNumber) /* ndimensions */ + + (ndims * sizeof(Oid)); /* attribute types */ /* dimension info */ - total_length += MAXALIGN(ndims * sizeof(DimensionInfo)); + total_length += ndims * sizeof(DimensionInfo); /* add space for the arrays of deduplicated values */ for (i = 0; i < ndims; i++) - total_length += MAXALIGN(info[i].nbytes); + total_length += info[i].nbytes; /* - * And finally the items (no additional alignment needed, we start at - * proper alignment and the itemsize formula uses MAXALIGN) + * And finally account for the items (those are fixed-length, thanks to + * replacing values with uint16 indexes into the deduplicated arrays). */ - total_length += mcvlist->nitems * itemsize; + total_length += mcvlist->nitems * ITEM_SIZE(dim); /* * Allocate space for the whole serialized MCV list (we'll skip bytes, so * we set them to zero to make the result more compressible). */ - raw = palloc0(total_length); - SET_VARSIZE(raw, total_length); + raw = (bytea *) palloc0(VARHDRSZ + total_length); + SET_VARSIZE(raw, VARHDRSZ + total_length); + ptr = VARDATA(raw); + endptr = ptr + total_length; /* copy the MCV list header fields, one by one */ memcpy(ptr, &mcvlist->magic, sizeof(uint32)); @@ -680,12 +854,9 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) memcpy(ptr, mcvlist->types, sizeof(Oid) * ndims); ptr += (sizeof(Oid) * ndims); - /* the header may not be exactly aligned, so make sure it is */ - ptr = raw + MAXALIGN(ptr - raw); - - /* store information about the attributes */ + /* store information about the attributes (data amounts, ...) */ memcpy(ptr, info, sizeof(DimensionInfo) * ndims); - ptr += MAXALIGN(sizeof(DimensionInfo) * ndims); + ptr += sizeof(DimensionInfo) * ndims; /* Copy the deduplicated values for all attributes to the output. */ for (dim = 0; dim < ndims; dim++) @@ -707,7 +878,7 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) * assumes little endian behavior. store_att_byval does * almost what we need, but it requires properly aligned * buffer - the output buffer does not guarantee that. So we - * simply use a static Datum variable (which guarantees proper + * simply use a local Datum variable (which guarantees proper * alignment), and then copy the value from it. */ store_att_byval(&tmp, value, info[dim].typlen); @@ -723,17 +894,27 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) } else if (info[dim].typlen == -1) /* varlena */ { - int len = VARSIZE_ANY(value); + uint32 len = VARSIZE_ANY_EXHDR(DatumGetPointer(value)); - memcpy(ptr, DatumGetPointer(value), len); - ptr += MAXALIGN(len); + /* copy the length */ + memcpy(ptr, &len, sizeof(uint32)); + ptr += sizeof(uint32); + + /* data from the varlena value (without the header) */ + memcpy(ptr, VARDATA_ANY(DatumGetPointer(value)), len); + ptr += len; } else if (info[dim].typlen == -2) /* cstring */ { - Size len = strlen(DatumGetCString(value)) + 1; /* terminator */ + uint32 len = (uint32) strlen(DatumGetCString(value)) + 1; + + /* copy the length */ + memcpy(ptr, &len, sizeof(uint32)); + ptr += sizeof(uint32); + /* value */ memcpy(ptr, DatumGetCString(value), len); - ptr += MAXALIGN(len); + ptr += len; } /* no underflows or overflows */ @@ -742,9 +923,6 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) /* we should get exactly nbytes of data for this dimension */ Assert((ptr - start) == info[dim].nbytes); - - /* make sure the pointer is aligned correctly after each dimension */ - ptr = raw + MAXALIGN(ptr - raw); } /* Serialize the items, with uint16 indexes instead of the values. */ @@ -753,51 +931,56 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats) MCVItem *mcvitem = &mcvlist->items[i]; /* don't write beyond the allocated space */ - Assert(ptr <= raw + total_length - itemsize); + Assert(ptr <= (endptr - ITEM_SIZE(dim))); + + /* copy NULL and frequency flags into the serialized MCV */ + memcpy(ptr, mcvitem->isnull, sizeof(bool) * ndims); + ptr += sizeof(bool) * ndims; + + memcpy(ptr, &mcvitem->frequency, sizeof(double)); + ptr += sizeof(double); - /* reset the item (we only allocate it once and reuse it) */ - memset(item, 0, itemsize); + memcpy(ptr, &mcvitem->base_frequency, sizeof(double)); + ptr += sizeof(double); + /* store the indexes last */ for (dim = 0; dim < ndims; dim++) { + uint16 index = 0; Datum *value; /* do the lookup only for non-NULL values */ - if (mcvitem->isnull[dim]) - continue; + if (!mcvitem->isnull[dim]) + { + value = (Datum *) bsearch_arg(&mcvitem->values[dim], values[dim], + info[dim].nvalues, sizeof(Datum), + compare_scalars_simple, &ssup[dim]); - value = (Datum *) bsearch_arg(&mcvitem->values[dim], values[dim], - info[dim].nvalues, sizeof(Datum), - compare_scalars_simple, &ssup[dim]); + Assert(value != NULL); /* serialization or deduplication error */ - Assert(value != NULL); /* serialization or deduplication error */ + /* compute index within the deduplicated array */ + index = (uint16) (value - values[dim]); - /* compute index within the array */ - ITEM_INDEXES(item)[dim] = (uint16) (value - values[dim]); + /* check the index is within expected bounds */ + Assert(index < info[dim].nvalues); + } - /* check the index is within expected bounds */ - Assert(ITEM_INDEXES(item)[dim] < info[dim].nvalues); + /* copy the index into the serialized MCV */ + memcpy(ptr, &index, sizeof(uint16)); + ptr += sizeof(uint16); } - /* copy NULL and frequency flags into the item */ - memcpy(ITEM_NULLS(item, ndims), mcvitem->isnull, sizeof(bool) * ndims); - memcpy(ITEM_FREQUENCY(item, ndims), &mcvitem->frequency, sizeof(double)); - memcpy(ITEM_BASE_FREQUENCY(item, ndims), &mcvitem->base_frequency, sizeof(double)); - - /* copy the serialized item into the array */ - memcpy(ptr, item, itemsize); - - ptr += itemsize; + /* make sure we don't overflow the allocated value */ + Assert(ptr <= endptr); } /* at this point we expect to match the total_length exactly */ - Assert((ptr - raw) == total_length); + Assert(ptr == endptr); - pfree(item); pfree(values); pfree(counts); - return (bytea *) raw; + return raw; } /* @@ -816,6 +999,7 @@ statext_mcv_deserialize(bytea *data) MCVList *mcvlist; char *raw; char *ptr; + char *endptr PG_USED_FOR_ASSERTS_ONLY; int ndims, nitems; @@ -849,8 +1033,9 @@ statext_mcv_deserialize(bytea *data) mcvlist = (MCVList *) palloc0(offsetof(MCVList, items)); /* pointer to the data part (skip the varlena header) */ - ptr = VARDATA_ANY(data); raw = (char *) data; + ptr = VARDATA_ANY(raw); + endptr = (char *) raw + VARSIZE_ANY(data); /* get the header and perform further sanity checks */ memcpy(&mcvlist->magic, ptr, sizeof(uint32)); @@ -909,12 +1094,11 @@ statext_mcv_deserialize(bytea *data) memcpy(mcvlist->types, ptr, sizeof(Oid) * ndims); ptr += (sizeof(Oid) * ndims); - /* ensure alignment of the pointer (after the header fields) */ - ptr = raw + MAXALIGN(ptr - raw); - /* Now it's safe to access the dimension info. */ - info = (DimensionInfo *) ptr; - ptr += MAXALIGN(ndims * sizeof(DimensionInfo)); + info = palloc(ndims * sizeof(DimensionInfo)); + + memcpy(info, ptr, ndims * sizeof(DimensionInfo)); + ptr += (ndims * sizeof(DimensionInfo)); /* account for the value arrays */ for (dim = 0; dim < ndims; dim++) @@ -926,7 +1110,7 @@ statext_mcv_deserialize(bytea *data) Assert(info[dim].nvalues >= 0); Assert(info[dim].nbytes >= 0); - expected_size += MAXALIGN(info[dim].nbytes); + expected_size += info[dim].nbytes; } /* @@ -955,15 +1139,18 @@ statext_mcv_deserialize(bytea *data) map[dim] = (Datum *) palloc(sizeof(Datum) * info[dim].nvalues); /* space needed for a copy of data for by-ref types */ - if (!info[dim].typbyval) - datalen += MAXALIGN(info[dim].nbytes); + datalen += info[dim].nbytes_aligned; } /* - * Now resize the MCV list so that the allocation includes all the data + * Now resize the MCV list so that the allocation includes all the data. + * * Allocate space for a copy of the data, as we can't simply reference the - * original data - it may disappear while we're still using the MCV list, - * e.g. due to catcache release. Only needed for by-ref types. + * serialized data - it's not aligned properly, and it may disappear while + * we're still using the MCV list, e.g. due to catcache release. + * + * We do care about alignment here, because we will allocate all the pieces + * at once, but then use pointers to different parts. */ mcvlen = MAXALIGN(offsetof(MCVList, items) + (sizeof(MCVItem) * nitems)); @@ -1024,7 +1211,7 @@ statext_mcv_deserialize(bytea *data) /* just point into the array */ map[dim][i] = PointerGetDatum(dataptr); - dataptr += info[dim].typlen; + dataptr += MAXALIGN(info[dim].typlen); } } else if (info[dim].typlen == -1) @@ -1032,14 +1219,22 @@ statext_mcv_deserialize(bytea *data) /* varlena */ for (i = 0; i < info[dim].nvalues; i++) { - Size len = VARSIZE_ANY(ptr); + uint32 len; - memcpy(dataptr, ptr, len); - ptr += MAXALIGN(len); + /* read the uint32 length */ + memcpy(&len, ptr, sizeof(uint32)); + ptr += sizeof(uint32); + + /* the length is data-only */ + SET_VARSIZE(dataptr, len + VARHDRSZ); + memcpy(VARDATA(dataptr), ptr, len); + ptr += len; /* just point into the array */ map[dim][i] = PointerGetDatum(dataptr); - dataptr += MAXALIGN(len); + + /* skip to place of the next deserialized value */ + dataptr += MAXALIGN(len + VARHDRSZ); } } else if (info[dim].typlen == -2) @@ -1047,10 +1242,13 @@ statext_mcv_deserialize(bytea *data) /* cstring */ for (i = 0; i < info[dim].nvalues; i++) { - Size len = (strlen(ptr) + 1); /* don't forget the \0 */ + uint32 len; + + memcpy(&len, ptr, sizeof(uint32)); + ptr += sizeof(uint32); memcpy(dataptr, ptr, len); - ptr += MAXALIGN(len); + ptr += len; /* just point into the array */ map[dim][i] = PointerGetDatum(dataptr); @@ -1067,9 +1265,6 @@ statext_mcv_deserialize(bytea *data) /* check we consumed input data for this dimension exactly */ Assert(ptr == (start + info[dim].nbytes)); - - /* ensure proper alignment of the data */ - ptr = raw + MAXALIGN(ptr - raw); } /* we should have also filled the MCV list exactly */ @@ -1078,7 +1273,6 @@ statext_mcv_deserialize(bytea *data) /* deserialize the MCV items and translate the indexes to Datums */ for (i = 0; i < nitems; i++) { - uint16 *indexes = NULL; MCVItem *item = &mcvlist->items[i]; item->values = (Datum *) valuesptr; @@ -1087,27 +1281,35 @@ statext_mcv_deserialize(bytea *data) item->isnull = (bool *) isnullptr; isnullptr += MAXALIGN(sizeof(bool) * ndims); + memcpy(item->isnull, ptr, sizeof(bool) * ndims); + ptr += sizeof(bool) * ndims; - /* just point to the right place */ - indexes = ITEM_INDEXES(ptr); + memcpy(&item->frequency, ptr, sizeof(double)); + ptr += sizeof(double); - memcpy(item->isnull, ITEM_NULLS(ptr, ndims), sizeof(bool) * ndims); - memcpy(&item->frequency, ITEM_FREQUENCY(ptr, ndims), sizeof(double)); - memcpy(&item->base_frequency, ITEM_BASE_FREQUENCY(ptr, ndims), sizeof(double)); + memcpy(&item->base_frequency, ptr, sizeof(double)); + ptr += sizeof(double); - /* translate the values */ + /* finally translate the indexes (for non-NULL only) */ for (dim = 0; dim < ndims; dim++) - if (!item->isnull[dim]) - item->values[dim] = map[dim][indexes[dim]]; + { + uint16 index; + + memcpy(&index, ptr, sizeof(uint16)); + ptr += sizeof(uint16); - ptr += ITEM_SIZE(ndims); + if (item->isnull[dim]) + continue; + + item->values[dim] = map[dim][index]; + } /* check we're not overflowing the input */ - Assert(ptr <= (char *) raw + VARSIZE_ANY(data)); + Assert(ptr <= endptr); } /* check that we processed all the data */ - Assert(ptr == raw + VARSIZE_ANY(data)); + Assert(ptr == endptr); /* release the buffers used for mapping */ for (dim = 0; dim < ndims; dim++) @@ -1134,9 +1336,6 @@ Datum pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; - int call_cntr; - int max_calls; - AttInMetadata *attinmeta; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) @@ -1166,13 +1365,13 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); + tupdesc = BlessTupleDesc(tupdesc); /* * generate attribute metadata needed later to produce tuples from raw * C strings */ - attinmeta = TupleDescGetAttInMetadata(tupdesc); - funcctx->attinmeta = attinmeta; + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); } @@ -1180,111 +1379,78 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS) /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); - call_cntr = funcctx->call_cntr; - max_calls = funcctx->max_calls; - attinmeta = funcctx->attinmeta; - - if (call_cntr < max_calls) /* do when there is more left to send */ + if (funcctx->call_cntr < funcctx->max_calls) /* do when there is more left to send */ { - char **values; + Datum values[5]; + bool nulls[5]; HeapTuple tuple; Datum result; - - StringInfoData itemValues; - StringInfoData itemNulls; + ArrayBuildState *astate_values = NULL; + ArrayBuildState *astate_nulls = NULL; int i; - - Oid *outfuncs; - FmgrInfo *fmgrinfo; - MCVList *mcvlist; MCVItem *item; mcvlist = (MCVList *) funcctx->user_fctx; - Assert(call_cntr < mcvlist->nitems); - - item = &mcvlist->items[call_cntr]; + Assert(funcctx->call_cntr < mcvlist->nitems); - /* - * Prepare a values array for building the returned tuple. This should - * be an array of C strings which will be processed later by the type - * input functions. - */ - values = (char **) palloc0(5 * sizeof(char *)); - - values[0] = (char *) palloc(64 * sizeof(char)); /* item index */ - values[3] = (char *) palloc(64 * sizeof(char)); /* frequency */ - values[4] = (char *) palloc(64 * sizeof(char)); /* base frequency */ - - outfuncs = (Oid *) palloc0(sizeof(Oid) * mcvlist->ndimensions); - fmgrinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * mcvlist->ndimensions); + item = &mcvlist->items[funcctx->call_cntr]; for (i = 0; i < mcvlist->ndimensions; i++) { - bool isvarlena; - getTypeOutputInfo(mcvlist->types[i], &outfuncs[i], &isvarlena); + astate_nulls = accumArrayResult(astate_nulls, + BoolGetDatum(item->isnull[i]), + false, + BOOLOID, + CurrentMemoryContext); - fmgr_info(outfuncs[i], &fmgrinfo[i]); - } - - /* build the arrays of values / nulls */ - initStringInfo(&itemValues); - initStringInfo(&itemNulls); - - appendStringInfoChar(&itemValues, '{'); - appendStringInfoChar(&itemNulls, '{'); - - for (i = 0; i < mcvlist->ndimensions; i++) - { - Datum val, - valout; - - if (i > 0) + if (!item->isnull[i]) { - appendStringInfoString(&itemValues, ", "); - appendStringInfoString(&itemNulls, ", "); + bool isvarlena; + Oid outfunc; + FmgrInfo fmgrinfo; + Datum val; + text *txt; + + /* lookup output func for the type */ + getTypeOutputInfo(mcvlist->types[i], &outfunc, &isvarlena); + fmgr_info(outfunc, &fmgrinfo); + + val = FunctionCall1(&fmgrinfo, item->values[i]); + txt = cstring_to_text(DatumGetPointer(val)); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(txt), + false, + TEXTOID, + CurrentMemoryContext); } - - if (item->isnull[i]) - valout = CStringGetDatum("NULL"); else - { - val = item->values[i]; - valout = FunctionCall1(&fmgrinfo[i], val); - } - - appendStringInfoString(&itemValues, DatumGetCString(valout)); - appendStringInfoString(&itemNulls, item->isnull[i] ? "t" : "f"); + astate_values = accumArrayResult(astate_values, + (Datum) 0, + true, + TEXTOID, + CurrentMemoryContext); } - appendStringInfoChar(&itemValues, '}'); - appendStringInfoChar(&itemNulls, '}'); + values[0] = Int32GetDatum(funcctx->call_cntr); + values[1] = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + values[2] = PointerGetDatum(makeArrayResult(astate_nulls, CurrentMemoryContext)); + values[3] = Float8GetDatum(item->frequency); + values[4] = Float8GetDatum(item->base_frequency); - snprintf(values[0], 64, "%d", call_cntr); - snprintf(values[3], 64, "%f", item->frequency); - snprintf(values[4], 64, "%f", item->base_frequency); - - values[1] = itemValues.data; - values[2] = itemNulls.data; + /* no NULLs in the tuple */ + memset(nulls, 0, sizeof(nulls)); /* build a tuple */ - tuple = BuildTupleFromCStrings(attinmeta, values); + tuple = heap_form_tuple(funcctx->attinmeta->tupdesc, values, nulls); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); - /* clean up (this is not really necessary) */ - pfree(itemValues.data); - pfree(itemNulls.data); - - pfree(values[0]); - pfree(values[3]); - pfree(values[4]); - pfree(values); - SRF_RETURN_NEXT(funcctx, result); } else /* do when there is no more left */ @@ -1413,45 +1579,23 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses, if (is_opclause(clause)) { OpExpr *expr = (OpExpr *) clause; - bool varonleft = true; - bool ok; FmgrInfo opproc; - /* get procedure computing operator selectivity */ - RegProcedure oprrest = get_oprrest(expr->opno); + /* valid only after examine_opclause_expression returns true */ + Var *var; + Const *cst; + bool varonleft; fmgr_info(get_opcode(expr->opno), &opproc); - ok = (NumRelids(clause) == 1) && - (is_pseudo_constant_clause(lsecond(expr->args)) || - (varonleft = false, - is_pseudo_constant_clause(linitial(expr->args)))); - - if (ok) + /* extract the var and const from the expression */ + if (examine_opclause_expression(expr, &var, &cst, &varonleft)) { - TypeCacheEntry *typecache; - FmgrInfo gtproc; - Var *var; - Const *cst; - bool isgt; int idx; - /* extract the var and const from the expression */ - var = (varonleft) ? linitial(expr->args) : lsecond(expr->args); - cst = (varonleft) ? lsecond(expr->args) : linitial(expr->args); - isgt = (!varonleft); - - /* strip binary-compatible relabeling */ - if (IsA(var, RelabelType)) - var = (Var *) ((RelabelType *) var)->arg; - /* match the attribute to a dimension of the statistic */ idx = bms_member_index(keys, var->varattno); - /* get information about the >= procedure */ - typecache = lookup_type_cache(var->vartype, TYPECACHE_GT_OPR); - fmgr_info(get_opcode(typecache->gt_opr), >proc); - /* * Walk through the MCV items and evaluate the current clause. * We can skip items that were already ruled out, and @@ -1460,84 +1604,53 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses, */ for (i = 0; i < mcvlist->nitems; i++) { - bool mismatch = false; + bool match = true; MCVItem *item = &mcvlist->items[i]; /* - * For AND-lists, we can also mark NULL items as 'no - * match' (and then skip them). For OR-lists this is not - * possible. + * When the MCV item or the Const value is NULL we can treat + * this as a mismatch. We must not call the operator because + * of strictness. */ - if ((!is_or) && item->isnull[idx]) - matches[i] = false; - - /* skip MCV items that were already ruled out */ - if ((!is_or) && (matches[i] == false)) - continue; - else if (is_or && (matches[i] == true)) - continue; - - switch (oprrest) + if (item->isnull[idx] || cst->constisnull) { - case F_EQSEL: - case F_NEQSEL: - - /* - * We don't care about isgt in equality, because - * it does not matter whether it's (var op const) - * or (const op var). - */ - mismatch = !DatumGetBool(FunctionCall2Coll(&opproc, - DEFAULT_COLLATION_OID, - cst->constvalue, - item->values[idx])); - - break; - - case F_SCALARLTSEL: /* column < constant */ - case F_SCALARLESEL: /* column <= constant */ - case F_SCALARGTSEL: /* column > constant */ - case F_SCALARGESEL: /* column >= constant */ - - /* - * First check whether the constant is below the - * lower boundary (in that case we can skip the - * bucket, because there's no overlap). - */ - if (isgt) - mismatch = !DatumGetBool(FunctionCall2Coll(&opproc, - DEFAULT_COLLATION_OID, - cst->constvalue, - item->values[idx])); - else - mismatch = !DatumGetBool(FunctionCall2Coll(&opproc, - DEFAULT_COLLATION_OID, - item->values[idx], - cst->constvalue)); - - break; + matches[i] = RESULT_MERGE(matches[i], is_or, false); + continue; } /* - * XXX The conditions on matches[i] are not needed, as we - * skip MCV items that can't become true/false, depending - * on the current flag. See beginning of the loop over MCV - * items. + * Skip MCV items that can't change result in the bitmap. + * Once the value gets false for AND-lists, or true for + * OR-lists, we don't need to look at more clauses. */ - - if ((is_or) && (!mismatch)) - { - /* OR - was not a match before, matches now */ - matches[i] = true; - continue; - } - else if ((!is_or) && mismatch) - { - /* AND - was a match before, does not match anymore */ - matches[i] = false; + if (RESULT_IS_FINAL(matches[i], is_or)) continue; - } + /* + * First check whether the constant is below the lower + * boundary (in that case we can skip the bucket, because + * there's no overlap). + * + * We don't store collations used to build the statistics, + * but we can use the collation for the attribute itself, + * as stored in varcollid. We do reset the statistics after + * a type change (including collation change), so this is + * OK. We may need to relax this after allowing extended + * statistics on expressions. + */ + if (varonleft) + match = DatumGetBool(FunctionCall2Coll(&opproc, + var->varcollid, + item->values[idx], + cst->constvalue)); + else + match = DatumGetBool(FunctionCall2Coll(&opproc, + var->varcollid, + cst->constvalue, + item->values[idx])); + + /* update the match bitmap with the result */ + matches[i] = RESULT_MERGE(matches[i], is_or, match); } } } @@ -1572,10 +1685,7 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses, } /* now, update the match bitmap, depending on OR/AND type */ - if (is_or) - matches[i] = Max(matches[i], match); - else - matches[i] = Min(matches[i], match); + matches[i] = RESULT_MERGE(matches[i], is_or, match); } } else if (is_orclause(clause) || is_andclause(clause)) @@ -1602,13 +1712,7 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses, * condition when merging the results. */ for (i = 0; i < mcvlist->nitems; i++) - { - /* Is this OR or AND clause? */ - if (is_or) - matches[i] = Max(matches[i], bool_matches[i]); - else - matches[i] = Min(matches[i], bool_matches[i]); - } + matches[i] = RESULT_MERGE(matches[i], is_or, bool_matches[i]); pfree(bool_matches); } @@ -1632,25 +1736,11 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses, /* * Merge the bitmap produced by mcv_get_match_bitmap into the - * current one. + * current one. We're handling a NOT clause, so invert the result + * before merging it into the global bitmap. */ for (i = 0; i < mcvlist->nitems; i++) - { - /* - * When handling a NOT clause, we need to invert the result - * before merging it into the global result. - */ - if (not_matches[i] == false) - not_matches[i] = true; - else - not_matches[i] = false; - - /* Is this OR or AND clause? */ - if (is_or) - matches[i] = Max(matches[i], not_matches[i]); - else - matches[i] = Min(matches[i], not_matches[i]); - } + matches[i] = RESULT_MERGE(matches[i], is_or, !not_matches[i]); pfree(not_matches); } @@ -1679,17 +1769,12 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses, if (!item->isnull[idx] && DatumGetBool(item->values[idx])) match = true; - /* now, update the match bitmap, depending on OR/AND type */ - if (is_or) - matches[i] = Max(matches[i], match); - else - matches[i] = Min(matches[i], match); + /* update the result bitmap */ + matches[i] = RESULT_MERGE(matches[i], is_or, match); } } else - { elog(ERROR, "unknown clause type: %d", clause->type); - } } return matches; diff --git a/src/backend/statistics/mvdistinct.c b/src/backend/statistics/mvdistinct.c index 9ebf183d909c..f3383c05d912 100644 --- a/src/backend/statistics/mvdistinct.c +++ b/src/backend/statistics/mvdistinct.c @@ -339,7 +339,7 @@ statext_ndistinct_deserialize(bytea *data) * input routine for type pg_ndistinct * * pg_ndistinct is real enough to be a table column, but it has no - * operations of its own, and disallows input (jus like pg_node_tree). + * operations of its own, and disallows input (just like pg_node_tree). */ Datum pg_ndistinct_in(PG_FUNCTION_ARGS) @@ -477,7 +477,7 @@ ndistinct_for_combination(double totalrows, int numrows, HeapTuple *rows, colstat->attrtypid); /* prepare the sort function for this dimension */ - multi_sort_add_dimension(mss, i, type->lt_opr, type->typcollation); + multi_sort_add_dimension(mss, i, type->lt_opr, colstat->attrcollid); /* accumulate all the data for this dimension into the arrays */ for (j = 0; j < numrows; j++) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index f11e47fc922c..10ec6ac63253 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -450,7 +450,8 @@ static void PinBuffer_Locked(BufferDesc *buf); static void UnpinBuffer(BufferDesc *buf, bool fixOwner); static void BufferSync(int flags); static uint32 WaitBufHdrUnlocked(BufferDesc *buf); -static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *flush_context); +static int SyncOneBuffer(int buf_id, bool skip_recently_used, + WritebackContext *wb_context); static void WaitIO(BufferDesc *buf); static bool StartBufferIO(BufferDesc *buf, bool forInput); static void TerminateBufferIO(BufferDesc *buf, bool clear_dirty, @@ -2481,7 +2482,7 @@ BgBufferSync(WritebackContext *wb_context) * BUF_REUSABLE: buffer is available for replacement, ie, it has * pin count 0 and usage count 0. * - * (BUF_WRITTEN could be set in error if FlushBuffers finds the buffer clean + * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) * * Note: caller must have done ResourceOwnerEnlargeBuffers. diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 222b94eb7286..1d533f9c8473 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -376,7 +376,7 @@ DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum, * This function removes from the buffer pool all pages of all forks * of the specified relation. * - * See DropRelFileNodeAllBuffers in bufmgr.c for more notes. + * See DropRelFileNodesAllBuffers in bufmgr.c for more notes. */ void DropRelFileNodeAllLocalBuffers(RelFileNode rnode) diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c index d7755845b49d..1924fc9909a0 100644 --- a/src/backend/storage/file/buffile.c +++ b/src/backend/storage/file/buffile.c @@ -907,7 +907,7 @@ BufFileSeek(BufFile *file, int fileno, off_t offset, int whence) /* * Relative seek considers only the signed offset, ignoring - * fileno. Note that large offsets (> 1 gig) risk overflow in this + * fileno. Note that large offsets (> 1 GB) risk overflow in this * add, unless we have 64-bit off_t. */ newFile = file->curFile; diff --git a/src/backend/storage/file/copydir.c b/src/backend/storage/file/copydir.c index 30f6200a86fc..ca1c9cd7655b 100644 --- a/src/backend/storage/file/copydir.c +++ b/src/backend/storage/file/copydir.c @@ -212,12 +212,12 @@ copy_file(char *fromfile, char *tofile) if (offset > flush_offset) pg_flush_data(dstfd, flush_offset, offset - flush_offset); - if (CloseTransientFile(dstfd)) + if (CloseTransientFile(dstfd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tofile))); - if (CloseTransientFile(srcfd)) + if (CloseTransientFile(srcfd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", fromfile))); diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index 256a7b91be8e..95eca5132c8b 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -684,7 +684,7 @@ durable_rename(const char *oldfile, const char *newfile, int elevel) return -1; } - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) { ereport(elevel, (errcode_for_file_access(), @@ -934,7 +934,7 @@ count_usable_fds(int max_to_probe, int *usable_fds, int *already_open) /* * set_max_safe_fds - * Determine number of filedescriptors that fd.c is allowed to use + * Determine number of file descriptors that fd.c is allowed to use */ void set_max_safe_fds(void) @@ -1084,7 +1084,7 @@ LruDelete(File file) * Close the file. We aren't expecting this to fail; if it does, better * to leak the FD than to mess up our internal state. */ - if (close(vfdP->fd)) + if (close(vfdP->fd) != 0) elog(vfdP->fdstate & FD_TEMP_FILE_LIMIT ? LOG : data_sync_elevel(LOG), "could not close file \"%s\": %m", vfdP->fileName); vfdP->fd = VFD_CLOSED; @@ -1794,7 +1794,7 @@ FileClose(File file) if (!FileIsNotOpen(file)) { /* close the file */ - if (gp_retry_close(vfdP->fd)) + if (gp_retry_close(vfdP->fd) != 0) { /* * We may need to panic on failure to close non-temporary files; @@ -3380,7 +3380,8 @@ SyncDataDirectory(void) * * Errors are reported at level elevel, which might be ERROR or less. * - * See also walkdir in initdb.c, which is a frontend version of this logic. + * See also walkdir in file_utils.c, which is a frontend version of this + * logic. */ static void walkdir(const char *path, @@ -3474,7 +3475,7 @@ pre_sync_fname(const char *fname, bool isdir, int elevel) */ pg_flush_data(fd, 0, 0); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(elevel, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", fname))); @@ -3576,7 +3577,7 @@ fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel) return -1; } - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) { ereport(elevel, (errcode_for_file_access(), diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c index d612f8edf039..d790ac2ceb12 100644 --- a/src/backend/storage/freespace/freespace.c +++ b/src/backend/storage/freespace/freespace.c @@ -225,7 +225,7 @@ XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk, } /* - * GetRecordedFreePage - return the amount of free space on a particular page, + * GetRecordedFreeSpace - return the amount of free space on a particular page, * according to the FSM. */ Size @@ -419,7 +419,7 @@ fsm_space_cat_to_avail(uint8 cat) /* * Which category does a page need to have, to accommodate x bytes of data? - * While fsm_size_to_avail_cat() rounds down, this needs to round up. + * While fsm_space_avail_to_cat() rounds down, this needs to round up. */ static uint8 fsm_space_needed_to_cat(Size needed) diff --git a/src/backend/storage/ipc/dsm_impl.c b/src/backend/storage/ipc/dsm_impl.c index bd9bf67fc2e3..780f9cebde27 100644 --- a/src/backend/storage/ipc/dsm_impl.c +++ b/src/backend/storage/ipc/dsm_impl.c @@ -917,7 +917,7 @@ dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size, *mapped_address = address; *mapped_size = request_size; - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) { ereport(elevel, (errcode_for_file_access(), diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index faa3e27d0088..2b4edf520adb 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -1542,10 +1542,11 @@ GetLocalOldestXmin(Relation rel, int flags) result = replication_slot_xmin; /* - * After locks have been released and defer_cleanup_age has been applied, - * check whether we need to back up further to make logical decoding - * possible. We need to do so if we're computing the global limit (rel = - * NULL) or if the passed relation is a catalog relation of some kind. + * After locks have been released and vacuum_defer_cleanup_age has been + * applied, check whether we need to back up further to make logical + * decoding possible. We need to do so if we're computing the global limit + * (rel = NULL) or if the passed relation is a catalog relation of some + * kind. */ if (!(flags & PROCARRAY_SLOTS_XMIN) && (rel == NULL || @@ -4018,7 +4019,7 @@ FindProcByGpSessionId(long gp_session_id) } /* ---------------------------------------------- - * KnownAssignedTransactions sub-module + * KnownAssignedTransactionIds sub-module * ---------------------------------------------- */ @@ -4057,7 +4058,7 @@ FindProcByGpSessionId(long gp_session_id) * * When we throw away subXIDs from KnownAssignedXids, we need to keep track of * that, similarly to tracking overflow of a PGPROC's subxids array. We do - * that by remembering the lastOverflowedXID, ie the last thrown-away subXID. + * that by remembering the lastOverflowedXid, ie the last thrown-away subXID. * As long as that is within the range of interesting XIDs, we have to assume * that subXIDs are missing from snapshots. (Note that subXID overflow occurs * on primary when 65th subXID arrives, whereas on standby it occurs when 64th diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c index 0dbc6f4e179c..77e3c9619703 100644 --- a/src/backend/storage/ipc/sinval.c +++ b/src/backend/storage/ipc/sinval.c @@ -74,8 +74,7 @@ SendSharedInvalidMessages(const SharedInvalidationMessage *msgs, int n) * sucked out of sinvaladt.c. */ void -ReceiveSharedInvalidMessages( - void (*invalFunction) (SharedInvalidationMessage *msg), +ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *msg), void (*resetFunction) (void)) { #define MAXINVALMSGS 32 diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 210be2f0af93..25bff88cc06e 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -99,7 +99,7 @@ InitRecoveryTransactionEnvironment(void) * Lock a virtual transaction id for Startup process. * * We need to do GetNextLocalTransactionId() because - * SharedInvalBackendInit() leaves localTransactionid invalid and the lock + * SharedInvalBackendInit() leaves localTransactionId invalid and the lock * manager doesn't like that at all. * * Note that we don't need to run XactLockTableInsert() because nobody diff --git a/src/backend/storage/lmgr/condition_variable.c b/src/backend/storage/lmgr/condition_variable.c index 58b7b514720d..e08507f0cc59 100644 --- a/src/backend/storage/lmgr/condition_variable.c +++ b/src/backend/storage/lmgr/condition_variable.c @@ -19,6 +19,7 @@ #include "postgres.h" #include "miscadmin.h" +#include "portability/instr_time.h" #include "storage/condition_variable.h" #include "storage/ipc.h" #include "storage/proc.h" @@ -122,8 +123,24 @@ ConditionVariablePrepareToSleep(ConditionVariable *cv) void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info) { - WaitEvent event; - bool done = false; + (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ , + wait_event_info); +} + +/* + * Wait for a condition variable to be signaled or a timeout to be reached. + * + * Returns true when timeout expires, otherwise returns false. + * + * See ConditionVariableSleep() for general usage. + */ +bool +ConditionVariableTimedSleep(ConditionVariable *cv, long timeout, + uint32 wait_event_info) +{ + long cur_timeout = -1; + instr_time start_time; + instr_time cur_time; /* * If the caller didn't prepare to sleep explicitly, then do so now and @@ -143,23 +160,37 @@ ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info) if (cv_sleep_target != cv) { ConditionVariablePrepareToSleep(cv); - return; + return false; } - do + /* + * Record the current time so that we can calculate the remaining timeout + * if we are woken up spuriously. + */ + if (timeout >= 0) { - CHECK_FOR_INTERRUPTS(); + INSTR_TIME_SET_CURRENT(start_time); + Assert(timeout >= 0 && timeout <= INT_MAX); + cur_timeout = timeout; + } + + while (true) + { + WaitEvent event; + bool done = false; /* * Wait for latch to be set. (If we're awakened for some other * reason, the code below will cope anyway.) */ - (void) WaitEventSetWait(cv_wait_event_set, -1, &event, 1, + (void) WaitEventSetWait(cv_wait_event_set, cur_timeout, &event, 1, wait_event_info); /* Reset latch before examining the state of the wait list. */ ResetLatch(MyLatch); + CHECK_FOR_INTERRUPTS(); + /* * If this process has been taken out of the wait list, then we know * that it has been signaled by ConditionVariableSignal (or @@ -182,7 +213,23 @@ ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info) proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink); } SpinLockRelease(&cv->mutex); - } while (!done); + + /* We were signaled, so return */ + if (done) + return false; + + /* If we're not done, update cur_timeout for next iteration */ + if (timeout >= 0) + { + INSTR_TIME_SET_CURRENT(cur_time); + INSTR_TIME_SUBTRACT(cur_time, start_time); + cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time); + + /* Have we crossed the timeout threshold? */ + if (cur_timeout <= 0) + return true; + } + } } /* @@ -198,6 +245,7 @@ void ConditionVariableCancelSleep(void) { ConditionVariable *cv = cv_sleep_target; + bool signaled = false; if (cv == NULL) return; @@ -205,8 +253,18 @@ ConditionVariableCancelSleep(void) SpinLockAcquire(&cv->mutex); if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink)) proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink); + else + signaled = true; SpinLockRelease(&cv->mutex); + /* + * If we've received a signal, pass it on to another waiting process, if + * there is one. Otherwise a call to ConditionVariableSignal() might get + * lost, despite there being another process ready to handle it. + */ + if (signaled) + ConditionVariableSignal(cv); + cv_sleep_target = NULL; } diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c index acb01c93bce0..a2d0b2ff5f70 100644 --- a/src/backend/storage/lmgr/deadlock.c +++ b/src/backend/storage/lmgr/deadlock.c @@ -927,6 +927,12 @@ TopoSort(LOCK *lock, * in the same lock group on the queue, set their number of * beforeConstraints to -1 to indicate that they should be emitted * with their groupmates rather than considered separately. + * + * In this loop and the similar one just below, it's critical that we + * consistently select the same representative member of any one lock + * group, so that all the constraints are associated with the same + * proc, and the -1's are only associated with not-representative + * members. We select the last one in the topoProcs array. */ proc = constraints[i].waiter; Assert(proc != NULL); @@ -945,7 +951,6 @@ TopoSort(LOCK *lock, Assert(beforeConstraints[j] <= 0); beforeConstraints[j] = -1; } - break; } } @@ -982,6 +987,7 @@ TopoSort(LOCK *lock, if (kk < 0) continue; + Assert(beforeConstraints[jj] >= 0); beforeConstraints[jj]++; /* waiter must come before */ /* add this constraint to list of after-constraints for blocker */ constraints[i].pred = jj; @@ -1120,7 +1126,7 @@ DeadLockReport(void) } /* Duplicate all the above for the server ... */ - appendStringInfoString(&logbuf, clientbuf.data); + appendBinaryStringInfo(&logbuf, clientbuf.data, clientbuf.len); /* ... and add info about query strings */ for (i = 0; i < nDeadlockDetails; i++) diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 051185b76928..e139e8f65364 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -248,9 +248,9 @@ static PROCLOCK *FastPathGetRelationLockEntry(LOCALLOCK *locallock); /* * To make the fast-path lock mechanism work, we must have some way of - * preventing the use of the fast-path when a conflicting lock might be - * present. We partition* the locktag space into FAST_PATH_HASH_BUCKETS - * partitions, and maintain an integer count of the number of "strong" lockers + * preventing the use of the fast-path when a conflicting lock might be present. + * We partition* the locktag space into FAST_PATH_STRONG_LOCK_HASH_PARTITIONS, + * and maintain an integer count of the number of "strong" lockers * in each partition. When any "strong" lockers are present (which is * hopefully not very often), the fast-path mechanism can't be used, and we * must fall back to the slower method of pushing matching locks directly @@ -3025,7 +3025,7 @@ FastPathTransferRelationLocks(LockMethod lockMethodTable, const LOCKTAG *locktag } /* - * FastPathGetLockEntry + * FastPathGetRelationLockEntry * Return the PROCLOCK for a lock originally taken via the fast-path, * transferring it to the primary lock table if necessary. * @@ -3212,8 +3212,8 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp) * the lock, then we needn't examine the individual relation IDs * at all; none of them can be relevant. * - * See FastPathTransferLocks() for discussion of why we do this - * test after acquiring the lock. + * See FastPathTransferRelationLocks() for discussion of why we do + * this test after acquiring the lock. */ if (proc->databaseId != locktag->locktag_field1) { diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index bc1aa88322b0..c77d47c01c6e 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -232,7 +232,7 @@ LOG_LWDEBUG(const char *where, LWLock *lock, const char *msg) static void init_lwlock_stats(void); static void print_lwlock_stats(int code, Datum arg); -static lwlock_stats * get_lwlock_stats_entry(LWLock *lockid); +static lwlock_stats * get_lwlock_stats_entry(LWLock *lock); static void init_lwlock_stats(void) diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index e585e4a0eeaf..54d3d8e2f863 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -3405,8 +3405,8 @@ ReleasePredicateLocks(bool isCommit, bool isReadOnlySafe) * * If this value is changing, we don't care that much whether we get the * old or new value -- it is just used to determine how far - * GlobalSerializableXmin must advance before this transaction can be - * fully cleaned up. The worst that could happen is we wait for one more + * SxactGlobalXmin must advance before this transaction can be fully + * cleaned up. The worst that could happen is we wait for one more * transaction to complete before freeing some RAM; correctness of visible * behavior is not affected. */ @@ -4360,7 +4360,7 @@ CheckTargetForConflictsIn(PREDICATELOCKTARGETTAG *targettag) /* * If we found one of our own SIREAD locks to remove, remove it now. * - * At this point our transaction already has an ExclusiveRowLock on the + * At this point our transaction already has a RowExclusiveLock on the * relation, so we are OK to drop the predicate lock on the tuple, if * found, without fearing that another write against the tuple will occur * before the MVCC information makes it to the buffer. @@ -4820,7 +4820,7 @@ OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader, * * If a dangerous structure is found, the pivot (the near conflict) is * marked for death, because rolling back another transaction might mean - * that we flail without ever making progress. This transaction is + * that we fail without ever making progress. This transaction is * committing writes, so letting it commit ensures progress. If we * canceled the far conflict, it might immediately fail again on retry. */ diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c index e78ff33b0046..84bfbb6aae3d 100644 --- a/src/backend/storage/smgr/md.c +++ b/src/backend/storage/smgr/md.c @@ -33,6 +33,7 @@ #include "miscadmin.h" #include "access/xlogutils.h" #include "access/xlog.h" +#include "commands/tablespace.h" #include "pgstat.h" #include "postmaster/bgwriter.h" #include "storage/fd.h" @@ -128,7 +129,7 @@ static MemoryContext MdCxt; /* context for all MdfdVec objects */ /* local routines */ static void mdunlinkfork(RelFileNodeBackend rnode, ForkNumber forkNum, bool isRedo); -static MdfdVec *mdopen(SMgrRelation reln, ForkNumber forknum, int behavior); +static MdfdVec *mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior); static void register_dirty_segment(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg); static void register_unlink_segment(RelFileNodeBackend rnode, ForkNumber forknum, @@ -173,7 +174,7 @@ mdexists(SMgrRelation reln, ForkNumber forkNum) */ mdclose(reln, forkNum); - return (mdopen(reln, forkNum, EXTENSION_RETURN_NULL) != NULL); + return (mdopenfork(reln, forkNum, EXTENSION_RETURN_NULL) != NULL); } /* @@ -193,6 +194,19 @@ mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo) Assert(reln->md_num_open_segs[forkNum] == 0); + /* + * We may be using the target table space for the first time in this + * database, so create a per-database subdirectory if needed. + * + * XXX this is a fairly ugly violation of module layering, but this seems + * to be the best place to put the check. Maybe TablespaceCreateDbspace + * should be here and not in commands/tablespace.c? But that would imply + * importing a lot of stuff that smgr.c oughtn't know, either. + */ + TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode, + reln->smgr_rnode.node.dbNode, + isRedo); + path = relpath(reln->smgr_rnode, forkNum); fd = PathNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY); @@ -513,7 +527,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, } /* - * mdopen() -- Open the specified relation. + * mdopenfork() -- Open one fork of the specified relation. * * Note we only open the first segment, when there are multiple segments. * @@ -523,7 +537,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, * invent one out of whole cloth. */ static MdfdVec * -mdopen(SMgrRelation reln, ForkNumber forknum, int behavior) +mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior) { MdfdVec *mdfd; char *path; @@ -562,6 +576,17 @@ mdopen(SMgrRelation reln, ForkNumber forknum, int behavior) return mdfd; } +/* + * mdopen() -- Initialize newly-opened relation. + */ +void +mdopen(SMgrRelation reln) +{ + /* mark it not open */ + for (int forknum = 0; forknum <= MAX_FORKNUM; forknum++) + reln->md_num_open_segs[forknum] = 0; +} + /* * mdclose() -- Close the specified relation, if it isn't closed already. */ @@ -794,14 +819,14 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, * mdnblocks() -- Get the number of blocks stored in a relation. * * Important side effect: all active segments of the relation are opened - * and added to the mdfd_seg_fds array. If this routine has not been + * and added to the md_seg_fds array. If this routine has not been * called, then only segments up to the last one actually touched * are present in the array. */ BlockNumber mdnblocks(SMgrRelation reln, ForkNumber forknum) { - MdfdVec *v = mdopen(reln, forknum, EXTENSION_FAIL); + MdfdVec *v = mdopenfork(reln, forknum, EXTENSION_FAIL); BlockNumber nblocks; BlockNumber segno = 0; @@ -1273,7 +1298,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno, v = &reln->md_seg_fds[forknum][reln->md_num_open_segs[forknum] - 1]; else { - v = mdopen(reln, forknum, behavior); + v = mdopenfork(reln, forknum, behavior); if (!v) return NULL; /* if behavior & EXTENSION_RETURN_NULL */ } @@ -1299,9 +1324,6 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno, * replaying WAL data that has a write into a high-numbered * segment of a relation that was later deleted. We want to go * ahead and create the segments so we can finish out the replay. - * However if the caller has specified - * EXTENSION_REALLY_RETURN_NULL, then extension is not desired - * even in recovery; we won't reach this point in that case. * * We have to maintain the invariant that segments before the last * active segment are of size RELSEG_SIZE; therefore, if diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c index cf7fe86d081c..271c1d25b827 100644 --- a/src/backend/storage/smgr/smgr.c +++ b/src/backend/storage/smgr/smgr.c @@ -24,8 +24,6 @@ #include "access/xlogutils.h" #include "catalog/catalog.h" #include "catalog/indexing.h" -#include "commands/tablespace.h" -#include "postmaster/postmaster.h" #include "lib/ilist.h" #include "storage/bufmgr.h" #include "storage/ipc.h" @@ -50,6 +48,7 @@ static const f_smgr smgrsw[] = { { .smgr_init = mdinit, .smgr_shutdown = NULL, + .smgr_open = mdopen, .smgr_close = mdclose, .smgr_create = mdcreate, .smgr_exists = mdexists, @@ -224,8 +223,6 @@ smgropen(RelFileNode rnode, BackendId backend, SMgrImpl which) /* Initialize it if not present before */ if (!found) { - int forknum; - /* hash_search already filled in the lookup key */ reln->smgr_owner = NULL; reln->smgr_targblock = InvalidBlockNumber; @@ -234,9 +231,8 @@ smgropen(RelFileNode rnode, BackendId backend, SMgrImpl which) reln->smgr_which = which; reln->storageManager = smgr(backend, rnode, which); - /* mark it not open */ - for (forknum = 0; forknum <= MAX_FORKNUM; forknum++) - reln->md_num_open_segs[forknum] = 0; + /* implementation-specific initialization */ + smgrsw[reln->smgr_which].smgr_open(reln); /* it has no owner yet */ dlist_push_tail(&unowned_relns, &reln->node); @@ -385,35 +381,11 @@ smgrclosenode(RelFileNodeBackend rnode) * Given an already-created (but presumably unused) SMgrRelation, * cause the underlying disk file or other storage for the fork * to be created. - * - * If isRedo is true, it is okay for the underlying file to exist - * already because we are in a WAL replay sequence. */ void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo) { - /* - * Exit quickly in WAL replay mode if we've already opened the file. If - * it's open, it surely must exist. - */ - if (isRedo && reln->md_num_open_segs[forknum] > 0) - return; - - /* - * We may be using the target table space for the first time in this - * database, so create a per-database subdirectory if needed. - * - * XXX this is a fairly ugly violation of module layering, but this seems - * to be the best place to put the check. Maybe TablespaceCreateDbspace - * should be here and not in commands/tablespace.c? But that would imply - * importing a lot of stuff that smgr.c oughtn't know, either. - */ - TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode, - reln->smgr_rnode.node.dbNode, - isRedo); - - (*reln->storageManager).smgr_create(reln, forknum, isRedo); - + smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo); if (file_create_hook) (*file_create_hook)(reln->smgr_rnode); } diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c index 1c139fceeb77..a34647e8fcab 100644 --- a/src/backend/storage/sync/sync.c +++ b/src/backend/storage/sync/sync.c @@ -496,9 +496,7 @@ RememberSyncRequest(const FileTag *ftag, SyncRequestType type) { HASH_SEQ_STATUS hstat; PendingFsyncEntry *entry; - ListCell *cell, - *prev, - *next; + ListCell *cell; /* Cancel matching fsync requests */ hash_seq_init(&hstat, pendingOps); @@ -510,20 +508,16 @@ RememberSyncRequest(const FileTag *ftag, SyncRequestType type) } /* Remove matching unlink requests */ - prev = NULL; - for (cell = list_head(pendingUnlinks); cell; cell = next) + foreach(cell, pendingUnlinks) { PendingUnlinkEntry *entry = (PendingUnlinkEntry *) lfirst(cell); - next = lnext(cell); if (entry->tag.handler == ftag->handler && syncsw[ftag->handler].sync_filetagmatches(ftag, &entry->tag)) { - pendingUnlinks = list_delete_cell(pendingUnlinks, cell, prev); + pendingUnlinks = foreach_delete_current(pendingUnlinks, cell); pfree(entry); } - else - prev = cell; } } else if (type == SYNC_UNLINK_REQUEST) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 1daaadb8aff6..54bca9d40ceb 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -1754,6 +1754,7 @@ exec_simple_query(const char *query_string) bool snapshot_set = false; const char *commandTag; char completionTag[COMPLETION_TAG_BUFSIZE]; + MemoryContext per_parsetree_context = NULL; List *querytree_list, *plantree_list; Portal portal; @@ -1837,10 +1838,25 @@ exec_simple_query(const char *query_string) /* * OK to analyze, rewrite, and plan this query. * - * Switch to appropriate context for constructing querytrees (again, - * these must outlive the execution context). + * Switch to appropriate context for constructing query and plan trees + * (these can't be in the transaction context, as that will get reset + * when the command is COMMIT/ROLLBACK). If we have multiple + * parsetrees, we use a separate context for each one, so that we can + * free that memory before moving on to the next one. But for the + * last (or only) parsetree, just use MessageContext, which will be + * reset shortly after completion anyway. In event of an error, the + * per_parsetree_context will be deleted when MessageContext is reset. */ - oldcontext = MemoryContextSwitchTo(MessageContext); + if (lnext(parsetree_list, parsetree_item) != NULL) + { + per_parsetree_context = + AllocSetContextCreate(MessageContext, + "per-parsetree message context", + ALLOCSET_DEFAULT_SIZES); + oldcontext = MemoryContextSwitchTo(per_parsetree_context); + } + else + oldcontext = MemoryContextSwitchTo(MessageContext); querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0, NULL); @@ -1865,8 +1881,8 @@ exec_simple_query(const char *query_string) /* * We don't have to copy anything into the portal, because everything - * we are passing here is in MessageContext, which will outlive the - * portal anyway. + * we are passing here is in MessageContext or the + * per_parsetree_context, and so will outlive the portal anyway. */ PortalDefineQuery(portal, NULL, @@ -1930,7 +1946,7 @@ exec_simple_query(const char *query_string) PortalDrop(portal, false); - if (lnext(parsetree_item) == NULL) + if (lnext(parsetree_list, parsetree_item) == NULL) { /* * If this is the last parsetree of the query string, close down @@ -1978,6 +1994,10 @@ exec_simple_query(const char *query_string) * aborted by error will not send an EndCommand report at all.) */ EndCommand(completionTag, dest); + + /* Now we may drop the per-parsetree context, if one was created. */ + if (per_parsetree_context) + MemoryContextDelete(per_parsetree_context); } /* end loop over parsetrees */ /* diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 3f647d01e8b4..669ea69c60e7 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -1203,8 +1203,8 @@ FillPortalStore(Portal portal, bool isTopLevel) /* * Run the portal to completion just as for the default - * MULTI_QUERY case, but send the primary query's output to the - * tuplestore. Auxiliary query outputs are discarded. Set the + * PORTAL_MULTI_QUERY case, but send the primary query's output to + * the tuplestore. Auxiliary query outputs are discarded. Set the * portal's holdSnapshot to the snapshot used (or a copy of it). */ PortalRunMulti(portal, isTopLevel, true, @@ -1518,7 +1518,7 @@ PortalRunMulti(Portal portal, * Increment command counter between queries, but not after the last * one. */ - if (lnext(stmtlist_item) != NULL) + if (lnext(portal->stmts, stmtlist_item) != NULL) CommandCounterIncrement(); /* diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 7cbbd477cc47..a1245f4ea99b 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1343,7 +1343,7 @@ ProcessUtilitySlow(ParseState *pstate, } /* Need CCI between commands */ - if (lnext(l) != NULL) + if (lnext(stmts, l) != NULL) CommandCounterIncrement(); } if (more_stmts) @@ -1440,7 +1440,7 @@ ProcessUtilitySlow(ParseState *pstate, } /* Need CCI between commands */ - if (lnext(l) != NULL) + if (lnext(stmts, l) != NULL) CommandCounterIncrement(); } diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 6522a3e50d97..f8d5dcd39ab7 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -141,7 +141,7 @@ static void array_insert_slice(ArrayType *destArray, ArrayType *origArray, int *st, int *endp, int typlen, bool typbyval, char typalign); static int array_cmp(FunctionCallInfo fcinfo); -static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes, +static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes, Oid elmtype, int dataoffset); static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, bool isnull, Oid elmtype, diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c index c92e9d5046ac..6515fc8ec695 100644 --- a/src/backend/utils/adt/cash.c +++ b/src/backend/utils/adt/cash.c @@ -1032,13 +1032,8 @@ Datum cash_numeric(PG_FUNCTION_ARGS) { Cash money = PG_GETARG_CASH(0); - Numeric result; + Datum result; int fpoint; - int64 scale; - int i; - Datum amount; - Datum numeric_scale; - Datum quotient; struct lconv *lconvert = PGLC_localeconv(); /* see comments about frac_digits in cash_in() */ @@ -1046,22 +1041,45 @@ cash_numeric(PG_FUNCTION_ARGS) if (fpoint < 0 || fpoint > 10) fpoint = 2; - /* compute required scale factor */ - scale = 1; - for (i = 0; i < fpoint; i++) - scale *= 10; + /* convert the integral money value to numeric */ + result = DirectFunctionCall1(int8_numeric, Int64GetDatum(money)); - /* form the result as money / scale */ - amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money)); - numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale)); - quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale); + /* scale appropriately, if needed */ + if (fpoint > 0) + { + int64 scale; + int i; + Datum numeric_scale; + Datum quotient; + + /* compute required scale factor */ + scale = 1; + for (i = 0; i < fpoint; i++) + scale *= 10; + numeric_scale = DirectFunctionCall1(int8_numeric, + Int64GetDatum(scale)); - /* forcibly round to exactly the intended number of digits */ - result = DatumGetNumeric(DirectFunctionCall2(numeric_round, - quotient, - Int32GetDatum(fpoint))); + /* + * Given integral inputs approaching INT64_MAX, select_div_scale() + * might choose a result scale of zero, causing loss of fractional + * digits in the quotient. We can ensure an exact result by setting + * the dscale of either input to be at least as large as the desired + * result scale. numeric_round() will do that for us. + */ + numeric_scale = DirectFunctionCall2(numeric_round, + numeric_scale, + Int32GetDatum(fpoint)); + + /* Now we can safely divide ... */ + quotient = DirectFunctionCall2(numeric_div, result, numeric_scale); + + /* ... and forcibly round to exactly the intended number of digits */ + result = DirectFunctionCall2(numeric_round, + quotient, + Int32GetDatum(fpoint)); + } - PG_RETURN_NUMERIC(result); + PG_RETURN_DATUM(result); } /* numeric_cash() diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index dadfcf250af1..4fb445ee174b 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -1896,7 +1896,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf, /* * Was this an "ISO time" with embedded field labels? An - * example is "h04m05s06" - thomas 2001-02-04 + * example is "h04mm05s06" - thomas 2001-02-04 */ if (ptype != 0) { @@ -4953,16 +4953,15 @@ pg_timezone_names(PG_FUNCTION_ARGS) continue; /* ignore if conversion fails */ /* - * Ignore zic's rather silly "Factory" time zone. The long string - * about "see zic manual page" is used in tzdata versions before - * 2016g; we can drop it someday when we're pretty sure no such data - * exists in the wild on platforms using --with-system-tzdata. In - * 2016g and later, the time zone abbreviation "-00" is used for - * "Factory" as well as some invalid cases, all of which we can - * reasonably omit from the pg_timezone_names view. + * IANA's rather silly "Factory" time zone used to emit ridiculously + * long "abbreviations" such as "Local time zone must be set--see zic + * manual page" or "Local time zone must be set--use tzsetup". While + * modern versions of tzdb emit the much saner "-00", it seems some + * benighted packagers are hacking the IANA data so that it continues + * to produce these strings. To prevent producing a weirdly wide + * abbrev column, reject ridiculously long abbreviations. */ - if (tzn && (strcmp(tzn, "-00") == 0 || - strcmp(tzn, "Local time zone must be set--see zic manual page") == 0)) + if (tzn && strlen(tzn) > 31) continue; /* Found a displayable zone */ diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 8d084e3b02bd..affe7b932754 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -282,8 +282,6 @@ static const char *const numth[] = {"st", "nd", "rd", "th", NULL}; #define ALL_UPPER 2 /* NAME */ #define ALL_LOWER 3 /* name */ -#define FULL_SIZ 0 - #define MAX_MONTH_LEN 9 #define MAX_MON_LEN 3 #define MAX_DAY_LEN 9 diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 373784fcc1ef..6558ea3937de 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -2348,6 +2348,17 @@ dist_pl(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(line_closept_point(NULL, line, pt)); } +/* + * Distance from a line to a point + */ +Datum +dist_lp(PG_FUNCTION_ARGS) +{ + LINE *line = PG_GETARG_LINE_P(0); + Point *pt = PG_GETARG_POINT_P(1); + + PG_RETURN_FLOAT8(line_closept_point(NULL, line, pt)); +} /* * Distance from a point to a lseg @@ -2362,13 +2373,20 @@ dist_ps(PG_FUNCTION_ARGS) } /* - * Distance from a point to a path + * Distance from a lseg to a point */ Datum -dist_ppath(PG_FUNCTION_ARGS) +dist_sp(PG_FUNCTION_ARGS) +{ + LSEG *lseg = PG_GETARG_LSEG_P(0); + Point *pt = PG_GETARG_POINT_P(1); + + PG_RETURN_FLOAT8(lseg_closept_point(NULL, lseg, pt)); +} + +static float8 +dist_ppath_internal(Point *pt, PATH *path) { - Point *pt = PG_GETARG_POINT_P(0); - PATH *path = PG_GETARG_PATH_P(1); float8 result = 0.0; /* keep compiler quiet */ bool have_min = false; float8 tmp; @@ -2403,7 +2421,31 @@ dist_ppath(PG_FUNCTION_ARGS) } } - PG_RETURN_FLOAT8(result); + return result; +} + +/* + * Distance from a point to a path + */ +Datum +dist_ppath(PG_FUNCTION_ARGS) +{ + Point *pt = PG_GETARG_POINT_P(0); + PATH *path = PG_GETARG_PATH_P(1); + + PG_RETURN_FLOAT8(dist_ppath_internal(pt, path)); +} + +/* + * Distance from a path to a point + */ +Datum +dist_pathp(PG_FUNCTION_ARGS) +{ + PATH *path = PG_GETARG_PATH_P(0); + Point *pt = PG_GETARG_POINT_P(1); + + PG_RETURN_FLOAT8(dist_ppath_internal(pt, path)); } /* @@ -2418,6 +2460,18 @@ dist_pb(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(box_closept_point(NULL, box, pt)); } +/* + * Distance from a box to a point + */ +Datum +dist_bp(PG_FUNCTION_ARGS) +{ + BOX *box = PG_GETARG_BOX_P(0); + Point *pt = PG_GETARG_POINT_P(1); + + PG_RETURN_FLOAT8(box_closept_point(NULL, box, pt)); +} + /* * Distance from a lseg to a line */ @@ -2430,6 +2484,18 @@ dist_sl(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(lseg_closept_line(NULL, lseg, line)); } +/* + * Distance from a line to a lseg + */ +Datum +dist_ls(PG_FUNCTION_ARGS) +{ + LINE *line = PG_GETARG_LINE_P(0); + LSEG *lseg = PG_GETARG_LSEG_P(1); + + PG_RETURN_FLOAT8(lseg_closept_line(NULL, lseg, line)); +} + /* * Distance from a lseg to a box */ @@ -2442,6 +2508,18 @@ dist_sb(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(box_closept_lseg(NULL, box, lseg)); } +/* + * Distance from a box to a lseg + */ +Datum +dist_bs(PG_FUNCTION_ARGS) +{ + BOX *box = PG_GETARG_BOX_P(0); + LSEG *lseg = PG_GETARG_LSEG_P(1); + + PG_RETURN_FLOAT8(box_closept_lseg(NULL, box, lseg)); +} + /* * Distance from a line to a box */ @@ -2462,13 +2540,27 @@ dist_lb(PG_FUNCTION_ARGS) } /* - * Distance from a circle to a polygon + * Distance from a box to a line */ Datum -dist_cpoly(PG_FUNCTION_ARGS) +dist_bl(PG_FUNCTION_ARGS) +{ +#ifdef NOT_USED + BOX *box = PG_GETARG_BOX_P(0); + LINE *line = PG_GETARG_LINE_P(1); +#endif + + /* need to think about this one for a while */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function \"dist_bl\" not implemented"))); + + PG_RETURN_NULL(); +} + +static float8 +dist_cpoly_internal(CIRCLE *circle, POLYGON *poly) { - CIRCLE *circle = PG_GETARG_CIRCLE_P(0); - POLYGON *poly = PG_GETARG_POLYGON_P(1); float8 result; /* calculate distance to center, and subtract radius */ @@ -2477,7 +2569,31 @@ dist_cpoly(PG_FUNCTION_ARGS) if (result < 0.0) result = 0.0; - PG_RETURN_FLOAT8(result); + return result; +} + +/* + * Distance from a circle to a polygon + */ +Datum +dist_cpoly(PG_FUNCTION_ARGS) +{ + CIRCLE *circle = PG_GETARG_CIRCLE_P(0); + POLYGON *poly = PG_GETARG_POLYGON_P(1); + + PG_RETURN_FLOAT8(dist_cpoly_internal(circle, poly)); +} + +/* + * Distance from a polygon to a circle + */ +Datum +dist_polyc(PG_FUNCTION_ARGS) +{ + POLYGON *poly = PG_GETARG_POLYGON_P(0); + CIRCLE *circle = PG_GETARG_CIRCLE_P(1); + + PG_RETURN_FLOAT8(dist_cpoly_internal(circle, poly)); } /* diff --git a/src/backend/utils/adt/inet_cidr_ntop.c b/src/backend/utils/adt/inet_cidr_ntop.c index 5fdc3ca2513d..3000b1735d05 100644 --- a/src/backend/utils/adt/inet_cidr_ntop.c +++ b/src/backend/utils/adt/inet_cidr_ntop.c @@ -146,7 +146,7 @@ inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) /* * static char * - * inet_cidr_ntop_ipv6(src, bits, fakebits, dst, size) + * inet_cidr_ntop_ipv6(src, bits, dst, size) * convert IPv6 network number from network to presentation format. * generates CIDR style result always. Picks the shortest representation * unless the IP is really IPv4. diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c index 3ff28af5b783..a39b36305330 100644 --- a/src/backend/utils/adt/jsonb_util.c +++ b/src/backend/utils/adt/jsonb_util.c @@ -510,7 +510,7 @@ fillJsonbValue(JsonbContainer *container, int index, * "raw scalar" pseudo array to append it - the actual scalar should be passed * next and it will be added as the only member of the array. * - * Values of type jvbBinary, which are rolled up arrays and objects, + * Values of type jbvBinary, which are rolled up arrays and objects, * are unpacked before being added to the result. */ JsonbValue * diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index 6bf4dcaec333..293d6da027cf 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -146,6 +146,7 @@ typedef struct JsonValueList typedef struct JsonValueListIterator { JsonbValue *value; + List *list; ListCell *next; } JsonValueListIterator; @@ -184,11 +185,9 @@ static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt, static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt, JsonPathItem *cur, JsonPathItem *next, JsonbValue *v, JsonValueList *found, bool copy); -static JsonPathExecResult executeItemOptUnwrapResult( - JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb, +static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb, bool unwrap, JsonValueList *found); -static JsonPathExecResult executeItemOptUnwrapResultNoThrow( - JsonPathExecContext *cxt, JsonPathItem *jsp, +static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb, bool unwrap, JsonValueList *found); static JsonPathBool executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext); @@ -2182,16 +2181,19 @@ JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it) if (jvl->singleton) { it->value = jvl->singleton; + it->list = NIL; it->next = NULL; } - else if (list_head(jvl->list) != NULL) + else if (jvl->list != NIL) { it->value = (JsonbValue *) linitial(jvl->list); - it->next = lnext(list_head(jvl->list)); + it->list = jvl->list; + it->next = list_second_cell(jvl->list); } else { it->value = NULL; + it->list = NIL; it->next = NULL; } } @@ -2207,7 +2209,7 @@ JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it) if (it->next) { it->value = lfirst(it->next); - it->next = lnext(it->next); + it->next = lnext(it->list, it->next); } else { diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y index e5f7aaa93d4f..47d132d5aa97 100644 --- a/src/backend/utils/adt/jsonpath_gram.y +++ b/src/backend/utils/adt/jsonpath_gram.y @@ -416,18 +416,18 @@ makeItemList(List *list) { JsonPathParseItem *head, *end; - ListCell *cell = list_head(list); + ListCell *cell; - head = end = (JsonPathParseItem *) lfirst(cell); + head = end = (JsonPathParseItem *) linitial(list); - if (!lnext(cell)) + if (list_length(list) == 1) return head; /* append items to the end of already existing list */ while (end->next) end = end->next; - for_each_cell(cell, lnext(cell)) + for_each_cell(cell, list, list_second_cell(list)) { JsonPathParseItem *c = (JsonPathParseItem *) lfirst(cell); diff --git a/src/backend/utils/adt/like_match.c b/src/backend/utils/adt/like_match.c index 9055a938132b..5b322559aa4c 100644 --- a/src/backend/utils/adt/like_match.c +++ b/src/backend/utils/adt/like_match.c @@ -27,7 +27,8 @@ /* * Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. * Rich $alz is now . - * Special thanks to Lars Mathiesen for the LABORT code. + * Special thanks to Lars Mathiesen for the + * LIKE_ABORT code. * * This code was shamelessly stolen from the "pql" code by myself and * slightly modified :) diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c index bb67e0149968..778d186f5b81 100644 --- a/src/backend/utils/adt/oid.c +++ b/src/backend/utils/adt/oid.c @@ -308,7 +308,7 @@ oidvectorsend(PG_FUNCTION_ARGS) } /* - * oidparse - get OID from IConst/FConst node + * oidparse - get OID from ICONST/FCONST node */ Oid oidparse(Node *node) diff --git a/src/backend/utils/adt/partitionfuncs.c b/src/backend/utils/adt/partitionfuncs.c index 714a0242a18a..e2e51563549a 100644 --- a/src/backend/utils/adt/partitionfuncs.c +++ b/src/backend/utils/adt/partitionfuncs.c @@ -67,14 +67,13 @@ pg_partition_tree(PG_FUNCTION_ARGS) #define PG_PARTITION_TREE_COLS 4 Oid rootrelid = PG_GETARG_OID(0); FuncCallContext *funcctx; - ListCell **next; + List *partitions; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcxt; TupleDesc tupdesc; - List *partitions; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); @@ -103,29 +102,27 @@ pg_partition_tree(PG_FUNCTION_ARGS) funcctx->tuple_desc = BlessTupleDesc(tupdesc); - /* allocate memory for user context */ - next = (ListCell **) palloc(sizeof(ListCell *)); - *next = list_head(partitions); - funcctx->user_fctx = (void *) next; + /* The only state we need is the partition list */ + funcctx->user_fctx = (void *) partitions; MemoryContextSwitchTo(oldcxt); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); - next = (ListCell **) funcctx->user_fctx; + partitions = (List *) funcctx->user_fctx; - if (*next != NULL) + if (funcctx->call_cntr < list_length(partitions)) { Datum result; Datum values[PG_PARTITION_TREE_COLS]; bool nulls[PG_PARTITION_TREE_COLS]; HeapTuple tuple; Oid parentid = InvalidOid; - Oid relid = lfirst_oid(*next); + Oid relid = list_nth_oid(partitions, funcctx->call_cntr); char relkind = get_rel_relkind(relid); int level = 0; - List *ancestors = get_partition_ancestors(lfirst_oid(*next)); + List *ancestors = get_partition_ancestors(relid); ListCell *lc; /* @@ -161,8 +158,6 @@ pg_partition_tree(PG_FUNCTION_ARGS) } values[3] = Int32GetDatum(level); - *next = lnext(*next); - tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); @@ -221,12 +216,11 @@ pg_partition_ancestors(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); FuncCallContext *funcctx; - ListCell **next; + List *ancestors; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcxt; - List *ancestors; funcctx = SRF_FIRSTCALL_INIT(); @@ -238,21 +232,19 @@ pg_partition_ancestors(PG_FUNCTION_ARGS) ancestors = get_partition_ancestors(relid); ancestors = lcons_oid(relid, ancestors); - next = (ListCell **) palloc(sizeof(ListCell *)); - *next = list_head(ancestors); - funcctx->user_fctx = (void *) next; + /* The only state we need is the ancestors list */ + funcctx->user_fctx = (void *) ancestors; MemoryContextSwitchTo(oldcxt); } funcctx = SRF_PERCALL_SETUP(); - next = (ListCell **) funcctx->user_fctx; + ancestors = (List *) funcctx->user_fctx; - if (*next != NULL) + if (funcctx->call_cntr < list_length(ancestors)) { - Oid relid = lfirst_oid(*next); + Oid relid = list_nth_oid(ancestors, funcctx->call_cntr); - *next = lnext(*next); SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid)); } diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c index 4c329a8d8fe8..b4c6c2309cf4 100644 --- a/src/backend/utils/adt/pg_lsn.c +++ b/src/backend/utils/adt/pg_lsn.c @@ -171,6 +171,24 @@ pg_lsn_ge(PG_FUNCTION_ARGS) PG_RETURN_BOOL(lsn1 >= lsn2); } +Datum +pg_lsn_larger(PG_FUNCTION_ARGS) +{ + XLogRecPtr lsn1 = PG_GETARG_LSN(0); + XLogRecPtr lsn2 = PG_GETARG_LSN(1); + + PG_RETURN_LSN((lsn1 > lsn2) ? lsn1 : lsn2); +} + +Datum +pg_lsn_smaller(PG_FUNCTION_ARGS) +{ + XLogRecPtr lsn1 = PG_GETARG_LSN(0); + XLogRecPtr lsn2 = PG_GETARG_LSN(1); + + PG_RETURN_LSN((lsn1 < lsn2) ? lsn1 : lsn2); +} + /* btree index opclass support */ Datum pg_lsn_cmp(PG_FUNCTION_ARGS) diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c index 544433cb0405..b872df81413a 100644 --- a/src/backend/utils/adt/rangetypes.c +++ b/src/backend/utils/adt/rangetypes.c @@ -1431,13 +1431,15 @@ daterange_canonical(PG_FUNCTION_ARGS) if (empty) PG_RETURN_RANGE_P(r); - if (!lower.infinite && !lower.inclusive) + if (!lower.infinite && !DATE_NOT_FINITE(DatumGetDateADT(lower.val)) && + !lower.inclusive) { lower.val = DirectFunctionCall2(date_pli, lower.val, Int32GetDatum(1)); lower.inclusive = true; } - if (!upper.infinite && upper.inclusive) + if (!upper.infinite && !DATE_NOT_FINITE(DatumGetDateADT(upper.val)) && + upper.inclusive) { upper.val = DirectFunctionCall2(date_pli, upper.val, Int32GetDatum(1)); upper.inclusive = false; diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 44a6eef5bbfd..8c895459c375 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -927,7 +927,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) queryoids[i] = pk_type; queryoids[j] = pk_type; } - appendStringInfoString(&querybuf, qualbuf.data); + appendBinaryStringInfo(&querybuf, qualbuf.data, qualbuf.len); /* Prepare and save the plan */ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids, @@ -1106,7 +1106,7 @@ ri_set(TriggerData *trigdata, bool is_set_null) qualsep = "AND"; queryoids[i] = pk_type; } - appendStringInfoString(&querybuf, qualbuf.data); + appendBinaryStringInfo(&querybuf, qualbuf.data, qualbuf.len); /* Prepare and save the plan */ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids, diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index eb1b87358d97..38d6d4727d54 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -478,7 +478,7 @@ static char *flatten_reloptions(Oid relid); /* ---------- - * get_ruledef - Do it all and return a text + * pg_get_ruledef - Do it all and return a text * that could be used as a statement * to recreate the rule * ---------- @@ -598,7 +598,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags) /* ---------- - * get_viewdef - Mainly the same thing, but we + * pg_get_viewdef - Mainly the same thing, but we * only return the SELECT part of a view * ---------- */ @@ -793,7 +793,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn) } /* ---------- - * get_triggerdef - Get the definition of a trigger + * pg_get_triggerdef - Get the definition of a trigger * ---------- */ Datum @@ -1087,7 +1087,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) } /* ---------- - * get_indexdef - Get the definition of an index + * pg_get_indexdef - Get the definition of an index * * In the extended version, there is a colno argument as well as pretty bool. * if colno == 0, we want a complete index definition. @@ -1353,7 +1353,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, if (indexpr_item == NULL) elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(indexprs, indexpr_item); /* Deparse */ str = deparse_expression_pretty(indexkey, context, false, false, prettyFlags, 0); @@ -1727,7 +1727,7 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags, { case PARTITION_STRATEGY_HASH: if (!attrsOnly) - appendStringInfo(&buf, "HASH"); + appendStringInfoString(&buf, "HASH"); break; case PARTITION_STRATEGY_LIST: if (!attrsOnly) @@ -1774,7 +1774,7 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags, if (partexpr_item == NULL) elog(ERROR, "too few entries in partexprs list"); partkey = (Node *) lfirst(partexpr_item); - partexpr_item = lnext(partexpr_item); + partexpr_item = lnext(partexprs, partexpr_item); /* Deparse */ str = deparse_expression_pretty(partkey, context, false, false, @@ -2353,7 +2353,7 @@ decompile_column_index_array(Datum column_index_array, Oid relId, /* ---------- - * get_expr - Decompile an expression tree + * pg_get_expr - Decompile an expression tree * * Input: an expression tree in nodeToString form, and a relation OID * @@ -2451,7 +2451,7 @@ pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags) /* ---------- - * get_userbyid - Get a user name by roleid and + * pg_get_userbyid - Get a user name by roleid and * fallback to 'unknown (OID=n)' * ---------- */ @@ -2775,7 +2775,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS) char *curname = (char *) lfirst(lc); simple_quote_literal(&buf, curname); - if (lnext(lc)) + if (lnext(namelist, lc)) appendStringInfoString(&buf, ", "); } } @@ -2815,9 +2815,9 @@ pg_get_functiondef(PG_FUNCTION_ARGS) appendStringInfoChar(&dq, 'x'); appendStringInfoChar(&dq, '$'); - appendStringInfoString(&buf, dq.data); + appendBinaryStringInfo(&buf, dq.data, dq.len); appendStringInfoString(&buf, prosrc); - appendStringInfoString(&buf, dq.data); + appendBinaryStringInfo(&buf, dq.data, dq.len); appendStringInfoChar(&buf, '\n'); @@ -2941,7 +2941,7 @@ print_function_rettype(StringInfo buf, HeapTuple proctup) appendStringInfoString(&rbuf, format_type_be(proc->prorettype)); } - appendStringInfoString(buf, rbuf.data); + appendBinaryStringInfo(buf, rbuf.data, rbuf.len); } /* @@ -2964,6 +2964,7 @@ print_function_arguments(StringInfo buf, HeapTuple proctup, int argsprinted; int inputargno; int nlackdefaults; + List *argdefaults = NIL; ListCell *nextargdefault = NULL; int i; @@ -2982,7 +2983,6 @@ print_function_arguments(StringInfo buf, HeapTuple proctup, if (!isnull) { char *str; - List *argdefaults; str = TextDatumGetCString(proargdefaults); argdefaults = castNode(List, stringToNode(str)); @@ -3072,7 +3072,7 @@ print_function_arguments(StringInfo buf, HeapTuple proctup, Assert(nextargdefault != NULL); expr = (Node *) lfirst(nextargdefault); - nextargdefault = lnext(nextargdefault); + nextargdefault = lnext(argdefaults, nextargdefault); appendStringInfo(buf, " DEFAULT %s", deparse_expression(expr, NIL, false, false)); @@ -4791,16 +4791,14 @@ push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, deparse_namespace *save_dpns) { PlanState *ps = (PlanState *) lfirst(ancestor_cell); - List *ancestors; /* Save state for restoration later */ *save_dpns = *dpns; /* Build a new ancestor list with just this node's ancestors */ - ancestors = NIL; - while ((ancestor_cell = lnext(ancestor_cell)) != NULL) - ancestors = lappend(ancestors, lfirst(ancestor_cell)); - dpns->ancestors = ancestors; + dpns->ancestors = + list_copy_tail(dpns->ancestors, + list_cell_number(dpns->ancestors, ancestor_cell) + 1); /* Set attention on selected ancestor */ set_deparse_planstate(dpns, ps); @@ -5777,7 +5775,7 @@ get_target_list(List *targetList, deparse_context *context, } /* Add the new field */ - appendStringInfoString(buf, targetbuf.data); + appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len); } /* clean up */ @@ -6569,7 +6567,7 @@ get_update_query_targetlist_def(Query *query, List *targetList, ((Param *) expr)->paramkind == PARAM_MULTIEXPR) { cur_ma_sublink = (SubLink *) lfirst(next_ma_cell); - next_ma_cell = lnext(next_ma_cell); + next_ma_cell = lnext(ma_sublinks, next_ma_cell); remaining_ma_columns = count_nonjunk_tlist_entries( ((Query *) cur_ma_sublink->subselect)->targetList); Assert(((Param *) expr)->paramid == @@ -6871,8 +6869,8 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) /* * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This - * routine is actually a callback for get_special_varno, which handles finding - * the correct TargetEntry. We get the expression contained in that + * routine is actually a callback for resolve_special_varno, which handles + * finding the correct TargetEntry. We get the expression contained in that * TargetEntry and just need to deparse it, a job we can throw back on * get_rule_expr. */ @@ -8117,7 +8115,7 @@ get_rule_expr(Node *node, deparse_context *context, { BoolExpr *expr = (BoolExpr *) node; Node *first_arg = linitial(expr->args); - ListCell *arg = lnext(list_head(expr->args)); + ListCell *arg = list_second_cell(expr->args); switch (expr->boolop) { @@ -8131,7 +8129,7 @@ get_rule_expr(Node *node, deparse_context *context, appendStringInfoString(buf, " AND "); get_rule_expr_paren((Node *) lfirst(arg), context, false, node); - arg = lnext(arg); + arg = lnext(expr->args, arg); } if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); @@ -8147,7 +8145,7 @@ get_rule_expr(Node *node, deparse_context *context, appendStringInfoString(buf, " OR "); get_rule_expr_paren((Node *) lfirst(arg), context, false, node); - arg = lnext(arg); + arg = lnext(expr->args, arg); } if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); @@ -8206,7 +8204,7 @@ get_rule_expr(Node *node, deparse_context *context, appendStringInfo(buf, "hashed %s", splan->plan_name); else appendStringInfoString(buf, splan->plan_name); - if (lnext(lc)) + if (lnext(asplan->subplans, lc)) appendStringInfoString(buf, " or "); } appendStringInfoChar(buf, ')'); @@ -9531,7 +9529,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context, { if (nargs++ > 0) appendStringInfoString(buf, ", "); - if (use_variadic && lnext(l) == NULL) + if (use_variadic && lnext(expr->args, l) == NULL) appendStringInfoString(buf, "VARIADIC "); get_rule_expr((Node *) lfirst(l), context, true); } @@ -10372,7 +10370,7 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context) } /* Add the new item */ - appendStringInfoString(buf, itembuf.data); + appendBinaryStringInfo(buf, itembuf.data, itembuf.len); /* clean up */ pfree(itembuf.data); @@ -11054,7 +11052,7 @@ printSubscripts(SubscriptingRef *sbsref, deparse_context *context) /* If subexpression is NULL, get_rule_expr prints nothing */ get_rule_expr((Node *) lfirst(lowlist_item), context, false); appendStringInfoChar(buf, ':'); - lowlist_item = lnext(lowlist_item); + lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item); } /* If subexpression is NULL, get_rule_expr prints nothing */ get_rule_expr((Node *) lfirst(uplist_item), context, false); @@ -11800,7 +11798,7 @@ pg_get_table_distributedby(PG_FUNCTION_ARGS) } /* - * get_one_range_partition_bound_string + * get_range_partbound_string * A C string representation of one range partition bound */ char * diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 15ad1fcac08b..7b3e5f917667 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -107,14 +107,13 @@ #include "access/gin.h" #include "access/table.h" #include "access/tableam.h" -#include "catalog/index.h" +#include "access/visibilitymap.h" #include "catalog/pg_am.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" #include "catalog/pg_statistic.h" #include "catalog/pg_statistic_ext.h" -#include "executor/executor.h" #include "executor/nodeAgg.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -128,6 +127,7 @@ #include "parser/parse_clause.h" #include "parser/parsetree.h" #include "statistics/statistics.h" +#include "storage/bufmgr.h" #include "utils/builtins.h" #include "utils/date.h" #include "utils/datum.h" @@ -135,6 +135,7 @@ #include "utils/fmgroids.h" #include "utils/index_selfuncs.h" #include "utils/lsyscache.h" +#include "utils/memutils.h" #include "utils/pg_locale.h" #include "utils/rel.h" #include "utils/selfuncs.h" @@ -202,6 +203,15 @@ static bool get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, Datum *min, Datum *max); +static bool get_actual_variable_endpoint(Relation heapRel, + Relation indexRel, + ScanDirection indexscandir, + ScanKey scankeys, + int16 typLen, + bool typByVal, + TupleTableSlot *tableslot, + MemoryContext outercontext, + Datum *endpointDatum); static RelOptInfo *find_join_input_rel(PlannerInfo *root, Relids relids); static void try_fetch_rel_stats(RangeTblEntry *rte, const char *attname, @@ -2964,15 +2974,10 @@ add_unique_group_var(PlannerInfo *root, List *varinfos, ndistinct = get_variable_numdistinct(vardata, &isdefault); - /* cannot use foreach here because of possible list_delete */ - lc = list_head(varinfos); - while (lc) + foreach(lc, varinfos) { varinfo = (GroupVarInfo *) lfirst(lc); - /* must advance lc before list_delete possibly pfree's it */ - lc = lnext(lc); - /* Drop exact duplicates */ if (equal(var, varinfo->var)) return varinfos; @@ -2992,7 +2997,7 @@ add_unique_group_var(PlannerInfo *root, List *varinfos, else { /* Delete the older item */ - varinfos = list_delete_ptr(varinfos, varinfo); + varinfos = foreach_delete_current(varinfos, lc); } } } @@ -3233,20 +3238,20 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, * Split the list of varinfos in two - one for the current rel, one * for remaining Vars on other rels. */ - relvarinfos = lcons(varinfo1, relvarinfos); - for_each_cell(l, lnext(list_head(varinfos))) + relvarinfos = lappend(relvarinfos, varinfo1); + for_each_cell(l, varinfos, list_second_cell(varinfos)) { GroupVarInfo *varinfo2 = (GroupVarInfo *) lfirst(l); if (varinfo2->rel == varinfo1->rel) { /* varinfos on current rel */ - relvarinfos = lcons(varinfo2, relvarinfos); + relvarinfos = lappend(relvarinfos, varinfo2); } else { /* not time to process varinfo2 yet */ - newvarinfos = lcons(varinfo2, newvarinfos); + newvarinfos = lappend(newvarinfos, varinfo2); } } @@ -4778,7 +4783,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, if (vardata->statsTuple) break; } - indexpr_item = lnext(indexpr_item); + indexpr_item = lnext(index->indexprs, indexpr_item); } } if (HeapTupleIsValid(getStatsTuple(vardata))) @@ -5425,30 +5430,23 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, } /* - * Found a suitable index to extract data from. We'll need an EState - * and a bunch of other infrastructure. + * Found a suitable index to extract data from. Set up some data that + * can be used by both invocations of get_actual_variable_endpoint. */ { - EState *estate; - ExprContext *econtext; MemoryContext tmpcontext; MemoryContext oldcontext; Relation heapRel; Relation indexRel; - IndexInfo *indexInfo; TupleTableSlot *slot; int16 typLen; bool typByVal; ScanKeyData scankeys[1]; - IndexScanDesc index_scan; - Datum values[INDEX_MAX_KEYS]; - bool isnull[INDEX_MAX_KEYS]; - SnapshotData SnapshotNonVacuumable; - - estate = CreateExecutorState(); - econtext = GetPerTupleExprContext(estate); - /* Make sure any cruft is generated in the econtext's memory */ - tmpcontext = econtext->ecxt_per_tuple_memory; + + /* Make sure any cruft gets recycled when we're done */ + tmpcontext = AllocSetContextCreate(CurrentMemoryContext, + "get_actual_variable_range workspace", + ALLOCSET_DEFAULT_SIZES); oldcontext = MemoryContextSwitchTo(tmpcontext); /* @@ -5458,14 +5456,9 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, heapRel = table_open(rte->relid, NoLock); indexRel = index_open(index->indexoid, NoLock); - /* extract index key information from the index's pg_index info */ - indexInfo = BuildIndexInfo(indexRel); - - /* some other stuff */ + /* build some stuff needed for indexscan execution */ slot = table_slot_create(heapRel, NULL); - econtext->ecxt_scantuple = slot; get_typlenbyval(vardata->atttype, &typLen, &typByVal); - InitNonVacuumableSnapshot(SnapshotNonVacuumable, RecentGlobalXmin); /* set up an IS NOT NULL scan key so that we ignore nulls */ ScanKeyEntryInitialize(&scankeys[0], @@ -5477,94 +5470,38 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, InvalidOid, /* no reg proc for this */ (Datum) 0); /* constant */ - have_data = true; - /* If min is requested ... */ if (min) { - /* - * In principle, we should scan the index with our current - * active snapshot, which is the best approximation we've got - * to what the query will see when executed. But that won't - * be exact if a new snap is taken before running the query, - * and it can be very expensive if a lot of recently-dead or - * uncommitted rows exist at the beginning or end of the index - * (because we'll laboriously fetch each one and reject it). - * Instead, we use SnapshotNonVacuumable. That will accept - * recently-dead and uncommitted rows as well as normal - * visible rows. On the other hand, it will reject known-dead - * rows, and thus not give a bogus answer when the extreme - * value has been deleted (unless the deletion was quite - * recent); that case motivates not using SnapshotAny here. - * - * A crucial point here is that SnapshotNonVacuumable, with - * RecentGlobalXmin as horizon, yields the inverse of the - * condition that the indexscan will use to decide that index - * entries are killable (see heap_hot_search_buffer()). - * Therefore, if the snapshot rejects a tuple and we have to - * continue scanning past it, we know that the indexscan will - * mark that index entry killed. That means that the next - * get_actual_variable_range() call will not have to visit - * that heap entry. In this way we avoid repetitive work when - * this function is used a lot during planning. - */ - index_scan = index_beginscan(heapRel, indexRel, - &SnapshotNonVacuumable, - 1, 0); - index_rescan(index_scan, scankeys, 1, NULL, 0); - - /* Fetch first tuple in sortop's direction */ - if (index_getnext_slot(index_scan, indexscandir, slot)) - { - /* Extract the index column values from the slot */ - FormIndexDatum(indexInfo, slot, estate, - values, isnull); - - /* Shouldn't have got a null, but be careful */ - if (isnull[0]) - elog(ERROR, "found unexpected null value in index \"%s\"", - RelationGetRelationName(indexRel)); - - /* Copy the index column value out to caller's context */ - MemoryContextSwitchTo(oldcontext); - *min = datumCopy(values[0], typByVal, typLen); - MemoryContextSwitchTo(tmpcontext); - } - else - have_data = false; - - index_endscan(index_scan); + have_data = get_actual_variable_endpoint(heapRel, + indexRel, + indexscandir, + scankeys, + typLen, + typByVal, + slot, + oldcontext, + min); + } + else + { + /* If min not requested, assume index is nonempty */ + have_data = true; } /* If max is requested, and we didn't find the index is empty */ if (max && have_data) { - index_scan = index_beginscan(heapRel, indexRel, - &SnapshotNonVacuumable, - 1, 0); - index_rescan(index_scan, scankeys, 1, NULL, 0); - - /* Fetch first tuple in reverse direction */ - if (index_getnext_slot(index_scan, -indexscandir, slot)) - { - /* Extract the index column values from the slot */ - FormIndexDatum(indexInfo, slot, estate, - values, isnull); - - /* Shouldn't have got a null, but be careful */ - if (isnull[0]) - elog(ERROR, "found unexpected null value in index \"%s\"", - RelationGetRelationName(indexRel)); - - /* Copy the index column value out to caller's context */ - MemoryContextSwitchTo(oldcontext); - *max = datumCopy(values[0], typByVal, typLen); - MemoryContextSwitchTo(tmpcontext); - } - else - have_data = false; - - index_endscan(index_scan); + /* scan in the opposite direction; all else is the same */ + have_data = get_actual_variable_endpoint(heapRel, + indexRel, + -indexscandir, + scankeys, + typLen, + typByVal, + slot, + oldcontext, + max); } /* Clean everything up */ @@ -5574,7 +5511,7 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, table_close(heapRel, NoLock); MemoryContextSwitchTo(oldcontext); - FreeExecutorState(estate); + MemoryContextDelete(tmpcontext); /* And we're done */ break; @@ -5584,6 +5521,139 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, return have_data; } +/* + * Get one endpoint datum (min or max depending on indexscandir) from the + * specified index. Return true if successful, false if index is empty. + * On success, endpoint value is stored to *endpointDatum (and copied into + * outercontext). + * + * scankeys is a 1-element scankey array set up to reject nulls. + * typLen/typByVal describe the datatype of the index's first column. + * tableslot is a slot suitable to hold table tuples, in case we need + * to probe the heap. + * (We could compute these values locally, but that would mean computing them + * twice when get_actual_variable_range needs both the min and the max.) + */ +static bool +get_actual_variable_endpoint(Relation heapRel, + Relation indexRel, + ScanDirection indexscandir, + ScanKey scankeys, + int16 typLen, + bool typByVal, + TupleTableSlot *tableslot, + MemoryContext outercontext, + Datum *endpointDatum) +{ + bool have_data = false; + SnapshotData SnapshotNonVacuumable; + IndexScanDesc index_scan; + Buffer vmbuffer = InvalidBuffer; + ItemPointer tid; + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + MemoryContext oldcontext; + + /* + * We use the index-only-scan machinery for this. With mostly-static + * tables that's a win because it avoids a heap visit. It's also a win + * for dynamic data, but the reason is less obvious; read on for details. + * + * In principle, we should scan the index with our current active + * snapshot, which is the best approximation we've got to what the query + * will see when executed. But that won't be exact if a new snap is taken + * before running the query, and it can be very expensive if a lot of + * recently-dead or uncommitted rows exist at the beginning or end of the + * index (because we'll laboriously fetch each one and reject it). + * Instead, we use SnapshotNonVacuumable. That will accept recently-dead + * and uncommitted rows as well as normal visible rows. On the other + * hand, it will reject known-dead rows, and thus not give a bogus answer + * when the extreme value has been deleted (unless the deletion was quite + * recent); that case motivates not using SnapshotAny here. + * + * A crucial point here is that SnapshotNonVacuumable, with + * RecentGlobalXmin as horizon, yields the inverse of the condition that + * the indexscan will use to decide that index entries are killable (see + * heap_hot_search_buffer()). Therefore, if the snapshot rejects a tuple + * (or more precisely, all tuples of a HOT chain) and we have to continue + * scanning past it, we know that the indexscan will mark that index entry + * killed. That means that the next get_actual_variable_endpoint() call + * will not have to re-consider that index entry. In this way we avoid + * repetitive work when this function is used a lot during planning. + * + * But using SnapshotNonVacuumable creates a hazard of its own. In a + * recently-created index, some index entries may point at "broken" HOT + * chains in which not all the tuple versions contain data matching the + * index entry. The live tuple version(s) certainly do match the index, + * but SnapshotNonVacuumable can accept recently-dead tuple versions that + * don't match. Hence, if we took data from the selected heap tuple, we + * might get a bogus answer that's not close to the index extremal value, + * or could even be NULL. We avoid this hazard because we take the data + * from the index entry not the heap. + */ + InitNonVacuumableSnapshot(SnapshotNonVacuumable, RecentGlobalXmin); + + index_scan = index_beginscan(heapRel, indexRel, + &SnapshotNonVacuumable, + 1, 0); + /* Set it up for index-only scan */ + index_scan->xs_want_itup = true; + index_rescan(index_scan, scankeys, 1, NULL, 0); + + /* Fetch first/next tuple in specified direction */ + while ((tid = index_getnext_tid(index_scan, indexscandir)) != NULL) + { + if (!VM_ALL_VISIBLE(heapRel, + ItemPointerGetBlockNumber(tid), + &vmbuffer)) + { + /* Rats, we have to visit the heap to check visibility */ + if (!index_fetch_heap(index_scan, tableslot)) + continue; /* no visible tuple, try next index entry */ + + /* We don't actually need the heap tuple for anything */ + ExecClearTuple(tableslot); + + /* + * We don't care whether there's more than one visible tuple in + * the HOT chain; if any are visible, that's good enough. + */ + } + + /* + * We expect that btree will return data in IndexTuple not HeapTuple + * format. It's not lossy either. + */ + if (!index_scan->xs_itup) + elog(ERROR, "no data returned for index-only scan"); + if (index_scan->xs_recheck) + elog(ERROR, "unexpected recheck indication from btree"); + + /* OK to deconstruct the index tuple */ + index_deform_tuple(index_scan->xs_itup, + index_scan->xs_itupdesc, + values, isnull); + + /* Shouldn't have got a null, but be careful */ + if (isnull[0]) + elog(ERROR, "found unexpected null value in index \"%s\"", + RelationGetRelationName(indexRel)); + + /* Copy the index column value out to caller's context */ + oldcontext = MemoryContextSwitchTo(outercontext); + *endpointDatum = datumCopy(values[0], typByVal, typLen); + MemoryContextSwitchTo(oldcontext); + have_data = true; + break; + } + + if (vmbuffer != InvalidBuffer) + ReleaseBuffer(vmbuffer); + index_endscan(index_scan); + + return have_data; +} + /* * find_join_input_rel * Look up the input relation for a join. diff --git a/src/backend/utils/adt/tsrank.c b/src/backend/utils/adt/tsrank.c index 55a96498798a..e28859d8e1c0 100644 --- a/src/backend/utils/adt/tsrank.c +++ b/src/backend/utils/adt/tsrank.c @@ -738,7 +738,7 @@ get_docrep(TSVector txt, QueryRepresentation *qr, int *doclen) doc = (DocRepresentation *) palloc(sizeof(DocRepresentation) * len); /* - * Iterate through query to make DocRepresentaion for words and it's + * Iterate through query to make DocRepresentation for words and it's * entries satisfied by query */ for (i = 0; i < qr->query->size; i++) diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c index 4f7bafd142e4..696a7fdf9146 100644 --- a/src/backend/utils/adt/tsvector_op.c +++ b/src/backend/utils/adt/tsvector_op.c @@ -1152,7 +1152,7 @@ tsvector_concat(PG_FUNCTION_ARGS) /* * Compare two strings by tsvector rules. * - * if isPrefix = true then it returns zero value iff b has prefix a + * if prefix = true then it returns zero value iff b has prefix a */ int32 tsCompareString(char *a, int lena, char *b, int lenb, bool prefix) diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c index 4d3b2fcbc42f..dc34d4882178 100644 --- a/src/backend/utils/adt/uuid.c +++ b/src/backend/utils/adt/uuid.c @@ -416,3 +416,23 @@ uuid_hash_extended(PG_FUNCTION_ARGS) return hash_any_extended(key->data, UUID_LEN, PG_GETARG_INT64(1)); } + +Datum +gen_random_uuid(PG_FUNCTION_ARGS) +{ + pg_uuid_t *uuid = palloc(UUID_LEN); + + if (!pg_strong_random(uuid, UUID_LEN)) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate random values"))); + + /* + * Set magic numbers for a "version 4" (pseudorandom) UUID, see + * http://tools.ietf.org/html/rfc4122#section-4.4 + */ + uuid->data[6] = (uuid->data[6] & 0x0f) | 0x40; /* time_hi_and_version */ + uuid->data[8] = (uuid->data[8] & 0x3f) | 0x80; /* clock_seq_hi_and_reserved */ + + PG_RETURN_UUID_P(uuid); +} diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index fb472b77dbbc..87e8e4b10e59 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -561,7 +561,7 @@ xmlconcat(List *args) 0, global_standalone); - appendStringInfoString(&buf2, buf.data); + appendBinaryStringInfo(&buf2, buf.data, buf.len); buf = buf2; } @@ -1891,7 +1891,8 @@ xml_errorHandler(void *data, xmlErrorPtr error) if (xmlerrcxt->strictness == PG_XML_STRICTNESS_LEGACY) { appendStringInfoLineSeparator(&xmlerrcxt->err_buf); - appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data); + appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf->data, + errorBuf->len); pfree(errorBuf->data); pfree(errorBuf); @@ -1909,7 +1910,8 @@ xml_errorHandler(void *data, xmlErrorPtr error) if (level >= XML_ERR_ERROR) { appendStringInfoLineSeparator(&xmlerrcxt->err_buf); - appendStringInfoString(&xmlerrcxt->err_buf, errorBuf->data); + appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf->data, + errorBuf->len); xmlerrcxt->err_occurred = true; } @@ -2886,7 +2888,7 @@ schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls, subres = table_to_xml_internal(relid, NULL, nulls, tableforest, targetns, false); - appendStringInfoString(result, subres->data); + appendBinaryStringInfo(result, subres->data, subres->len); appendStringInfoChar(result, '\n'); } @@ -3061,7 +3063,7 @@ database_to_xml_internal(const char *xmlschema, bool nulls, subres = schema_to_xml_internal(nspid, NULL, nulls, tableforest, targetns, false); - appendStringInfoString(result, subres->data); + appendBinaryStringInfo(result, subres->data, subres->len); appendStringInfoChar(result, '\n'); } diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c index 29b4a76e1329..342ab4dbaa62 100644 --- a/src/backend/utils/cache/partcache.c +++ b/src/backend/utils/cache/partcache.c @@ -222,7 +222,7 @@ RelationBuildPartitionKey(Relation relation) key->parttypmod[i] = exprTypmod(lfirst(partexprs_item)); key->parttypcoll[i] = exprCollation(lfirst(partexprs_item)); - partexprs_item = lnext(partexprs_item); + partexprs_item = lnext(key->partexprs, partexprs_item); } get_typlenbyvalalign(key->parttypid[i], &key->parttyplen[i], diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index c72129fa8861..bf6e64ea1cb4 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -298,7 +298,6 @@ static TupleDesc GetPgIndexDescriptor(void); static void AttrDefaultFetch(Relation relation); static void CheckConstraintFetch(Relation relation); static int CheckConstraintCmp(const void *a, const void *b); -static List *insert_ordered_oid(List *list, Oid datum); static void InitIndexAmRoutine(Relation relation); static void IndexSupportInitialize(oidvector *indclass, RegProcedure *indexSupport, @@ -2424,6 +2423,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc) pfree(relation->rd_options); if (relation->rd_indextuple) pfree(relation->rd_indextuple); + if (relation->rd_amcache) + pfree(relation->rd_amcache); + if (relation->rd_fdwroutine) + pfree(relation->rd_fdwroutine); if (relation->rd_indexcxt) MemoryContextDelete(relation->rd_indexcxt); if (relation->rd_rulescxt) @@ -2485,6 +2488,11 @@ RelationClearRelation(Relation relation, bool rebuild) */ RelationCloseSmgr(relation); + /* Free AM cached data, if any */ + if (relation->rd_amcache) + pfree(relation->rd_amcache); + relation->rd_amcache = NULL; + /* * Treat nailed-in system relations separately, they always need to be * accessible, so we can't blow them away. @@ -4577,8 +4585,8 @@ RelationGetIndexList(Relation relation) if (!index->indislive) continue; - /* Add index's OID to result list in the proper order */ - result = insert_ordered_oid(result, index->indexrelid); + /* add index's OID to result list */ + result = lappend_oid(result, index->indexrelid); /* * Invalid, non-unique, non-immediate or predicate indexes aren't @@ -4603,6 +4611,9 @@ RelationGetIndexList(Relation relation) table_close(indrel, AccessShareLock); + /* Sort the result list into OID order, per API spec. */ + list_sort(result, list_oid_cmp); + /* Now save a copy of the completed list in the relcache entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); oldlist = relation->rd_indexlist; @@ -4684,13 +4695,16 @@ RelationGetStatExtList(Relation relation) { Oid oid = ((Form_pg_statistic_ext) GETSTRUCT(htup))->oid; - result = insert_ordered_oid(result, oid); + result = lappend_oid(result, oid); } systable_endscan(indscan); table_close(indrel, AccessShareLock); + /* Sort the result list into OID order, per API spec. */ + list_sort(result, list_oid_cmp); + /* Now save a copy of the completed list in the relcache entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); oldlist = relation->rd_statlist; @@ -4705,39 +4719,6 @@ RelationGetStatExtList(Relation relation) return result; } -/* - * insert_ordered_oid - * Insert a new Oid into a sorted list of Oids, preserving ordering - * - * Building the ordered list this way is O(N^2), but with a pretty small - * constant, so for the number of entries we expect it will probably be - * faster than trying to apply qsort(). Most tables don't have very many - * indexes... - */ -static List * -insert_ordered_oid(List *list, Oid datum) -{ - ListCell *prev; - - /* Does the datum belong at the front? */ - if (list == NIL || datum < linitial_oid(list)) - return lcons_oid(datum, list); - /* No, so find the entry it belongs after */ - prev = list_head(list); - for (;;) - { - ListCell *curr = lnext(prev); - - if (curr == NULL || datum < lfirst_oid(curr)) - break; /* it belongs after 'prev', before 'curr' */ - - prev = curr; - } - /* Insert datum into list after 'prev' */ - lappend_cell_oid(list, prev, datum); - return list; -} - /* * RelationGetPrimaryKeyIndex -- get OID of the relation's primary key index * diff --git a/src/backend/utils/cache/relmapper.c b/src/backend/utils/cache/relmapper.c index 4ce87e6f9fe6..5cbf76d6206f 100644 --- a/src/backend/utils/cache/relmapper.c +++ b/src/backend/utils/cache/relmapper.c @@ -754,7 +754,7 @@ load_relmap_file(bool shared) } pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(FATAL, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", @@ -893,7 +893,7 @@ write_relmap_file(bool shared, RelMapFile *newmap, mapfilename))); pgstat_report_wait_end(); - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 0f6804b590be..4cb7b5352f91 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -219,7 +219,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, /* * For an ordinary builtin function, we should never get here - * because the isbuiltin() search above will have succeeded. + * because the fmgr_isbuiltin() search above will have succeeded. * However, if the user has done a CREATE FUNCTION to create an * alias for a builtin function, we can end up here. In that case * we have to look up the function by name. The name of the diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 3199ec8897af..7adf806d45f3 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -1361,10 +1361,10 @@ process_startup_options(Port *port, bool am_superuser) char *value; name = lfirst(gucopts); - gucopts = lnext(gucopts); + gucopts = lnext(port->guc_options, gucopts); value = lfirst(gucopts); - gucopts = lnext(gucopts); + gucopts = lnext(port->guc_options, gucopts); SetConfigOption(name, value, gucctx, PGC_S_CLIENT); } diff --git a/src/backend/utils/mb/conv.c b/src/backend/utils/mb/conv.c index f2b51acffe2b..3ecc92b0a6a2 100644 --- a/src/backend/utils/mb/conv.c +++ b/src/backend/utils/mb/conv.c @@ -132,51 +132,6 @@ mic2latin(const unsigned char *mic, unsigned char *p, int len, } -/* - * ASCII ---> MIC - * - * While ordinarily SQL_ASCII encoding is forgiving of high-bit-set - * characters, here we must take a hard line because we don't know - * the appropriate MIC equivalent. - */ -void -pg_ascii2mic(const unsigned char *l, unsigned char *p, int len) -{ - int c1; - - while (len > 0) - { - c1 = *l; - if (c1 == 0 || IS_HIGHBIT_SET(c1)) - report_invalid_encoding(PG_SQL_ASCII, (const char *) l, len); - *p++ = c1; - l++; - len--; - } - *p = '\0'; -} - -/* - * MIC ---> ASCII - */ -void -pg_mic2ascii(const unsigned char *mic, unsigned char *p, int len) -{ - int c1; - - while (len > 0) - { - c1 = *mic; - if (c1 == 0 || IS_HIGHBIT_SET(c1)) - report_untranslatable_char(PG_MULE_INTERNAL, PG_SQL_ASCII, - (const char *) mic, len); - *p++ = c1; - mic++; - len--; - } - *p = '\0'; -} - /* * latin2mic_with_table: a generic single byte charset encoding * conversion from a local charset to the mule internal code. diff --git a/src/backend/utils/mb/conversion_procs/Makefile b/src/backend/utils/mb/conversion_procs/Makefile index 258ffec06bfb..413aeb9861ec 100644 --- a/src/backend/utils/mb/conversion_procs/Makefile +++ b/src/backend/utils/mb/conversion_procs/Makefile @@ -14,9 +14,9 @@ top_builddir = ../../../../.. include $(top_builddir)/src/Makefile.global SUBDIRS = \ - ascii_and_mic cyrillic_and_mic euc_cn_and_mic euc_jp_and_sjis \ + cyrillic_and_mic euc_cn_and_mic euc_jp_and_sjis \ euc_kr_and_mic euc_tw_and_big5 latin2_and_win1250 latin_and_mic \ - utf8_and_ascii utf8_and_big5 utf8_and_cyrillic utf8_and_euc_cn \ + utf8_and_big5 utf8_and_cyrillic utf8_and_euc_cn \ utf8_and_euc_jp utf8_and_euc_kr utf8_and_euc_tw utf8_and_gb18030 \ utf8_and_gbk utf8_and_iso8859 utf8_and_iso8859_1 utf8_and_johab \ utf8_and_sjis utf8_and_win utf8_and_uhc \ diff --git a/src/backend/utils/mb/conversion_procs/ascii_and_mic/Makefile b/src/backend/utils/mb/conversion_procs/ascii_and_mic/Makefile deleted file mode 100644 index fa65eba9fd7c..000000000000 --- a/src/backend/utils/mb/conversion_procs/ascii_and_mic/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -#------------------------------------------------------------------------- -# -# src/backend/utils/mb/conversion_procs/ascii_and_mic/Makefile -# -#------------------------------------------------------------------------- -subdir = src/backend/utils/mb/conversion_procs/ascii_and_mic -top_builddir = ../../../../../.. -include $(top_builddir)/src/Makefile.global - -NAME = ascii_and_mic -PGFILEDESC = "ascii <-> mic text conversions" - -include $(srcdir)/../proc.mk diff --git a/src/backend/utils/mb/conversion_procs/ascii_and_mic/ascii_and_mic.c b/src/backend/utils/mb/conversion_procs/ascii_and_mic/ascii_and_mic.c deleted file mode 100644 index 95ecc94f07f0..000000000000 --- a/src/backend/utils/mb/conversion_procs/ascii_and_mic/ascii_and_mic.c +++ /dev/null @@ -1,60 +0,0 @@ -/*------------------------------------------------------------------------- - * - * ASCII and MULE_INTERNAL - * - * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * src/backend/utils/mb/conversion_procs/ascii_and_mic/ascii_and_mic.c - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" -#include "fmgr.h" -#include "mb/pg_wchar.h" - -PG_MODULE_MAGIC; - -PG_FUNCTION_INFO_V1(ascii_to_mic); -PG_FUNCTION_INFO_V1(mic_to_ascii); - -/* ---------- - * conv_proc( - * INTEGER, -- source encoding id - * INTEGER, -- destination encoding id - * CSTRING, -- source string (null terminated C string) - * CSTRING, -- destination string (null terminated C string) - * INTEGER -- source string length - * ) returns VOID; - * ---------- - */ - -Datum -ascii_to_mic(PG_FUNCTION_ARGS) -{ - unsigned char *src = (unsigned char *) PG_GETARG_CSTRING(2); - unsigned char *dest = (unsigned char *) PG_GETARG_CSTRING(3); - int len = PG_GETARG_INT32(4); - - CHECK_ENCODING_CONVERSION_ARGS(PG_SQL_ASCII, PG_MULE_INTERNAL); - - pg_ascii2mic(src, dest, len); - - PG_RETURN_VOID(); -} - -Datum -mic_to_ascii(PG_FUNCTION_ARGS) -{ - unsigned char *src = (unsigned char *) PG_GETARG_CSTRING(2); - unsigned char *dest = (unsigned char *) PG_GETARG_CSTRING(3); - int len = PG_GETARG_INT32(4); - - CHECK_ENCODING_CONVERSION_ARGS(PG_MULE_INTERNAL, PG_SQL_ASCII); - - pg_mic2ascii(src, dest, len); - - PG_RETURN_VOID(); -} diff --git a/src/backend/utils/mb/conversion_procs/utf8_and_ascii/Makefile b/src/backend/utils/mb/conversion_procs/utf8_and_ascii/Makefile deleted file mode 100644 index 7bd68e209b73..000000000000 --- a/src/backend/utils/mb/conversion_procs/utf8_and_ascii/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -#------------------------------------------------------------------------- -# -# src/backend/utils/mb/conversion_procs/utf8_and_ascii/Makefile -# -#------------------------------------------------------------------------- -subdir = src/backend/utils/mb/conversion_procs/utf8_and_ascii -top_builddir = ../../../../../.. -include $(top_builddir)/src/Makefile.global - -NAME = utf8_and_ascii -PGFILEDESC = "utf8 <-> ascii text conversions" - -include $(srcdir)/../proc.mk diff --git a/src/backend/utils/mb/conversion_procs/utf8_and_ascii/utf8_and_ascii.c b/src/backend/utils/mb/conversion_procs/utf8_and_ascii/utf8_and_ascii.c deleted file mode 100644 index 37db19345ed0..000000000000 --- a/src/backend/utils/mb/conversion_procs/utf8_and_ascii/utf8_and_ascii.c +++ /dev/null @@ -1,62 +0,0 @@ -/*------------------------------------------------------------------------- - * - * ASCII <--> UTF8 - * - * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * src/backend/utils/mb/conversion_procs/utf8_and_ascii/utf8_and_ascii.c - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" -#include "fmgr.h" -#include "mb/pg_wchar.h" - -PG_MODULE_MAGIC; - -PG_FUNCTION_INFO_V1(ascii_to_utf8); -PG_FUNCTION_INFO_V1(utf8_to_ascii); - -/* ---------- - * conv_proc( - * INTEGER, -- source encoding id - * INTEGER, -- destination encoding id - * CSTRING, -- source string (null terminated C string) - * CSTRING, -- destination string (null terminated C string) - * INTEGER -- source string length - * ) returns VOID; - * ---------- - */ - -Datum -ascii_to_utf8(PG_FUNCTION_ARGS) -{ - unsigned char *src = (unsigned char *) PG_GETARG_CSTRING(2); - unsigned char *dest = (unsigned char *) PG_GETARG_CSTRING(3); - int len = PG_GETARG_INT32(4); - - CHECK_ENCODING_CONVERSION_ARGS(PG_SQL_ASCII, PG_UTF8); - - /* this looks wrong, but basically we're just rejecting high-bit-set */ - pg_ascii2mic(src, dest, len); - - PG_RETURN_VOID(); -} - -Datum -utf8_to_ascii(PG_FUNCTION_ARGS) -{ - unsigned char *src = (unsigned char *) PG_GETARG_CSTRING(2); - unsigned char *dest = (unsigned char *) PG_GETARG_CSTRING(3); - int len = PG_GETARG_INT32(4); - - CHECK_ENCODING_CONVERSION_ARGS(PG_UTF8, PG_SQL_ASCII); - - /* this looks wrong, but basically we're just rejecting high-bit-set */ - pg_mic2ascii(src, dest, len); - - PG_RETURN_VOID(); -} diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c index 154fb189f03a..cc9df9c43513 100644 --- a/src/backend/utils/mb/mbutils.c +++ b/src/backend/utils/mb/mbutils.c @@ -205,8 +205,6 @@ SetClientEncoding(int encoding) int current_server_encoding; bool found; ListCell *lc; - ListCell *prev; - ListCell *next; if (!PG_VALID_FE_ENCODING(encoding)) return -1; @@ -240,13 +238,10 @@ SetClientEncoding(int encoding) * leak memory. */ found = false; - prev = NULL; - for (lc = list_head(ConvProcList); lc; lc = next) + foreach(lc, ConvProcList) { ConvProcInfo *convinfo = (ConvProcInfo *) lfirst(lc); - next = lnext(lc); - if (convinfo->s_encoding == current_server_encoding && convinfo->c_encoding == encoding) { @@ -261,13 +256,10 @@ SetClientEncoding(int encoding) else { /* Duplicate entry, release it */ - ConvProcList = list_delete_cell(ConvProcList, lc, prev); + ConvProcList = foreach_delete_current(ConvProcList, lc); pfree(convinfo); - continue; /* prev mustn't advance */ } } - - prev = lc; } if (found) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 2bb28db2611d..6148cb570d46 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -4214,7 +4214,7 @@ static struct config_string ConfigureNamesString[] = GUC_SUPERUSER_ONLY }, &SSLCipherSuites, -#ifdef USE_SSL +#ifdef USE_OPENSSL "HIGH:MEDIUM:+3DES:!aNULL", #else "none", diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index bc0059fe1a98..8dfac624ba8c 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -84,7 +84,7 @@ # (change requires restart) # - TCP settings - -# see "man 7 tcp" for details +# see "man tcp" for details #tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; # 0 selects the system default diff --git a/src/backend/utils/misc/tzparser.c b/src/backend/utils/misc/tzparser.c index cdb9db475d0d..167db84c4deb 100644 --- a/src/backend/utils/misc/tzparser.c +++ b/src/backend/utils/misc/tzparser.c @@ -53,8 +53,7 @@ validateTzEntry(tzEntry *tzentry) unsigned char *p; /* - * Check restrictions imposed by datetkntbl storage format (see - * datetime.c) + * Check restrictions imposed by datetktbl storage format (see datetime.c) */ if (strlen(tzentry->abbrev) > TOKMAXLEN) { diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 3c89708edb9a..de9d529b1415 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -1087,7 +1087,7 @@ AllocSetAlloc(MemoryContext context, Size size) /* * We could be asking for pretty big blocks here, so cope if malloc - * fails. But give up if there's less than a meg or so available... + * fails. But give up if there's less than 1 MB or so available... */ while (block == NULL && blksize > 1024 * 1024) { diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c index 900cd8357cac..6590e55a241e 100644 --- a/src/backend/utils/mmgr/dsa.c +++ b/src/backend/utils/mmgr/dsa.c @@ -2235,8 +2235,8 @@ check_for_freed_segments(dsa_area *area) /* * Any other process that has freed a segment has incremented - * free_segment_counter while holding an LWLock, and that must precede any - * backend creating a new segment in the same slot while holding an + * freed_segment_counter while holding an LWLock, and that must precede + * any backend creating a new segment in the same slot while holding an * LWLock, and that must precede the creation of any dsa_pointer pointing * into the new segment which might reach us here, and the caller must * have sent the dsa_pointer to this process using appropriate memory diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index 81cb96588182..001a44c14931 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -1066,6 +1066,36 @@ xmin_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg) return 0; } +/* + * Get current RecentGlobalXmin value, as a FullTransactionId. + */ +FullTransactionId +GetFullRecentGlobalXmin(void) +{ + FullTransactionId nextxid_full; + uint32 nextxid_epoch; + TransactionId nextxid_xid; + uint32 epoch; + + Assert(TransactionIdIsNormal(RecentGlobalXmin)); + + /* + * Compute the epoch from the next XID's epoch. This relies on the fact + * that RecentGlobalXmin must be within the 2 billion XID horizon from the + * next XID. + */ + nextxid_full = ReadNextFullTransactionId(); + nextxid_epoch = EpochFromFullTransactionId(nextxid_full); + nextxid_xid = XidFromFullTransactionId(nextxid_full); + + if (RecentGlobalXmin > nextxid_xid) + epoch = nextxid_epoch - 1; + else + epoch = nextxid_epoch; + + return FullTransactionIdFromEpochAndXid(epoch, RecentGlobalXmin); +} + /* * SnapshotResetXmin * @@ -1203,7 +1233,7 @@ AtEOXact_Snapshot(bool isCommit, bool resetXmin) * prevent a warning below. * * As with the FirstXactSnapshot, we don't need to free resources of - * the snapshot iself as it will go away with the memory context. + * the snapshot itself as it will go away with the memory context. */ foreach(lc, exportedSnapshots) { diff --git a/src/bin/initdb/findtimezone.c b/src/bin/initdb/findtimezone.c index a5c9c9ee5113..786e78742dda 100644 --- a/src/bin/initdb/findtimezone.c +++ b/src/bin/initdb/findtimezone.c @@ -608,22 +608,28 @@ check_system_link_file(const char *linkname, struct tztry *tt, /* * Given a timezone name, determine whether it should be preferred over other * names which are equally good matches. The output is arbitrary but we will - * use 0 for "neutral" default preference. - * - * Ideally we'd prefer the zone.tab/zone1970.tab names, since in general those - * are the ones offered to the user to select from. But for the moment, to - * minimize changes in behaviour, simply prefer UTC over alternative spellings - * such as UCT that otherwise cause confusion. The existing "shortest first" - * rule would prefer "UTC" over "Etc/UTC" so keep that the same way (while - * still preferring Etc/UTC over Etc/UCT). + * use 0 for "neutral" default preference; larger values are more preferred. */ static int zone_name_pref(const char *zonename) { + /* + * Prefer UTC over alternatives such as UCT. Also prefer Etc/UTC over + * Etc/UCT; but UTC is preferred to Etc/UTC. + */ if (strcmp(zonename, "UTC") == 0) return 50; if (strcmp(zonename, "Etc/UTC") == 0) return 40; + + /* + * We don't want to pick "localtime" or "posixrules", unless we can find + * no other name for the prevailing zone. Those aren't real zone names. + */ + if (strcmp(zonename, "localtime") == 0 || + strcmp(zonename, "posixrules") == 0) + return -50; + return 0; } diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 7f026f7a539e..34be6581c523 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -725,6 +725,8 @@ static const struct tsearch_config_match tsearch_config_languages[] = {"french", "French"}, {"german", "de"}, {"german", "German"}, + {"greek", "el"}, + {"greek", "Greek"}, {"hungarian", "hu"}, {"hungarian", "Hungarian"}, {"indonesian", "id"}, @@ -1074,7 +1076,7 @@ test_config_settings(void) else printf("%dkB\n", n_buffers * (BLCKSZ / 1024)); - printf(_("selecting default timezone ... ")); + printf(_("selecting default time zone ... ")); fflush(stdout); default_timezone = select_default_timezone(share_path); printf("%s\n", default_timezone ? default_timezone : "GMT"); @@ -2180,7 +2182,7 @@ make_template0(FILE *cmdfd) "CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false;\n\n", /* - * We use the OID of template0 to determine lastsysoid + * We use the OID of template0 to determine datlastsysoid */ "UPDATE pg_database SET datlastsysoid = " " (SELECT oid FROM pg_database " @@ -2736,7 +2738,7 @@ setup_bin_paths(const char *argv0) pg_log_error("The program \"postgres\" is needed by %s but was not found in the\n" "same directory as \"%s\".\n" "Check your installation.", - full_path, progname); + progname, full_path); else pg_log_error("The program \"postgres\" was found by \"%s\"\n" "but was not the same version as %s.\n" diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 3a76f8f1caca..99ca4bd0be03 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -1479,7 +1479,9 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum) if (file == NULL) { +#ifndef WIN32 int filemode; +#endif /* * No current file, so this must be the header for a new file @@ -1493,8 +1495,10 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum) current_len_left = read_tar_number(©buf[124], 12); +#ifndef WIN32 /* Set permissions on the file */ filemode = read_tar_number(©buf[100], 8); +#endif /* * All files are padded up to 512 bytes diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c index b029118bf604..af29dd7651a4 100644 --- a/src/bin/pg_basebackup/pg_recvlogical.c +++ b/src/bin/pg_basebackup/pg_recvlogical.c @@ -192,8 +192,8 @@ OutputFsync(TimestampTz now) if (fsync(outfd) != 0) { - pg_log_error("could not fsync file \"%s\": %m", outfile); - return false; + pg_log_fatal("could not fsync file \"%s\": %m", outfile); + exit(1); } return true; @@ -1020,12 +1020,11 @@ prepareToTerminate(PGconn *conn, XLogRecPtr endpos, bool keepalive, XLogRecPtr l if (verbose) { if (keepalive) - pg_log_info("endpos %X/%X reached by keepalive", + pg_log_info("end position %X/%X reached by keepalive", (uint32) (endpos >> 32), (uint32) endpos); else - pg_log_info("endpos %X/%X reached by record at %X/%X", + pg_log_info("end position %X/%X reached by WAL record at %X/%X", (uint32) (endpos >> 32), (uint32) (endpos), (uint32) (lsn >> 32), (uint32) lsn); - } } diff --git a/src/bin/pg_basebackup/receivelog.c b/src/bin/pg_basebackup/receivelog.c index 2fd16421aa75..d73f6145c1a5 100644 --- a/src/bin/pg_basebackup/receivelog.c +++ b/src/bin/pg_basebackup/receivelog.c @@ -134,10 +134,10 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint) /* fsync file in case of a previous crash */ if (stream->walmethod->sync(f) != 0) { - pg_log_error("could not fsync existing write-ahead log file \"%s\": %s", + pg_log_fatal("could not fsync existing write-ahead log file \"%s\": %s", fn, stream->walmethod->getlasterror()); stream->walmethod->close(f, CLOSE_UNLINK); - return false; + exit(1); } walfile = f; @@ -763,9 +763,9 @@ HandleCopyStream(PGconn *conn, StreamCtl *stream, { if (stream->walmethod->sync(walfile) != 0) { - pg_log_error("could not fsync file \"%s\": %s", + pg_log_fatal("could not fsync file \"%s\": %s", current_walfile_name, stream->walmethod->getlasterror()); - goto error; + exit(1); } lastFlushPosition = blockpos; @@ -1015,9 +1015,9 @@ ProcessKeepaliveMsg(PGconn *conn, StreamCtl *stream, char *copybuf, int len, */ if (stream->walmethod->sync(walfile) != 0) { - pg_log_error("could not fsync file \"%s\": %s", + pg_log_fatal("could not fsync file \"%s\": %s", current_walfile_name, stream->walmethod->getlasterror()); - return false; + exit(1); } lastFlushPosition = blockpos; } diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c index aa66f3a8a058..aba64ec63000 100644 --- a/src/bin/pg_basebackup/streamutil.c +++ b/src/bin/pg_basebackup/streamutil.c @@ -500,19 +500,19 @@ CreateReplicationSlot(PGconn *conn, const char *slot_name, const char *plugin, /* Build query */ appendPQExpBuffer(query, "CREATE_REPLICATION_SLOT \"%s\"", slot_name); if (is_temporary) - appendPQExpBuffer(query, " TEMPORARY"); + appendPQExpBufferStr(query, " TEMPORARY"); if (is_physical) { - appendPQExpBuffer(query, " PHYSICAL"); + appendPQExpBufferStr(query, " PHYSICAL"); if (reserve_wal) - appendPQExpBuffer(query, " RESERVE_WAL"); + appendPQExpBufferStr(query, " RESERVE_WAL"); } else { appendPQExpBuffer(query, " LOGICAL \"%s\"", plugin); if (PQserverVersion(conn) >= 100000) /* pg_recvlogical doesn't use an exported snapshot, so suppress */ - appendPQExpBuffer(query, " NOEXPORT_SNAPSHOT"); + appendPQExpBufferStr(query, " NOEXPORT_SNAPSHOT"); } res = PQexec(conn, query->data); diff --git a/src/bin/pg_basebackup/walmethods.c b/src/bin/pg_basebackup/walmethods.c index 83b520898bef..8ec12e6f723c 100644 --- a/src/bin/pg_basebackup/walmethods.c +++ b/src/bin/pg_basebackup/walmethods.c @@ -864,7 +864,7 @@ tar_close(Walfile f, WalCloseMethod method) /* Always fsync on close, so the padding gets fsynced */ if (tar_sync(f) < 0) - return -1; + exit(1); /* Clean up and done */ pg_free(tf->pathname); diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c index b591fcc864a6..8c00ec9a3b1a 100644 --- a/src/bin/pg_checksums/pg_checksums.c +++ b/src/bin/pg_checksums/pg_checksums.c @@ -235,7 +235,7 @@ scan_file(const char *fn, BlockNumber segmentno) /* Write block with checksum */ if (write(f, buf.data, BLCKSZ) != BLCKSZ) { - pg_log_error("could not update checksum of block %u in file \"%s\": %m", + pg_log_error("could not write block %u in file \"%s\": %m", blockno, fn); exit(1); } @@ -469,7 +469,7 @@ main(int argc, char *argv[]) /* filenode checking only works in --check mode */ if (mode != PG_MODE_CHECK && only_filenode) { - pg_log_error("--filenode option only possible with --check"); + pg_log_error("option -f/--filenode can only be used with --check"); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 03ee90f1be06..0231114a9bd3 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -4,7 +4,7 @@ * reads the data from $PGDATA/global/pg_control * * copyright (c) Oliver Elphick , 2001; - * licence: BSD + * license: BSD * * src/bin/pg_controldata/pg_controldata.c */ @@ -100,7 +100,6 @@ main(int argc, char *argv[]) time_t time_tmp; char pgctime_str[128]; char ckpttime_str[128]; - char sysident_str[32]; char mock_auth_nonce_str[MOCK_AUTH_NONCE_LEN * 2 + 1]; const char *strftime_fmt = "%c"; const char *progname; @@ -229,13 +228,6 @@ main(int argc, char *argv[]) else strcpy(xlogfilename, _("???")); - /* - * Format system_identifier and mock_authentication_nonce separately to - * keep platform-dependent format code out of the translatable message - * string. - */ - snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT, - ControlFile->system_identifier); for (i = 0; i < MOCK_AUTH_NONCE_LEN; i++) snprintf(&mock_auth_nonce_str[i * 2], 3, "%02x", (unsigned char) ControlFile->mock_authentication_nonce[i]); @@ -244,8 +236,8 @@ main(int argc, char *argv[]) ControlFile->pg_control_version); printf(_("Catalog version number: %u\n"), ControlFile->catalog_version_no); - printf(_("Database system identifier: %s\n"), - sysident_str); + printf(_("Database system identifier: %llu\n"), + (unsigned long long) ControlFile->system_identifier); printf(_("Database cluster state: %s\n"), dbState(ControlFile->state)); printf(_("pg_control last modified: %s\n"), diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 6760a78d02f2..d0b4000b7be0 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -1524,14 +1524,14 @@ pgwin32_CommandLine(bool registration) appendPQExpBuffer(cmdLine, " -e \"%s\"", event_source); if (registration && do_wait) - appendPQExpBuffer(cmdLine, " -w"); + appendPQExpBufferStr(cmdLine, " -w"); /* Don't propagate a value from an environment variable. */ if (registration && wait_seconds_arg && wait_seconds != DEFAULT_WAIT) appendPQExpBuffer(cmdLine, " -t %d", wait_seconds); if (registration && silent_mode) - appendPQExpBuffer(cmdLine, " -s"); + appendPQExpBufferStr(cmdLine, " -s"); if (post_opts) { @@ -1922,7 +1922,7 @@ pgwin32_is_service(void) /* * Mingw headers are incomplete, and so are the libraries. So we have to load * a whole lot of API functions dynamically. Since we have to do this anyway, - * also load the couple of functions that *do* exist in minwg headers but not + * also load the couple of functions that *do* exist in mingw headers but not * on NT4. That way, we don't break on NT4. */ typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); @@ -2376,6 +2376,7 @@ adjust_data_dir(void) filename[MAXPGPATH], *my_exec_path; FILE *fd; + int len; /* do nothing if we're working without knowledge of data dir */ if (pg_config == NULL) @@ -2418,9 +2419,12 @@ adjust_data_dir(void) pclose(fd); free(my_exec_path); - /* Remove trailing newline */ - if (strchr(filename, '\n') != NULL) - *strchr(filename, '\n') = '\0'; + /* Remove trailing newline, handling Windows newlines as well */ + len = strlen(filename); + while (len > 0 && + (filename[len - 1] == '\n' || + filename[len - 1] == '\r')) + filename[--len] = '\0'; free(pg_data); pg_data = pg_strdup(filename); diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index cd990c0a5104..2b3e854a372a 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -189,7 +189,6 @@ typedef struct _dumpOptions int no_synchronized_snapshots; int no_unlogged_table_data; int serializable_deferrable; - int quote_all_identifiers; int disable_triggers; int outputNoTablespaces; int use_setsessauth; diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 0641fb545a84..62fba0a7ae1d 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -563,8 +563,8 @@ RestoreArchive(Archive *AHX) */ if (strncmp(dropStmt, "ALTER TABLE", 11) == 0) { - appendPQExpBuffer(ftStmt, - "ALTER TABLE IF EXISTS"); + appendPQExpBufferStr(ftStmt, + "ALTER TABLE IF EXISTS"); dropStmt = dropStmt + 11; } @@ -4957,7 +4957,7 @@ CloneArchive(ArchiveHandle *AH) * any data to/from the database. */ initPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, PQdb(AH->connection)); pghost = PQhost(AH->connection); pgport = PQport(AH->connection); diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c index 90cc9420ae54..13b2cd4c04ed 100644 --- a/src/bin/pg_dump/pg_backup_db.c +++ b/src/bin/pg_dump/pg_backup_db.c @@ -150,7 +150,7 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser) } initPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, newdb); do @@ -177,7 +177,7 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser) newConn = PQconnectdbParams(keywords, values, true); if (!newConn) - fatal("failed to reconnect to database"); + fatal("could not reconnect to database"); if (PQstatus(newConn) == CONNECTION_BAD) { @@ -288,7 +288,7 @@ ConnectDatabase(Archive *AHX, AH->connection = PQconnectdbParams(keywords, values, true); if (!AH->connection) - fatal("failed to connect to database"); + fatal("could not connect to database"); if (PQstatus(AH->connection) == CONNECTION_BAD && PQconnectionNeedsPassword(AH->connection) && @@ -394,7 +394,7 @@ notice_processor(void *arg pg_attribute_unused(), const char *message) pg_log_generic(PG_LOG_INFO, "%s", message); } -/* Like exit_fatal(), but with a complaint about a particular query. */ +/* Like fatal(), but with a complaint about a particular query. */ static void die_on_query_failure(ArchiveHandle *AH, const char *query) { diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c index 7a3917f12ba9..060f5fb028c4 100644 --- a/src/bin/pg_dump/pg_backup_tar.c +++ b/src/bin/pg_dump/pg_backup_tar.c @@ -645,7 +645,7 @@ _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh) } } else - fatal("internal error -- neither th nor fh specified in tarReadRaw()\n"); + fatal("internal error -- neither th nor fh specified in tarReadRaw()"); } ctx->tarFHpos += res + used; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4fe19f087f34..778be70738a3 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1377,7 +1377,7 @@ setup_connection(Archive *AH, const char *dumpencoding, { PQExpBuffer query = createPQExpBuffer(); - appendPQExpBuffer(query, "SET TRANSACTION SNAPSHOT "); + appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT "); appendStringLiteralConn(query, AH->sync_snapshot_id, conn); ExecuteSqlStatement(AH, query->data); destroyPQExpBuffer(query); @@ -1487,8 +1487,8 @@ expand_schema_name_patterns(Archive *fout, for (cell = patterns->head; cell; cell = cell->next) { - appendPQExpBuffer(query, - "SELECT oid FROM pg_catalog.pg_namespace n\n"); + appendPQExpBufferStr(query, + "SELECT oid FROM pg_catalog.pg_namespace n\n"); processSQLNamePattern(GetConnection(fout), query, cell->val, false, false, NULL, "n.nspname", NULL, NULL); @@ -3074,15 +3074,23 @@ dumpDatabase(Archive *fout) appendPQExpBufferStr(creaQry, " ENCODING = "); appendStringLiteralAH(creaQry, encoding, fout); } - if (strlen(collate) > 0) + if (strlen(collate) > 0 && strcmp(collate, ctype) == 0) { - appendPQExpBufferStr(creaQry, " LC_COLLATE = "); + appendPQExpBufferStr(creaQry, " LOCALE = "); appendStringLiteralAH(creaQry, collate, fout); } - if (strlen(ctype) > 0) + else { - appendPQExpBufferStr(creaQry, " LC_CTYPE = "); - appendStringLiteralAH(creaQry, ctype, fout); + if (strlen(collate) > 0) + { + appendPQExpBufferStr(creaQry, " LC_COLLATE = "); + appendStringLiteralAH(creaQry, collate, fout); + } + if (strlen(ctype) > 0) + { + appendPQExpBufferStr(creaQry, " LC_CTYPE = "); + appendStringLiteralAH(creaQry, ctype, fout); + } } /* @@ -3971,7 +3979,7 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo) if (polinfo->polwithcheck != NULL) appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck); - appendPQExpBuffer(query, ";\n"); + appendPQExpBufferStr(query, ";\n"); appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname)); appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo)); @@ -5589,9 +5597,9 @@ getAccessMethods(Archive *fout, int *numAccessMethods) query = createPQExpBuffer(); /* Select all access methods from pg_am table */ - appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, " - "amhandler::pg_catalog.regproc AS amhandler " - "FROM pg_am"); + appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, " + "amhandler::pg_catalog.regproc AS amhandler " + "FROM pg_am"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -7076,9 +7084,7 @@ getInherits(Archive *fout, int *numInherits) /* * Find all the inheritance information, excluding implicit inheritance - * via partitioning. We handle that case using getPartitions(), because - * we want more information about partitions than just the parent-child - * relationship. + * via partitioning. */ appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); @@ -8439,10 +8445,10 @@ getTransforms(Archive *fout, int *numTransforms) query = createPQExpBuffer(); - appendPQExpBuffer(query, "SELECT tableoid, oid, " - "trftype, trflang, trffromsql::oid, trftosql::oid " - "FROM pg_transform " - "ORDER BY 3,4"); + appendPQExpBufferStr(query, "SELECT tableoid, oid, " + "trftype, trflang, trffromsql::oid, trftosql::oid " + "FROM pg_transform " + "ORDER BY 3,4"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -12884,7 +12890,7 @@ dumpTransform(Archive *fout, const TransformInfo *transform) if (transform->trftosql) { if (transform->trffromsql) - appendPQExpBuffer(defqry, ", "); + appendPQExpBufferStr(defqry, ", "); if (tosqlFuncInfo) { @@ -12902,7 +12908,7 @@ dumpTransform(Archive *fout, const TransformInfo *transform) pg_log_warning("bogus value in pg_transform.trftosql field"); } - appendPQExpBuffer(defqry, ");\n"); + appendPQExpBufferStr(defqry, ");\n"); appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s", transformType, lanname); @@ -13272,10 +13278,10 @@ dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo) switch (aminfo->amtype) { case AMTYPE_INDEX: - appendPQExpBuffer(q, "TYPE INDEX "); + appendPQExpBufferStr(q, "TYPE INDEX "); break; case AMTYPE_TABLE: - appendPQExpBuffer(q, "TYPE TABLE "); + appendPQExpBufferStr(q, "TYPE TABLE "); break; default: pg_log_warning("invalid type \"%c\" of access method \"%s\"", @@ -13937,23 +13943,23 @@ dumpCollation(Archive *fout, const CollInfo *collinfo) qcollname = pg_strdup(fmtId(collinfo->dobj.name)); /* Get collation-specific details */ - appendPQExpBuffer(query, "SELECT "); + appendPQExpBufferStr(query, "SELECT "); if (fout->remoteVersion >= 100000) - appendPQExpBuffer(query, - "collprovider, " - "collversion, "); + appendPQExpBufferStr(query, + "collprovider, " + "collversion, "); else - appendPQExpBuffer(query, - "'c' AS collprovider, " - "NULL AS collversion, "); + appendPQExpBufferStr(query, + "'c' AS collprovider, " + "NULL AS collversion, "); if (fout->remoteVersion >= 120000) - appendPQExpBuffer(query, - "collisdeterministic, "); + appendPQExpBufferStr(query, + "collisdeterministic, "); else - appendPQExpBuffer(query, - "true AS collisdeterministic, "); + appendPQExpBufferStr(query, + "true AS collisdeterministic, "); appendPQExpBuffer(query, "collcollate, " @@ -13988,7 +13994,7 @@ dumpCollation(Archive *fout, const CollInfo *collinfo) /* to allow dumping pg_catalog; not accepted on input */ appendPQExpBufferStr(q, "default"); else - fatal("unrecognized collation provider: %s\n", + fatal("unrecognized collation provider: %s", collprovider); if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0) @@ -14169,7 +14175,7 @@ format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quo appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name); if (agginfo->aggfn.nargs == 0) - appendPQExpBuffer(&buf, "(*)"); + appendPQExpBufferStr(&buf, "(*)"); else { appendPQExpBufferChar(&buf, '('); @@ -15505,7 +15511,7 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId, if (dopt->binary_upgrade && privtype == 'e' && initprivs && *initprivs != '\0') { - appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n"); + appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n"); if (!buildACLCommands(name, subname, nspname, type, initprivs, acldefault, owner, "", fout->remoteVersion, sql)) @@ -17911,7 +17917,7 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo) } if (indxinfo->indnkeyattrs < indxinfo->indnattrs) - appendPQExpBuffer(q, ") INCLUDE ("); + appendPQExpBufferStr(q, ") INCLUDE ("); for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++) { @@ -18290,9 +18296,9 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo) "ALTER COLUMN %s ADD GENERATED ", fmtId(owning_tab->attnames[tbinfo->owning_col - 1])); if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS) - appendPQExpBuffer(query, "ALWAYS"); + appendPQExpBufferStr(query, "ALWAYS"); else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT) - appendPQExpBuffer(query, "BY DEFAULT"); + appendPQExpBufferStr(query, "BY DEFAULT"); appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n", fmtQualifiedDumpable(tbinfo)); } diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index b706606b8eff..835136b415cc 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -1154,7 +1154,15 @@ repairDependencyLoop(DumpableObject **loop, } } - /* Loop of table with itself, happens with generated columns */ + /* + * Loop of table with itself --- just ignore it. + * + * (Actually, what this arises from is a dependency of a table column on + * another column, which happens with generated columns; or a dependency + * of a table column on the whole table, which happens with partitioning. + * But we didn't pay attention to sub-object IDs while collecting the + * dependency data, so we can't see that here.) + */ if (nLoop == 1) { if (loop[0]->objType == DO_TABLE) diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 1d80b62a0307..962b5fd0d3a0 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -535,7 +535,7 @@ main(int argc, char *argv[]) OPF = fopen(filename, PG_BINARY_W); if (!OPF) { - pg_log_error("could not open the output file \"%s\": %m", + pg_log_error("could not open output file \"%s\": %m", filename); exit_nicely(1); } @@ -1820,8 +1820,8 @@ expand_dbname_patterns(PGconn *conn, for (SimpleStringListCell *cell = patterns->head; cell; cell = cell->next) { - appendPQExpBuffer(query, - "SELECT datname FROM pg_catalog.pg_database n\n"); + appendPQExpBufferStr(query, + "SELECT datname FROM pg_catalog.pg_database n\n"); processSQLNamePattern(conn, query, cell->val, false, false, NULL, "datname", NULL, NULL); @@ -1880,11 +1880,11 @@ dumpDatabases(PGconn *conn) /* Skip any explicitly excluded database */ if (simple_string_list_member(&database_exclude_names, dbname)) { - pg_log_info("excluding database \"%s\"...", dbname); + pg_log_info("excluding database \"%s\"", dbname); continue; } - pg_log_info("dumping database \"%s\"...", dbname); + pg_log_info("dumping database \"%s\"", dbname); fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n", dbname); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 90ed211d693f..4cec37456f28 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1430,6 +1430,15 @@ like => { pg_dumpall_dbprivs => 1, }, }, + "CREATE DATABASE dump_test2 LOCALE = 'C'" => { + create_order => 47, + create_sql => "CREATE DATABASE dump_test2 LOCALE = 'C' TEMPLATE = template0;", + regexp => qr/^ + \QCREATE DATABASE dump_test2 \E.*\QLOCALE = 'C';\E + /xm, + like => { pg_dumpall_dbprivs => 1, }, + }, + 'CREATE EXTENSION ... plpgsql' => { regexp => qr/^ \QCREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;\E diff --git a/src/bin/pg_dump/t/010_dump_connstr.pl b/src/bin/pg_dump/t/010_dump_connstr.pl index 7a39b2e8fd62..4e1b37fb6f3f 100644 --- a/src/bin/pg_dump/t/010_dump_connstr.pl +++ b/src/bin/pg_dump/t/010_dump_connstr.pl @@ -171,21 +171,19 @@ system_log('cat', $plain); my ($stderr, $result); my $restore_super = qq{regress_a'b\\c=d\\ne"f}; +$restore_super =~ s/"//g + if $TestLib::windows_os; # IPC::Run mishandles '"' on Windows # Restore full dump through psql using environment variables for # dbname/user connection parameters my $envar_node = get_new_node('destination_envar'); -$envar_node->init(extra => - [ '-U', $dst_bootstrap_super, '--locale=C', '--encoding=LATIN1' ]); -$envar_node->run_log( - [ - $ENV{PG_REGRESS}, '--config-auth', - $envar_node->data_dir, '--user', - $dst_bootstrap_super, '--create-role', - $restore_super - ]); +$envar_node->init( + extra => + [ '-U', $dst_bootstrap_super, '--locale=C', '--encoding=LATIN1' ], + auth_extra => + [ '--user', $dst_bootstrap_super, '--create-role', $restore_super ]); $envar_node->start; # make superuser for restore @@ -207,18 +205,12 @@ # dbname/user connection parameters. "\connect dbname=" forgets # user/port from command line. -$restore_super =~ s/"//g - if $TestLib::windows_os; # IPC::Run mishandles '"' on Windows my $cmdline_node = get_new_node('destination_cmdline'); -$cmdline_node->init(extra => - [ '-U', $dst_bootstrap_super, '--locale=C', '--encoding=LATIN1' ]); -$cmdline_node->run_log( - [ - $ENV{PG_REGRESS}, '--config-auth', - $cmdline_node->data_dir, '--user', - $dst_bootstrap_super, '--create-role', - $restore_super - ]); +$cmdline_node->init( + extra => + [ '-U', $dst_bootstrap_super, '--locale=C', '--encoding=LATIN1' ], + auth_extra => + [ '--user', $dst_bootstrap_super, '--create-role', $restore_super ]); $cmdline_node->start; $cmdline_node->run_log( [ 'createuser', '-U', $dst_bootstrap_super, '-s', $restore_super ]); diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index a5848ed088cb..13b14e435622 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -733,12 +733,10 @@ CheckDataVersion(void) /* remove trailing newline, handling Windows newlines as well */ len = strlen(rawline); - if (len > 0 && rawline[len - 1] == '\n') - { + while (len > 0 && + (rawline[len - 1] == '\n' || + rawline[len - 1] == '\r')) rawline[--len] = '\0'; - if (len > 0 && rawline[len - 1] == '\r') - rawline[--len] = '\0'; - } if (strcmp(rawline, PG_MAJORVERSION) != 0) { @@ -925,26 +923,17 @@ GuessControlValues(void) static void PrintControlValues(bool guessed) { - char sysident_str[32]; - if (guessed) printf(_("Guessed pg_control values:\n\n")); else printf(_("Current pg_control values:\n\n")); - /* - * Format system_identifier separately to keep platform-dependent format - * code out of the translatable message string. - */ - snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT, - ControlFile.system_identifier); - printf(_("pg_control version number: %u\n"), ControlFile.pg_control_version); printf(_("Catalog version number: %u\n"), ControlFile.catalog_version_no); - printf(_("Database system identifier: %s\n"), - sysident_str); + printf(_("Database system identifier: %llu\n"), + (unsigned long long) ControlFile.system_identifier); printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile.checkPointCopy.ThisTimeLineID); printf(_("Latest checkpoint's full_page_writes: %s\n"), diff --git a/src/bin/pg_rewind/libpq_fetch.c b/src/bin/pg_rewind/libpq_fetch.c index 8adbfdf57b0e..71806eea5f8e 100644 --- a/src/bin/pg_rewind/libpq_fetch.c +++ b/src/bin/pg_rewind/libpq_fetch.c @@ -260,7 +260,6 @@ receiveFileChunks(const char *sql) char *filename; int filenamelen; int64 chunkoff; - char chunkoff_str[32]; int chunksize; char *chunk; @@ -336,13 +335,8 @@ receiveFileChunks(const char *sql) continue; } - /* - * Separate step to keep platform-dependent format code out of - * translatable strings. - */ - snprintf(chunkoff_str, sizeof(chunkoff_str), INT64_FORMAT, chunkoff); - pg_log_debug("received chunk for file \"%s\", offset %s, size %d", - filename, chunkoff_str, chunksize); + pg_log_debug("received chunk for file \"%s\", offset %lld, size %d", + filename, (long long int) chunkoff, chunksize); open_target_file(filename, false); diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c index 94e15d2adca6..36e00a51d747 100644 --- a/src/bin/pg_rewind/pg_rewind.c +++ b/src/bin/pg_rewind/pg_rewind.c @@ -628,7 +628,7 @@ getTimelineHistory(ControlFileData *controlFile, int *nentries) else if (controlFile == &ControlFile_target) histfile = slurpFile(datadir_target, path, NULL); else - pg_fatal("invalid control file\n"); + pg_fatal("invalid control file"); history = rewind_parseTimeLineHistory(histfile, tli, nentries); pg_free(histfile); diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c index 6e2fd1ab8c7a..e14802372bd6 100644 --- a/src/bin/pg_test_timing/pg_test_timing.c +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -18,7 +18,7 @@ static uint64 test_timing(int32); static void output(uint64 loop_count); /* record duration in powers of 2 microseconds */ -int64 histogram[32]; +long long int histogram[32]; int main(int argc, char *argv[]) @@ -190,14 +190,8 @@ output(uint64 loop_count) Max(10, len3), header3); for (i = 0; i <= max_bit; i++) - { - char buf[100]; - - /* lame hack to work around INT64_FORMAT deficiencies */ - snprintf(buf, sizeof(buf), INT64_FORMAT, histogram[i]); - printf("%*ld %*.5f %*s\n", + printf("%*ld %*.5f %*lld\n", Max(6, len1), 1l << i, Max(10, len2) - 1, (double) histogram[i] * 100 / loop_count, - Max(10, len3), buf); - } + Max(10, len3), histogram[i]); } diff --git a/src/bin/pg_upgrade/dump.c b/src/bin/pg_upgrade/dump.c index ac7b834ccb54..80c94a0abbbb 100644 --- a/src/bin/pg_upgrade/dump.c +++ b/src/bin/pg_upgrade/dump.c @@ -44,7 +44,7 @@ generate_old_dump(void) escaped_connstr; initPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, old_db->db_name); initPQExpBuffer(&escaped_connstr); appendShellString(&escaped_connstr, connstr.data); diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c index c2b4382e61bc..72443a9cb97e 100644 --- a/src/bin/pg_upgrade/exec.c +++ b/src/bin/pg_upgrade/exec.c @@ -380,6 +380,7 @@ check_bin_dir(ClusterInfo *cluster) cluster->bindir); validate_exec(cluster->bindir, "postgres"); + validate_exec(cluster->bindir, "pg_controldata"); validate_exec(cluster->bindir, "pg_ctl"); /* @@ -394,12 +395,20 @@ check_bin_dir(ClusterInfo *cluster) validate_exec(cluster->bindir, "pg_resetxlog"); else validate_exec(cluster->bindir, "pg_resetwal"); + if (cluster == &new_cluster) { - /* these are only needed in the new cluster */ - validate_exec(cluster->bindir, "psql"); + /* + * These binaries are only needed for the target version. pg_dump and + * pg_dumpall are used to dump the old cluster, but must be of the + * target version. + */ + validate_exec(cluster->bindir, "initdb"); validate_exec(cluster->bindir, "pg_dump"); validate_exec(cluster->bindir, "pg_dumpall"); + validate_exec(cluster->bindir, "pg_restore"); + validate_exec(cluster->bindir, "psql"); + validate_exec(cluster->bindir, "vacuumdb"); } } diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c index cdd885f638d7..8a6d81ad3071 100644 --- a/src/bin/pg_upgrade/option.c +++ b/src/bin/pg_upgrade/option.c @@ -1,5 +1,5 @@ /* - * opt.c + * option.c * * options functions * @@ -24,7 +24,8 @@ static void usage(void); static void check_required_directory(char **dirpath, const char *envVarName, bool useCwd, - const char *cmdLineOption, const char *description); + const char *cmdLineOption, const char *description, + bool missingOk); #define FIX_DEFAULT_READ_ONLY "-c default_transaction_read_only=false" @@ -262,21 +263,18 @@ parseCommandLine(int argc, char *argv[]) /* Get values from env if not already set */ check_required_directory(&old_cluster.bindir, "PGBINOLD", false, - "-b", _("old cluster binaries reside")); - + "-b", _("old cluster binaries reside"), false); if(!is_skip_target_check()) check_required_directory(&new_cluster.bindir, "PGBINNEW", false, - "-B", "new cluster binaries reside"); - + "-B", _("new cluster binaries reside"), true); check_required_directory(&old_cluster.pgdata, "PGDATAOLD", false, - "-d", _("old cluster data resides")); - + "-d", _("old cluster data resides"), false); if(!is_skip_target_check()) check_required_directory(&new_cluster.pgdata, "PGDATANEW", false, - "-D", "new cluster data resides"); + "-D", _("new cluster data resides"), false); check_required_directory(&user_opts.socketdir, "PGSOCKETDIR", true, - "-s", _("sockets will be created")); + "-s", _("sockets will be created"), false); #ifdef WIN32 /* @@ -309,7 +307,8 @@ usage(void) printf(_(" pg_upgrade [OPTION]...\n\n")); printf(_("Options:\n")); printf(_(" -b, --old-bindir=BINDIR old cluster executable directory\n")); - printf(_(" -B, --new-bindir=BINDIR new cluster executable directory\n")); + printf(_(" -B, --new-bindir=BINDIR new cluster executable directory (default\n" + " same directory as pg_upgrade)")); printf(_(" -c, --check check clusters only, don't change any data\n")); printf(_(" -d, --old-datadir=DATADIR old cluster data directory\n")); printf(_(" -D, --new-datadir=DATADIR new cluster data directory\n")); @@ -320,7 +319,7 @@ usage(void) printf(_(" -p, --old-port=PORT old cluster port number (default %d)\n"), old_cluster.port); printf(_(" -P, --new-port=PORT new cluster port number (default %d)\n"), new_cluster.port); printf(_(" -r, --retain retain SQL and log files after success\n")); - printf(_(" -s, --socketdir=DIR socket directory to use (default CWD)\n")); + printf(_(" -s, --socketdir=DIR socket directory to use (default current dir.)\n")); printf(_(" -U, --username=NAME cluster superuser (default \"%s\")\n"), os_info.user); printf(_(" -v, --verbose enable verbose internal logging\n")); printf(_(" -V, --version display version information, then exit\n")); @@ -369,13 +368,15 @@ usage(void) * useCwd - true if OK to default to CWD * cmdLineOption - the command line option for this directory * description - a description of this directory option + * missingOk - true if OK that both dirpath and envVarName are not existing * * We use the last two arguments to construct a meaningful error message if the * user hasn't provided the required directory name. */ static void check_required_directory(char **dirpath, const char *envVarName, bool useCwd, - const char *cmdLineOption, const char *description) + const char *cmdLineOption, const char *description, + bool missingOk) { if (*dirpath == NULL || strlen(*dirpath) == 0) { @@ -391,6 +392,8 @@ check_required_directory(char **dirpath, const char *envVarName, bool useCwd, pg_fatal("could not determine current directory\n"); *dirpath = pg_strdup(cwd); } + else if (missingOk) + return; else pg_fatal("You must identify the directory where the %s.\n" "Please use the %s command-line option or the %s environment variable.\n", @@ -423,6 +426,7 @@ adjust_data_dir(ClusterInfo *cluster) cmd_output[MAX_STRING]; FILE *fp, *output; + int len; /* Initially assume config dir and data dir are the same */ cluster->pgconfig = pg_strdup(cluster->pgdata); @@ -463,9 +467,12 @@ adjust_data_dir(ClusterInfo *cluster) pclose(output); - /* Remove trailing newline */ - if (strchr(cmd_output, '\n') != NULL) - *strchr(cmd_output, '\n') = '\0'; + /* Remove trailing newline, handling Windows newlines as well */ + len = strlen(cmd_output); + while (len > 0 && + (cmd_output[len - 1] == '\n' || + cmd_output[len - 1] == '\r')) + cmd_output[--len] = '\0'; cluster->pgdata = pg_strdup(cmd_output); @@ -526,10 +533,15 @@ get_sock_dir(ClusterInfo *cluster, bool live_check) sscanf(line, "%hu", &old_cluster.port); if (lineno == LOCK_FILE_LINE_SOCKET_DIR) { + int len; + cluster->sockdir = pg_strdup(line); - /* strip off newline */ - if (strchr(cluster->sockdir, '\n') != NULL) - *strchr(cluster->sockdir, '\n') = '\0'; + /* strip off newline, handling Windows newlines as well */ + len = strlen(cluster->sockdir); + while (len > 0 && + (cluster->sockdir[len - 1] == '\n' || + cluster->sockdir[len - 1] == '\r')) + cluster->sockdir[--len] = '\0'; } } fclose(fp); diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c index 003ff36c85d9..658e9d53c9f2 100644 --- a/src/bin/pg_upgrade/pg_upgrade.c +++ b/src/bin/pg_upgrade/pg_upgrade.c @@ -379,14 +379,29 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char static void setup(char *argv0, bool *live_check) { - char exec_path[MAXPGPATH]; /* full path to my executable */ - /* * make sure the user has a clean environment, otherwise, we may confuse * libpq when we connect to one (or both) of the servers. */ check_pghost_envvar(); + /* + * In case the user hasn't specified the directory for the new binaries + * with -B, default to using the path of the currently executed pg_upgrade + * binary. + */ + if (!new_cluster.bindir) + { + char exec_path[MAXPGPATH]; + + if (find_my_exec(argv0, exec_path) < 0) + pg_fatal("%s: could not find own program executable\n", argv0); + /* Trim off program name and keep just path */ + *last_dir_separator(exec_path) = '\0'; + canonicalize_path(exec_path); + new_cluster.bindir = pg_strdup(exec_path); + } + verify_directories(); /* no postmasters should be running, except for a live check */ @@ -425,15 +440,6 @@ setup(char *argv0, bool *live_check) "Please shutdown that postmaster and try again.\n"); } } - - /* get path to pg_upgrade executable */ - if (find_my_exec(argv0, exec_path) < 0) - pg_fatal("%s: could not find own program executable\n", argv0); - - /* Trim off program name and keep just path */ - *last_dir_separator(exec_path) = '\0'; - canonicalize_path(exec_path); - os_info.exec_path = pg_strdup(exec_path); } @@ -442,7 +448,7 @@ prepare_new_cluster(void) { /* * It would make more sense to freeze after loading the schema, but that - * would cause us to lose the frozenids restored by the load. We use + * would cause us to lose the frozenxids restored by the load. We use * --analyze so autovacuum doesn't update statistics later * * GPDB: after we've copied the master data directory to the segments, diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h index 5bcccf6b85d1..b32bc0c28a9b 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -24,11 +24,8 @@ #define USER_NAME_SIZE 128 #define MAX_STRING 1024 -#define LINE_ALLOC 4096 #define QUERY_ALLOC 8192 -#define MIGRATOR_API_VERSION 1 - #define MESSAGE_WIDTH 60 #define GET_MAJOR_VERSION(v) ((v) / 100) @@ -421,7 +418,6 @@ typedef struct typedef struct { const char *progname; /* complete pathname for this program */ - char *exec_path; /* full path to my executable */ char *user; /* username for clusters */ bool user_specified; /* user specified on command-line */ char **old_tablespaces; /* tablespaces */ diff --git a/src/bin/pg_upgrade/test.sh b/src/bin/pg_upgrade/test.sh index 78820247b352..7e44747e39de 100644 --- a/src/bin/pg_upgrade/test.sh +++ b/src/bin/pg_upgrade/test.sh @@ -220,7 +220,7 @@ PGDATA="$BASE_PGDATA" standard_initdb 'initdb' -pg_upgrade $PG_UPGRADE_OPTS -d "${PGDATA}.old" -D "$PGDATA" -b "$oldbindir" -B "$bindir" -p "$PGPORT" -P "$PGPORT" +pg_upgrade $PG_UPGRADE_OPTS -d "${PGDATA}.old" -D "$PGDATA" -b "$oldbindir" -p "$PGPORT" -P "$PGPORT" # make sure all directories and files have group permissions, on Unix hosts # Windows hosts don't support Unix-y permissions. diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index cd6f4036441a..9e051f9e3da4 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -657,6 +657,7 @@ usage(void) " --progress-timestamp use Unix epoch timestamps for progress\n" " --random-seed=SEED set random seed (\"time\", \"rand\", integer)\n" " --sampling-rate=NUM fraction of transactions to log (e.g., 0.01 for 1%%)\n" + " --show-script=NAME show builtin script code, then exit\n" "\nCommon options:\n" " -d, --debug print debugging output\n" " -h, --host=HOSTNAME database server host or socket directory\n" @@ -3960,32 +3961,48 @@ checkInitSteps(const char *initialize_steps) static void runInitSteps(const char *initialize_steps) { + PQExpBufferData stats; PGconn *con; const char *step; + double run_time = 0.0; + bool first = true; + + initPQExpBuffer(&stats); if ((con = doConnect()) == NULL) exit(1); for (step = initialize_steps; *step != '\0'; step++) { + instr_time start; + char *op = NULL; + + INSTR_TIME_SET_CURRENT(start); + switch (*step) { case 'd': + op = "drop tables"; initDropTables(con); break; case 't': + op = "create tables"; initCreateTables(con); break; case 'g': + op = "generate"; initGenerateData(con); break; case 'v': + op = "vacuum"; initVacuum(con); break; case 'p': + op = "primary keys"; initCreatePKeys(con); break; case 'f': + op = "foreign keys"; initCreateFKeys(con); break; case ' ': @@ -3996,10 +4013,30 @@ runInitSteps(const char *initialize_steps) PQfinish(con); exit(1); } + + if (op != NULL) + { + instr_time diff; + double elapsed_sec; + + INSTR_TIME_SET_CURRENT(diff); + INSTR_TIME_SUBTRACT(diff, start); + elapsed_sec = INSTR_TIME_GET_DOUBLE(diff); + + if (!first) + appendPQExpBufferStr(&stats, ", "); + else + first = false; + + appendPQExpBuffer(&stats, "%s %.2f s", op, elapsed_sec); + + run_time += elapsed_sec; + } } - fprintf(stderr, "done.\n"); + fprintf(stderr, "done in %.2f s (%s).\n", run_time, stats.data); PQfinish(con); + termPQExpBuffer(&stats); } /* @@ -4677,7 +4714,7 @@ listAvailableScripts(void) fprintf(stderr, "Available builtin scripts:\n"); for (i = 0; i < lengthof(builtin_script); i++) - fprintf(stderr, "\t%s\n", builtin_script[i].name); + fprintf(stderr, " %13s: %s\n", builtin_script[i].name, builtin_script[i].desc); fprintf(stderr, "\n"); } @@ -5118,6 +5155,7 @@ main(int argc, char **argv) {"foreign-keys", no_argument, NULL, 8}, {"random-seed", required_argument, NULL, 9}, {"use-unique-keys", no_argument, &use_unique_key, 1}, + {"show-script", required_argument, NULL, 10}, {NULL, 0, NULL, 0} }; @@ -5473,6 +5511,14 @@ main(int argc, char **argv) exit(1); } break; + case 10: /* list */ + { + const BuiltinScript *s = findBuiltin(optarg); + + fprintf(stderr, "-- %s: %s\n%s\n", s->name, s->desc, s->script); + exit(0); + } + break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl index dc2c72fa9276..b82d3f65c4f4 100644 --- a/src/bin/pgbench/t/001_pgbench_with_server.pl +++ b/src/bin/pgbench/t/001_pgbench_with_server.pl @@ -58,27 +58,20 @@ sub pgbench return; } -# Test concurrent insertion into table with serial column. This -# indirectly exercises LWLock and spinlock concurrency. This test -# makes a 5-MiB table. - -$node->safe_psql('postgres', - 'CREATE UNLOGGED TABLE insert_tbl (id serial primary key); '); - +# Test concurrent OID generation via pg_enum_oid_index. This indirectly +# exercises LWLock and spinlock concurrency. +my $labels = join ',', map { "'l$_'" } 1 .. 1000; pgbench( '--no-vacuum --client=5 --protocol=prepared --transactions=25', 0, [qr{processed: 125/125}], [qr{^$}], - 'concurrent insert workload', + 'concurrent OID generation', { '001_pgbench_concurrent_insert' => - 'INSERT INTO insert_tbl SELECT FROM generate_series(1,1000);' + "CREATE TYPE pg_temp.e AS ENUM ($labels); DROP TYPE pg_temp.e;" }); -# cleanup -$node->safe_psql('postgres', 'DROP TABLE insert_tbl;'); - # Trigger various connection errors pgbench( 'no-such-database', @@ -101,7 +94,7 @@ sub pgbench [qr{^$}], [ qr{creating tables}, qr{vacuuming}, - qr{creating primary keys}, qr{done\.} + qr{creating primary keys}, qr{done in \d+\.\d\d s } ], 'pgbench scale 1 initialization',); @@ -116,7 +109,8 @@ sub pgbench qr{vacuuming}, qr{creating primary keys}, qr{creating foreign keys}, - qr{done\.} + qr{(?!vacuuming)}, # no vacuum + qr{done in \d+\.\d\d s } ], 'pgbench scale 1 initialization'); @@ -131,7 +125,8 @@ sub pgbench qr{creating primary keys}, qr{.* of .* tuples \(.*\) done}, qr{creating foreign keys}, - qr{done\.} + qr{(?!vacuuming)}, # no vacuum + qr{done in \d+\.\d\d s } ], 'pgbench --init-steps'); @@ -517,7 +512,7 @@ sub pgbench qr{processed: 1/1}, qr{shell-echo-output} ], - [qr{command=8.: int 2\b}], + [qr{command=8.: int 1\b}], 'pgbench backslash commands', { '001_pgbench_backslash_commands' => q{-- run set @@ -529,10 +524,10 @@ sub pgbench \sleep 0 s \sleep :zero -- setshell and continuation -\setshell two\ - expr \ - 1 + :one -\set n debug(:two) +\setshell another_one\ + echo \ + :one +\set n debug(:another_one) -- shell \shell echo shell-echo-output } diff --git a/src/bin/pgbench/t/002_pgbench_no_server.pl b/src/bin/pgbench/t/002_pgbench_no_server.pl index 69a6d03bb3f8..f7fa18418b42 100644 --- a/src/bin/pgbench/t/002_pgbench_no_server.pl +++ b/src/bin/pgbench/t/002_pgbench_no_server.pl @@ -218,6 +218,15 @@ sub pgbench_scripts ], 'pgbench builtin list'); +# builtin listing +pgbench( + '--show-script se', + 0, + [qr{^$}], + [ qr{select-only: }, qr{SELECT abalance FROM pgbench_accounts WHERE}, + qr{(?!UPDATE)}, qr{(?!INSERT)} ], + 'pgbench builtin listing'); + my @script_tests = ( # name, err, { file => contents } diff --git a/src/bin/pgevent/README b/src/bin/pgevent/README index 3d0329ec3300..10ec8d2d62be 100644 --- a/src/bin/pgevent/README +++ b/src/bin/pgevent/README @@ -3,7 +3,7 @@ src/bin/pgevent/README pgevent ======= -MSG000001.bin is a binary file, result of Microsoft MC compiler. MC compiler +MSG00001.bin is a binary file, result of Microsoft MC compiler. MC compiler can be downloaded for free with MS Core SDK but it is not included with MSYS tools and I didn't find an alternative way to compile MC file. diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index f8c1811295b8..9e0806506f62 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -319,7 +319,8 @@ exec_command(const char *cmd, status = exec_command_ef_ev(scan_state, active_branch, query_buf, true); else if (strcmp(cmd, "ev") == 0) status = exec_command_ef_ev(scan_state, active_branch, query_buf, false); - else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0) + else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0 || + strcmp(cmd, "warn") == 0) status = exec_command_echo(scan_state, active_branch, cmd); else if (strcmp(cmd, "elif") == 0) status = exec_command_elif(scan_state, cstack, query_buf); @@ -1114,7 +1115,7 @@ exec_command_ef_ev(PsqlScanState scan_state, bool active_branch, } /* - * \echo and \qecho -- echo arguments to stdout or query output + * \echo, \qecho, and \warn -- echo arguments to stdout, query output, or stderr */ static backslashResult exec_command_echo(PsqlScanState scan_state, bool active_branch, const char *cmd) @@ -1129,13 +1130,15 @@ exec_command_echo(PsqlScanState scan_state, bool active_branch, const char *cmd) if (strcmp(cmd, "qecho") == 0) fout = pset.queryFout; + else if (strcmp(cmd, "warn") == 0) + fout = stderr; else fout = stdout; while ((value = psql_scan_slash_option(scan_state, OT_NORMAL, "ed, false))) { - if (!quoted && strcmp(value, "-n") == 0) + if (first && !no_newline && !quoted && strcmp(value, "-n") == 0) no_newline = true; else { @@ -2992,7 +2995,7 @@ do_connect(enum trivalue reuse_previous_specification, if (!dbname && reuse_previous) { initPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, PQdb(o_conn)); dbname = connstr.data; /* has_connection_string=true would be a dead store */ @@ -4576,7 +4579,7 @@ lookup_object_oid(EditableObjectType obj_type, const char *desc, */ appendPQExpBufferStr(query, "SELECT "); appendStringLiteralConn(query, desc, pset.db); - appendPQExpBuffer(query, "::pg_catalog.regclass::pg_catalog.oid"); + appendPQExpBufferStr(query, "::pg_catalog.regclass::pg_catalog.oid"); break; } diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index f9af2df01043..f2698a69ca37 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -2475,37 +2475,32 @@ describeOneTableDetails(const char *schemaname, } /* Make footers */ - if (pset.sversion >= 100000) + + if (tableinfo.ispartition) { - /* Get the partition information */ + /* Footer information for a partition child table */ PGresult *result; - char *parent_name; - char *partdef; - char *partconstraintdef = NULL; printfPQExpBuffer(&buf, "SELECT inhparent::pg_catalog.regclass,\n" - " pg_catalog.pg_get_expr(c.relpartbound, inhrelid)"); + " pg_catalog.pg_get_expr(c.relpartbound, c.oid)"); /* If verbose, also request the partition constraint definition */ if (verbose) - appendPQExpBuffer(&buf, - ",\n pg_catalog.pg_get_partition_constraintdef(inhrelid)"); + appendPQExpBufferStr(&buf, + ",\n pg_catalog.pg_get_partition_constraintdef(c.oid)"); appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c" " JOIN pg_catalog.pg_inherits i" " ON c.oid = inhrelid" - "\nWHERE c.oid = '%s' AND c.relispartition;", oid); + "\nWHERE c.oid = '%s';", oid); result = PSQLexec(buf.data); if (!result) goto error_return; if (PQntuples(result) > 0) { - parent_name = PQgetvalue(result, 0, 0); - partdef = PQgetvalue(result, 0, 1); - - if (PQnfields(result) == 3 && !PQgetisnull(result, 0, 2)) - partconstraintdef = PQgetvalue(result, 0, 2); + char *parent_name = PQgetvalue(result, 0, 0); + char *partdef = PQgetvalue(result, 0, 1); printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s"), parent_name, partdef); @@ -2513,6 +2508,10 @@ describeOneTableDetails(const char *schemaname, if (verbose) { + char *partconstraintdef = NULL; + + if (!PQgetisnull(result, 0, 2)) + partconstraintdef = PQgetvalue(result, 0, 2); /* If there isn't any constraint, show that explicitly */ if (partconstraintdef == NULL || partconstraintdef[0] == '\0') printfPQExpBuffer(&tmpbuf, _("No partition constraint")); @@ -2521,27 +2520,56 @@ describeOneTableDetails(const char *schemaname, partconstraintdef); printTableAddFooter(&cont, tmpbuf.data); } - - PQclear(result); } + PQclear(result); } if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE) { - /* Get the partition key information */ + /* Footer information for a partitioned table (partitioning parent) */ PGresult *result; - char *partkeydef; printfPQExpBuffer(&buf, "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", oid); result = PSQLexec(buf.data); - if (!result || PQntuples(result) != 1) + if (!result) + goto error_return; + + if (PQntuples(result) == 1) + { + char *partkeydef = PQgetvalue(result, 0, 0); + + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + } + PQclear(result); + } + + if (tableinfo.relkind == RELKIND_TOASTVALUE) + { + /* For a TOAST table, print name of owning table */ + PGresult *result; + + printfPQExpBuffer(&buf, + "SELECT n.nspname, c.relname\n" + "FROM pg_catalog.pg_class c" + " JOIN pg_catalog.pg_namespace n" + " ON n.oid = c.relnamespace\n" + "WHERE reltoastrelid = '%s';", oid); + result = PSQLexec(buf.data); + if (!result) goto error_return; - partkeydef = PQgetvalue(result, 0, 0); - printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); - printTableAddFooter(&cont, tmpbuf.data); + if (PQntuples(result) == 1) + { + char *schemaname = PQgetvalue(result, 0, 0); + char *relname = PQgetvalue(result, 0, 1); + + printfPQExpBuffer(&tmpbuf, _("Owning table: \"%s.%s\""), + schemaname, relname); + printTableAddFooter(&cont, tmpbuf.data); + } PQclear(result); } @@ -2576,9 +2604,9 @@ describeOneTableDetails(const char *schemaname, " false AS condeferrable, false AS condeferred,\n"); if (pset.sversion >= 90400) - appendPQExpBuffer(&buf, "i.indisreplident,\n"); + appendPQExpBufferStr(&buf, "i.indisreplident,\n"); else - appendPQExpBuffer(&buf, "false AS indisreplident,\n"); + appendPQExpBufferStr(&buf, "false AS indisreplident,\n"); appendPQExpBuffer(&buf, " a.amname, c2.relname, " "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n" @@ -2636,19 +2664,27 @@ describeOneTableDetails(const char *schemaname, appendPQExpBufferStr(&tmpbuf, _(", initially deferred")); if (strcmp(indisreplident, "t") == 0) - appendPQExpBuffer(&tmpbuf, _(", replica identity")); + appendPQExpBufferStr(&tmpbuf, _(", replica identity")); printTableAddFooter(&cont, tmpbuf.data); - add_tablespace_footer(&cont, tableinfo.relkind, - tableinfo.tablespace, true); + + /* + * If it's a partitioned index, we'll print the tablespace below + */ + if (tableinfo.relkind == RELKIND_INDEX) + add_tablespace_footer(&cont, tableinfo.relkind, + tableinfo.tablespace, true); } PQclear(result); } + /* If you add relkinds here, see also "Finish printing..." stanza below */ else if (tableinfo.relkind == RELKIND_RELATION || tableinfo.relkind == RELKIND_MATVIEW || tableinfo.relkind == RELKIND_FOREIGN_TABLE || tableinfo.relkind == RELKIND_PARTITIONED_TABLE || + tableinfo.relkind == RELKIND_PARTITIONED_INDEX || + tableinfo.relkind == RELKIND_TOASTVALUE || tableinfo.relkind == RELKIND_AOSEGMENTS || tableinfo.relkind == RELKIND_AOBLOCKDIR || tableinfo.relkind == RELKIND_AOVISIMAP) @@ -2710,7 +2746,7 @@ describeOneTableDetails(const char *schemaname, " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n"); appendPQExpBuffer(&buf, "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n" - "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname;", + "ORDER BY i.indisprimary DESC, c2.relname;", oid); result = PSQLexec(buf.data); if (!result) @@ -2772,7 +2808,7 @@ describeOneTableDetails(const char *schemaname, appendPQExpBufferStr(&buf, " INVALID"); if (strcmp(PQgetvalue(result, i, 10), "t") == 0) - appendPQExpBuffer(&buf, " REPLICA IDENTITY"); + appendPQExpBufferStr(&buf, " REPLICA IDENTITY"); printTableAddFooter(&cont, buf.data); @@ -2855,8 +2891,8 @@ describeOneTableDetails(const char *schemaname, oid); if (pset.sversion >= 120000) - appendPQExpBuffer(&buf, " AND conparentid = 0\n"); - appendPQExpBuffer(&buf, "ORDER BY conname"); + appendPQExpBufferStr(&buf, " AND conparentid = 0\n"); + appendPQExpBufferStr(&buf, "ORDER BY conname"); } result = PSQLexec(buf.data); @@ -2954,11 +2990,11 @@ describeOneTableDetails(const char *schemaname, { printfPQExpBuffer(&buf, "SELECT pol.polname,"); if (pset.sversion >= 100000) - appendPQExpBuffer(&buf, - " pol.polpermissive,\n"); + appendPQExpBufferStr(&buf, + " pol.polpermissive,\n"); else - appendPQExpBuffer(&buf, - " 't' as polpermissive,\n"); + appendPQExpBufferStr(&buf, + " 't' as polpermissive,\n"); appendPQExpBuffer(&buf, " CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(select rolname from pg_catalog.pg_roles where oid = any (pol.polroles) order by 1),',') END,\n" " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n" @@ -3006,7 +3042,7 @@ describeOneTableDetails(const char *schemaname, PQgetvalue(result, i, 0)); if (*(PQgetvalue(result, i, 1)) == 'f') - appendPQExpBuffer(&buf, " AS RESTRICTIVE"); + appendPQExpBufferStr(&buf, " AS RESTRICTIVE"); if (!PQgetisnull(result, i, 5)) appendPQExpBuffer(&buf, " FOR %s", @@ -3311,12 +3347,12 @@ describeOneTableDetails(const char *schemaname, "t.tgconstraint <> 0 AS tgisinternal" : "false AS tgisinternal"), oid); if (pset.sversion >= 110000) - appendPQExpBuffer(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n" - " OR EXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n" - " AND refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass))"); + appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n" + " OR EXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n" + " AND refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass))"); else if (pset.sversion >= 90000) /* display/warn about disabled internal triggers */ - appendPQExpBuffer(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))"); + appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))"); else if (pset.sversion >= 80300) appendPQExpBufferStr(&buf, "(t.tgconstraint = 0 OR (t.tgconstraint <> 0 AND t.tgenabled = 'D'))"); else @@ -3448,11 +3484,18 @@ describeOneTableDetails(const char *schemaname, if (tableinfo.relkind == RELKIND_RELATION || tableinfo.relkind == RELKIND_MATVIEW || tableinfo.relkind == RELKIND_FOREIGN_TABLE || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE) + tableinfo.relkind == RELKIND_PARTITIONED_TABLE || + tableinfo.relkind == RELKIND_PARTITIONED_INDEX || + tableinfo.relkind == RELKIND_TOASTVALUE) { + bool is_partitioned; PGresult *result; int tuples; + /* simplify some repeated tests below */ + is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE || + tableinfo.relkind == RELKIND_PARTITIONED_INDEX); + /* print foreign server name */ if (tableinfo.relkind == RELKIND_FOREIGN_TABLE) { @@ -3496,13 +3539,15 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } - /* print inherited tables (exclude, if parent is a partitioned table) */ + /* print tables inherited from (exclude partitioned parents) */ printfPQExpBuffer(&buf, - "SELECT c.oid::pg_catalog.regclass" - " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i" - " WHERE c.oid=i.inhparent AND i.inhrelid = '%s'" - " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE) - " ORDER BY inhseqno;", oid); + "SELECT c.oid::pg_catalog.regclass\n" + "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" + "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n" + " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE) + " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX) + "\nORDER BY inhseqno;", + oid); result = PSQLexec(buf.data); if (!result) @@ -3534,31 +3579,32 @@ describeOneTableDetails(const char *schemaname, /* print child tables (with additional info if partitions) */ if (pset.sversion >= 100000) printfPQExpBuffer(&buf, - "SELECT c.oid::pg_catalog.regclass," - " pg_catalog.pg_get_expr(c.relpartbound, c.oid)," - " c.relkind" - " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i" - " WHERE c.oid=i.inhrelid AND i.inhparent = '%s'" - " ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT'," - " c.oid::pg_catalog.regclass::pg_catalog.text;", oid); + "SELECT c.oid::pg_catalog.regclass, c.relkind," + " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n" + "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" + "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" + "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT'," + " c.oid::pg_catalog.regclass::pg_catalog.text;", + oid); else if (pset.sversion >= 80300) printfPQExpBuffer(&buf, - "SELECT c.oid::pg_catalog.regclass" - " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i" - " WHERE c.oid=i.inhrelid AND i.inhparent = '%s'" - " ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid); + "SELECT c.oid::pg_catalog.regclass, c.relkind, NULL\n" + "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" + "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" + "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", + oid); else printfPQExpBuffer(&buf, - "SELECT c.oid::pg_catalog.regclass" - " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i" - " WHERE c.oid=i.inhrelid AND i.inhparent = '%s'" - " ORDER BY c.relname;", oid); + "SELECT c.oid::pg_catalog.regclass, c.relkind, NULL\n" + "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" + "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n" + "ORDER BY c.relname;", + oid); result = PSQLexec(buf.data); if (!result) goto error_return; - else - tuples = PQntuples(result); + tuples = PQntuples(result); /* * For a partitioned table with no partitions, always print the number @@ -3566,7 +3612,7 @@ describeOneTableDetails(const char *schemaname, * Otherwise, we will not print "Partitions" section for a partitioned * table without any partitions. */ - if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE && tuples == 0) + if (is_partitioned && tuples == 0) { printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples); printTableAddFooter(&cont, buf.data); @@ -3576,49 +3622,34 @@ describeOneTableDetails(const char *schemaname, /* print the number of child tables, if any */ if (tuples > 0) { - if (tableinfo.relkind != RELKIND_PARTITIONED_TABLE) - printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples); - else + if (is_partitioned) printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples); + else + printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples); printTableAddFooter(&cont, buf.data); } } else { /* display the list of child tables */ - const char *ct = (tableinfo.relkind != RELKIND_PARTITIONED_TABLE) ? - _("Child tables") : _("Partitions"); + const char *ct = is_partitioned ? _("Partitions") : _("Child tables"); int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding); for (i = 0; i < tuples; i++) { - if (tableinfo.relkind != RELKIND_PARTITIONED_TABLE) - { - if (i == 0) - printfPQExpBuffer(&buf, "%s: %s", - ct, PQgetvalue(result, i, 0)); - else - printfPQExpBuffer(&buf, "%*s %s", - ctw, "", PQgetvalue(result, i, 0)); - } - else - { - char *partitioned_note; - - if (*PQgetvalue(result, i, 2) == RELKIND_PARTITIONED_TABLE) - partitioned_note = ", PARTITIONED"; - else - partitioned_note = ""; + char child_relkind = *PQgetvalue(result, i, 1); - if (i == 0) - printfPQExpBuffer(&buf, "%s: %s %s%s", - ct, PQgetvalue(result, i, 0), PQgetvalue(result, i, 1), - partitioned_note); - else - printfPQExpBuffer(&buf, "%*s %s %s%s", - ctw, "", PQgetvalue(result, i, 0), PQgetvalue(result, i, 1), - partitioned_note); - } + if (i == 0) + printfPQExpBuffer(&buf, "%s: %s", + ct, PQgetvalue(result, i, 0)); + else + printfPQExpBuffer(&buf, "%*s %s", + ctw, "", PQgetvalue(result, i, 0)); + if (!PQgetisnull(result, i, 2)) + appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 2)); + if (child_relkind == RELKIND_PARTITIONED_TABLE || + child_relkind == RELKIND_PARTITIONED_INDEX) + appendPQExpBufferStr(&buf, ", PARTITIONED"); if (i < tuples - 1) appendPQExpBufferChar(&buf, ','); @@ -4245,7 +4276,8 @@ add_tablespace_footer(printTableContent *const cont, char relkind, relkind == RELKIND_MATVIEW || relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_TABLE || - relkind == RELKIND_PARTITIONED_INDEX) + relkind == RELKIND_PARTITIONED_INDEX || + relkind == RELKIND_TOASTVALUE) { /* * We ignore the database default tablespace so that users not using @@ -4590,7 +4622,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, true, false, false /* Storage */, false, false, false, false}; + int cols_so_far; + bool translate_columns[] = {false, false, true, false, false /* Storage */, false, false, false, false, false}; /* If tabtypes is empty, we default to \dtvmsE (but see also command.c) */ if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign)) @@ -4638,6 +4671,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("partitioned index"), gettext_noop("Type"), gettext_noop("Owner")); + cols_so_far = 4; /* Show Storage type for tables */ if (showTables && isGPDB()) @@ -4662,12 +4696,36 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys } if (showIndexes) + { appendPQExpBuffer(&buf, - ",\n c2.relname as \"%s\"", + ",\n c2.relname as \"%s\"", gettext_noop("Table")); + cols_so_far++; + } if (verbose) { + /* + * Show whether a relation is permanent, temporary, or unlogged. Like + * describeOneTableDetails(), we consider that persistence emerged in + * v9.1, even though related concepts existed before. + */ + if (pset.sversion >= 90100) + { + appendPQExpBuffer(&buf, + ",\n CASE c.relpersistence WHEN 'p' THEN '%s' WHEN 't' THEN '%s' WHEN 'u' THEN '%s' END as \"%s\"", + gettext_noop("permanent"), + gettext_noop("temporary"), + gettext_noop("unlogged"), + gettext_noop("Persistence")); + translate_columns[cols_so_far] = true; + } + + /* + * We don't bother to count cols_so_far below here, as there's no need + * to; this might change with future additions to the output columns. + */ + /* * As of PostgreSQL 9.0, use pg_table_size() to show a more accurate * size of a table, including FSM, VM and TOAST tables. @@ -4924,33 +4982,33 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose) { if (pset.sversion < 120000) { - appendPQExpBuffer(&buf, - ",\n LATERAL (WITH RECURSIVE d\n" - " AS (SELECT inhrelid AS oid, 1 AS level\n" - " FROM pg_catalog.pg_inherits\n" - " WHERE inhparent = c.oid\n" - " UNION ALL\n" - " SELECT inhrelid, level + 1\n" - " FROM pg_catalog.pg_inherits i\n" - " JOIN d ON i.inhparent = d.oid)\n" - " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size(" - "d.oid))) AS tps,\n" - " pg_catalog.pg_size_pretty(sum(" - "\n CASE WHEN d.level = 1" - " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n" - " FROM d) s"); + appendPQExpBufferStr(&buf, + ",\n LATERAL (WITH RECURSIVE d\n" + " AS (SELECT inhrelid AS oid, 1 AS level\n" + " FROM pg_catalog.pg_inherits\n" + " WHERE inhparent = c.oid\n" + " UNION ALL\n" + " SELECT inhrelid, level + 1\n" + " FROM pg_catalog.pg_inherits i\n" + " JOIN d ON i.inhparent = d.oid)\n" + " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size(" + "d.oid))) AS tps,\n" + " pg_catalog.pg_size_pretty(sum(" + "\n CASE WHEN d.level = 1" + " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n" + " FROM d) s"); } else { /* PostgreSQL 12 has pg_partition_tree function */ - appendPQExpBuffer(&buf, - ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum(" - "\n CASE WHEN ppt.isleaf AND ppt.level = 1" - "\n THEN pg_catalog.pg_table_size(ppt.relid)" - " ELSE 0 END)) AS dps" - ",\n pg_catalog.pg_size_pretty(sum(" - "pg_catalog.pg_table_size(ppt.relid))) AS tps" - "\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s"); + appendPQExpBufferStr(&buf, + ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum(" + "\n CASE WHEN ppt.isleaf AND ppt.level = 1" + "\n THEN pg_catalog.pg_table_size(ppt.relid)" + " ELSE 0 END)) AS dps" + ",\n pg_catalog.pg_size_pretty(sum(" + "pg_catalog.pg_table_size(ppt.relid))) AS tps" + "\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s"); } } @@ -4992,7 +5050,7 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose) return false; initPQExpBuffer(&title); - appendPQExpBuffer(&title, "%s", tabletitle); + appendPQExpBufferStr(&title, tabletitle); myopt.nullPrint = NULL; myopt.title = title.data; @@ -5556,8 +5614,8 @@ listSchemas(const char *pattern, bool verbose, bool showSystem) gettext_noop("Description")); } - appendPQExpBuffer(&buf, - "\nFROM pg_catalog.pg_namespace n\n"); + appendPQExpBufferStr(&buf, + "\nFROM pg_catalog.pg_namespace n\n"); if (!showSystem && !pattern) appendPQExpBufferStr(&buf, @@ -6772,10 +6830,10 @@ describePublications(const char *pattern) " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n" " puballtables, pubinsert, pubupdate, pubdelete"); if (has_pubtruncate) - appendPQExpBuffer(&buf, - ", pubtruncate"); - appendPQExpBuffer(&buf, - "\nFROM pg_catalog.pg_publication\n"); + appendPQExpBufferStr(&buf, + ", pubtruncate"); + appendPQExpBufferStr(&buf, + "\nFROM pg_catalog.pg_publication\n"); processSQLNamePattern(pset.db, &buf, pattern, false, false, NULL, "pubname", NULL, diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 118720c7ede7..028cc952244f 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -171,7 +171,7 @@ slashUsage(unsigned short int pager) * Use "psql --help=commands | wc" to count correctly. It's okay to count * the USE_READLINE line even in builds without that. */ - output = PageOutput(127, pager ? &(pset.popt.topt) : NULL); + output = PageOutput(128, pager ? &(pset.popt.topt) : NULL); fprintf(output, _("General\n")); fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n")); @@ -208,11 +208,12 @@ slashUsage(unsigned short int pager) fprintf(output, _("Input/Output\n")); fprintf(output, _(" \\copy ... perform SQL COPY with data stream to the client host\n")); - fprintf(output, _(" \\echo [STRING] write string to standard output\n")); + fprintf(output, _(" \\echo [-n] [STRING] write string to standard output (-n for no newline)\n")); fprintf(output, _(" \\i FILE execute commands from file\n")); fprintf(output, _(" \\ir FILE as \\i, but relative to location of current script\n")); fprintf(output, _(" \\o [FILE] send all query results to file or |pipe\n")); - fprintf(output, _(" \\qecho [STRING] write string to query output stream (see \\o)\n")); + fprintf(output, _(" \\qecho [-n] [STRING] write string to \\o output stream (-n for no newline)\n")); + fprintf(output, _(" \\warn [-n] [STRING] write string to standard error (-n for no newline)\n")); fprintf(output, "\n"); fprintf(output, _("Conditional\n")); diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c index 4514cf8551c3..59afbc793a99 100644 --- a/src/bin/psql/prompt.c +++ b/src/bin/psql/prompt.c @@ -264,6 +264,7 @@ get_prompt(promptStatus_t status, ConditionalStack cstack) FILE *fd; char *file = pg_strdup(p + 1); int cmdend; + int buflen; cmdend = strcspn(file, "`"); file[cmdend] = '\0'; @@ -274,8 +275,10 @@ get_prompt(promptStatus_t status, ConditionalStack cstack) buf[0] = '\0'; pclose(fd); } - if (strlen(buf) > 0 && buf[strlen(buf) - 1] == '\n') - buf[strlen(buf) - 1] = '\0'; + buflen = strlen(buf); + while (buflen > 0 && (buf[buflen - 1] == '\n' || + buf[buflen - 1] == '\r')) + buf[--buflen] = '\0'; free(file); p += cmdend + 1; break; diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 7e98edcbdbbe..50b266702a05 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -207,19 +207,22 @@ do { \ matches = completion_matches(text, complete_from_versioned_schema_query); \ } while (0) +/* + * Caution: COMPLETE_WITH_CONST is not for general-purpose use; you probably + * want COMPLETE_WITH() with one element, instead. + */ +#define COMPLETE_WITH_CONST(cs, con) \ +do { \ + completion_case_sensitive = (cs); \ + completion_charp = (con); \ + matches = completion_matches(text, complete_from_const); \ +} while (0) + #define COMPLETE_WITH_LIST_INT(cs, list) \ do { \ completion_case_sensitive = (cs); \ - if (!(list)[1]) \ - { \ - completion_charp = (list)[0]; \ - matches = completion_matches(text, complete_from_const); \ - } \ - else \ - { \ - completion_charpp = (list); \ - matches = completion_matches(text, complete_from_list); \ - } \ + completion_charpp = (list); \ + matches = completion_matches(text, complete_from_list); \ } while (0) #define COMPLETE_WITH_LIST(list) COMPLETE_WITH_LIST_INT(false, list) @@ -1444,7 +1447,7 @@ psql_completion(const char *text, int start, int end) "\\t", "\\T", "\\timing", "\\unset", "\\x", - "\\w", "\\watch", + "\\w", "\\warn", "\\watch", "\\z", "\\!", "\\?", NULL @@ -2721,6 +2724,45 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_CREATERESOURCEGROUP); } +/* CREATE TYPE */ + else if (Matches("CREATE", "TYPE", MatchAny)) + COMPLETE_WITH("(", "AS"); + else if (Matches("CREATE", "TYPE", MatchAny, "AS")) + COMPLETE_WITH("ENUM", "RANGE", "("); + else if (HeadMatches("CREATE", "TYPE", MatchAny, "AS", "(")) + { + if (TailMatches("(|*,", MatchAny)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + else if (TailMatches("(|*,", MatchAny, MatchAnyExcept("*)"))) + COMPLETE_WITH("COLLATE", ",", ")"); + } + else if (Matches("CREATE", "TYPE", MatchAny, "AS", "ENUM|RANGE")) + COMPLETE_WITH("("); + else if (HeadMatches("CREATE", "TYPE", MatchAny, "(")) + { + if (TailMatches("(|*,")) + COMPLETE_WITH("INPUT", "OUTPUT", "RECEIVE", "SEND", + "TYPMOD_IN", "TYPMOD_OUT", "ANALYZE", + "INTERNALLENGTH", "PASSBYVALUE", "ALIGNMENT", + "STORAGE", "LIKE", "CATEGORY", "PREFERRED", + "DEFAULT", "ELEMENT", "DELIMITER", + "COLLATABLE"); + else if (TailMatches("(*|*,", MatchAnyExcept("*="))) + COMPLETE_WITH("="); + else if (TailMatches("=", MatchAnyExcept("*)"))) + COMPLETE_WITH(",", ")"); + } + else if (HeadMatches("CREATE", "TYPE", MatchAny, "AS", "RANGE", "(")) + { + if (TailMatches("(|*,")) + COMPLETE_WITH("SUBTYPE", "SUBTYPE_OPCLASS", "COLLATION", + "CANONICAL", "SUBTYPE_DIFF"); + else if (TailMatches("(*|*,", MatchAnyExcept("*="))) + COMPLETE_WITH("="); + else if (TailMatches("=", MatchAnyExcept("*)"))) + COMPLETE_WITH(",", ")"); + } + /* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */ /* Complete CREATE VIEW with AS */ @@ -3401,8 +3443,13 @@ psql_completion(const char *text, int start, int end) else if (HeadMatches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER") && TailMatches("SET", MatchAny)) COMPLETE_WITH("FROM CURRENT", "TO"); - /* Suggest possible variable values */ - else if (TailMatches("SET", MatchAny, "TO|=")) + + /* + * Suggest possible variable values in SET variable TO|=, along with the + * preceding ALTER syntaxes. + */ + else if (TailMatches("SET", MatchAny, "TO|=") && + !TailMatches("UPDATE", MatchAny, "SET", MatchAny, "TO|=")) { /* special cased code for individual GUCs */ if (TailMatches("DateStyle", "TO|=")) @@ -3420,21 +3467,29 @@ psql_completion(const char *text, int start, int end) /* generic, type based, GUC support */ char *guctype = get_guctype(prev2_wd); - if (guctype && strcmp(guctype, "enum") == 0) + /* + * Note: if we don't recognize the GUC name, it's important to not + * offer any completions, as most likely we've misinterpreted the + * context and this isn't a GUC-setting command at all. + */ + if (guctype) { - char querybuf[1024]; + if (strcmp(guctype, "enum") == 0) + { + char querybuf[1024]; - snprintf(querybuf, sizeof(querybuf), Query_for_enum, prev2_wd); - COMPLETE_WITH_QUERY(querybuf); - } - else if (guctype && strcmp(guctype, "bool") == 0) - COMPLETE_WITH("on", "off", "true", "false", "yes", "no", - "1", "0", "DEFAULT"); - else - COMPLETE_WITH("DEFAULT"); + snprintf(querybuf, sizeof(querybuf), + Query_for_enum, prev2_wd); + COMPLETE_WITH_QUERY(querybuf); + } + else if (strcmp(guctype, "bool") == 0) + COMPLETE_WITH("on", "off", "true", "false", "yes", "no", + "1", "0", "DEFAULT"); + else + COMPLETE_WITH("DEFAULT"); - if (guctype) free(guctype); + } } } @@ -3471,7 +3526,7 @@ psql_completion(const char *text, int start, int end) else if (TailMatches("UPDATE", MatchAny, "SET")) COMPLETE_WITH_ATTR(prev2_wd, ""); /* UPDATE SET = */ - else if (TailMatches("UPDATE", MatchAny, "SET", MatchAny)) + else if (TailMatches("UPDATE", MatchAny, "SET", MatchAnyExcept("*="))) COMPLETE_WITH("="); /* USER MAPPING */ @@ -3784,7 +3839,7 @@ psql_completion(const char *text, int start, int end) */ if (matches == NULL) { - COMPLETE_WITH(""); + COMPLETE_WITH_CONST(true, ""); #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rl_completion_append_character = '\0'; #endif @@ -4214,10 +4269,21 @@ complete_from_list(const char *text, int state) /* * This function returns one fixed string the first time even if it doesn't - * match what's there, and nothing the second time. This should be used if - * there is only one possibility that can appear at a certain spot, so - * misspellings will be overwritten. The string to be passed must be in - * completion_charp. + * match what's there, and nothing the second time. The string + * to be used must be in completion_charp. + * + * If the given string is "", this has the effect of preventing readline + * from doing any completion. (Without this, readline tries to do filename + * completion which is seldom the right thing.) + * + * If the given string is not empty, readline will replace whatever the + * user typed with that string. This behavior might be useful if it's + * completely certain that we know what must appear at a certain spot, + * so that it's okay to overwrite misspellings. In practice, given the + * relatively lame parsing technology used in this file, the level of + * certainty is seldom that high, so that you probably don't want to + * use this. Use complete_from_list with a one-element list instead; + * that won't try to auto-correct "misspellings". */ static char * complete_from_const(const char *text, int state) diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile index 9f352b5e2b26..ede665090f77 100755 --- a/src/bin/scripts/Makefile +++ b/src/bin/scripts/Makefile @@ -28,8 +28,8 @@ createuser: createuser.o common.o $(WIN32RES) | submake-libpq submake-libpgport dropdb: dropdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils dropuser: dropuser.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils clusterdb: clusterdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils -vacuumdb: vacuumdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils -reindexdb: reindexdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils +vacuumdb: vacuumdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils +reindexdb: reindexdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils pg_isready: pg_isready.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils install: all installdirs @@ -50,7 +50,7 @@ uninstall: clean distclean maintainer-clean: rm -f $(addsuffix $(X), $(PROGRAMS)) $(addsuffix .o, $(PROGRAMS)) - rm -f common.o $(WIN32RES) + rm -f common.o scripts_parallel.o $(WIN32RES) rm -rf tmp_check check: diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c index 0955f6caf050..4a2c37e8d475 100644 --- a/src/bin/scripts/clusterdb.c +++ b/src/bin/scripts/clusterdb.c @@ -206,7 +206,7 @@ cluster_one_database(const char *dbname, bool verbose, const char *table, if (table) { appendPQExpBufferChar(&sql, ' '); - appendQualifiedRelation(&sql, table, conn, progname, echo); + appendQualifiedRelation(&sql, table, conn, echo); } appendPQExpBufferChar(&sql, ';'); @@ -239,7 +239,7 @@ cluster_all_databases(bool verbose, const char *maintenance_db, conn = connectMaintenanceDatabase(maintenance_db, host, port, username, prompt_password, progname, echo); - result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", progname, echo); + result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", echo); PQfinish(conn); initPQExpBuffer(&connstr); @@ -254,7 +254,7 @@ cluster_all_databases(bool verbose, const char *maintenance_db, } resetPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, dbname); cluster_one_database(connstr.data, verbose, NULL, diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c index 544700f6178f..da3ac25829f5 100644 --- a/src/bin/scripts/common.c +++ b/src/bin/scripts/common.c @@ -22,6 +22,10 @@ #include "fe_utils/connect.h" #include "fe_utils/string_utils.h" +#define ERRCODE_UNDEFINED_TABLE "42P01" + + + static PGcancel *volatile cancelConn = NULL; bool CancelRequested = false; @@ -145,8 +149,7 @@ connectDatabase(const char *dbname, const char *pghost, exit(1); } - PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, - progname, echo)); + PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo)); return conn; } @@ -177,11 +180,35 @@ connectMaintenanceDatabase(const char *maintenance_db, return conn; } +/* + * Disconnect the given connection, canceling any statement if one is active. + */ +void +disconnectDatabase(PGconn *conn) +{ + char errbuf[256]; + + Assert(conn != NULL); + + if (PQtransactionStatus(conn) == PQTRANS_ACTIVE) + { + PGcancel *cancel; + + if ((cancel = PQgetCancel(conn))) + { + (void) PQcancel(cancel, errbuf, sizeof(errbuf)); + PQfreeCancel(cancel); + } + } + + PQfinish(conn); +} + /* * Run a query, return the results, exit program on failure. */ PGresult * -executeQuery(PGconn *conn, const char *query, const char *progname, bool echo) +executeQuery(PGconn *conn, const char *query, bool echo) { PGresult *res; @@ -206,8 +233,7 @@ executeQuery(PGconn *conn, const char *query, const char *progname, bool echo) * As above for a SQL command (which returns nothing). */ void -executeCommand(PGconn *conn, const char *query, - const char *progname, bool echo) +executeCommand(PGconn *conn, const char *query, bool echo) { PGresult *res; @@ -254,6 +280,57 @@ executeMaintenanceCommand(PGconn *conn, const char *query, bool echo) return r; } +/* + * Consume all the results generated for the given connection until + * nothing remains. If at least one error is encountered, return false. + * Note that this will block if the connection is busy. + */ +bool +consumeQueryResult(PGconn *conn) +{ + bool ok = true; + PGresult *result; + + SetCancelConn(conn); + while ((result = PQgetResult(conn)) != NULL) + { + if (!processQueryResult(conn, result)) + ok = false; + } + ResetCancelConn(); + return ok; +} + +/* + * Process (and delete) a query result. Returns true if there's no error, + * false otherwise -- but errors about trying to work on a missing relation + * are reported and subsequently ignored. + */ +bool +processQueryResult(PGconn *conn, PGresult *result) +{ + /* + * If it's an error, report it. Errors about a missing table are harmless + * so we continue processing; but die for other errors. + */ + if (PQresultStatus(result) != PGRES_COMMAND_OK) + { + char *sqlState = PQresultErrorField(result, PG_DIAG_SQLSTATE); + + pg_log_error("processing of database \"%s\" failed: %s", + PQdb(conn), PQerrorMessage(conn)); + + if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) != 0) + { + PQclear(result); + return false; + } + } + + PQclear(result); + return true; +} + /* * Split TABLE[(COLUMNS)] into TABLE and [(COLUMNS)] portions. When you @@ -298,7 +375,7 @@ splitTableColumnsSpec(const char *spec, int encoding, */ void appendQualifiedRelation(PQExpBuffer buf, const char *spec, - PGconn *conn, const char *progname, bool echo) + PGconn *conn, bool echo) { char *table; const char *columns; @@ -323,7 +400,7 @@ appendQualifiedRelation(PQExpBuffer buf, const char *spec, appendStringLiteralConn(&sql, table, conn); appendPQExpBufferStr(&sql, "::pg_catalog.regclass;"); - executeCommand(conn, "RESET search_path;", progname, echo); + executeCommand(conn, "RESET search_path;", echo); /* * One row is a typical result, as is a nonexistent relation ERROR. @@ -331,7 +408,7 @@ appendQualifiedRelation(PQExpBuffer buf, const char *spec, * relation has that OID; this query returns no rows. Catalog corruption * might elicit other row counts. */ - res = executeQuery(conn, sql.data, progname, echo); + res = executeQuery(conn, sql.data, echo); ntups = PQntuples(res); if (ntups != 1) { @@ -350,8 +427,7 @@ appendQualifiedRelation(PQExpBuffer buf, const char *spec, termPQExpBuffer(&sql); pg_free(table); - PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, - progname, echo)); + PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo)); } diff --git a/src/bin/scripts/common.h b/src/bin/scripts/common.h index ae0702656d15..aab5916d7298 100644 --- a/src/bin/scripts/common.h +++ b/src/bin/scripts/common.h @@ -39,20 +39,24 @@ extern PGconn *connectMaintenanceDatabase(const char *maintenance_db, const char *pguser, enum trivalue prompt_password, const char *progname, bool echo); -extern PGresult *executeQuery(PGconn *conn, const char *query, - const char *progname, bool echo); +extern void disconnectDatabase(PGconn *conn); -extern void executeCommand(PGconn *conn, const char *query, - const char *progname, bool echo); +extern PGresult *executeQuery(PGconn *conn, const char *query, bool echo); + +extern void executeCommand(PGconn *conn, const char *query, bool echo); extern bool executeMaintenanceCommand(PGconn *conn, const char *query, bool echo); +extern bool consumeQueryResult(PGconn *conn); + +extern bool processQueryResult(PGconn *conn, PGresult *result); + extern void splitTableColumnsSpec(const char *spec, int encoding, char **table, const char **columns); extern void appendQualifiedRelation(PQExpBuffer buf, const char *name, - PGconn *conn, const char *progname, bool echo); + PGconn *conn, bool echo); extern void appendQualifiedRelation(PQExpBuffer buf, const char *name, PGconn *conn, const char *progname, bool echo); diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index 6fc8e6d94922..973de017a0a3 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -10,27 +10,45 @@ */ #include "postgres_fe.h" + +#include "catalog/pg_class_d.h" #include "common.h" #include "common/logging.h" +#include "fe_utils/connect.h" #include "fe_utils/simple_list.h" #include "fe_utils/string_utils.h" +#include "scripts_parallel.h" - -static void reindex_one_database(const char *name, const char *dbname, - const char *type, const char *host, +typedef enum ReindexType +{ + REINDEX_DATABASE, + REINDEX_INDEX, + REINDEX_SCHEMA, + REINDEX_SYSTEM, + REINDEX_TABLE +} ReindexType; + + +static SimpleStringList *get_parallel_object_list(PGconn *conn, + ReindexType type, + SimpleStringList *user_list, + bool echo); +static void reindex_one_database(const char *dbname, ReindexType type, + SimpleStringList *user_list, const char *host, const char *port, const char *username, enum trivalue prompt_password, const char *progname, - bool echo, bool verbose, bool concurrently); + bool echo, bool verbose, bool concurrently, + int concurrentCons); static void reindex_all_databases(const char *maintenance_db, const char *host, const char *port, const char *username, enum trivalue prompt_password, const char *progname, bool echo, - bool quiet, bool verbose, bool concurrently); -static void reindex_system_catalogs(const char *dbname, - const char *host, const char *port, - const char *username, enum trivalue prompt_password, - const char *progname, bool echo, bool verbose, - bool concurrently); + bool quiet, bool verbose, bool concurrently, + int concurrentCons); +static void run_reindex_command(PGconn *conn, ReindexType type, + const char *name, bool echo, bool verbose, + bool concurrently, bool async); + static void help(const char *progname); int @@ -50,6 +68,7 @@ main(int argc, char *argv[]) {"system", no_argument, NULL, 's'}, {"table", required_argument, NULL, 't'}, {"index", required_argument, NULL, 'i'}, + {"jobs", required_argument, NULL, 'j'}, {"verbose", no_argument, NULL, 'v'}, {"concurrently", no_argument, NULL, 1}, {"maintenance-db", required_argument, NULL, 2}, @@ -75,6 +94,7 @@ main(int argc, char *argv[]) SimpleStringList indexes = {NULL, NULL}; SimpleStringList tables = {NULL, NULL}; SimpleStringList schemas = {NULL, NULL}; + int concurrentCons = 1; pg_logging_init(argv[0]); progname = get_progname(argv[0]); @@ -83,7 +103,7 @@ main(int argc, char *argv[]) handle_help_version_opts(argc, argv, "reindexdb", help); /* process command-line options */ - while ((c = getopt_long(argc, argv, "h:p:U:wWeqS:d:ast:i:v", long_options, &optindex)) != -1) + while ((c = getopt_long(argc, argv, "h:p:U:wWeqS:d:ast:i:j:v", long_options, &optindex)) != -1) { switch (c) { @@ -126,6 +146,20 @@ main(int argc, char *argv[]) case 'i': simple_string_list_append(&indexes, optarg); break; + case 'j': + concurrentCons = atoi(optarg); + if (concurrentCons <= 0) + { + pg_log_error("number of parallel jobs must be at least 1"); + exit(1); + } + if (concurrentCons > FD_SETSIZE - 1) + { + pg_log_error("too many parallel jobs requested (maximum: %d)", + FD_SETSIZE - 1); + exit(1); + } + break; case 'v': verbose = true; break; @@ -190,7 +224,8 @@ main(int argc, char *argv[]) } reindex_all_databases(maintenance_db, host, port, username, - prompt_password, progname, echo, quiet, verbose, concurrently); + prompt_password, progname, echo, quiet, verbose, + concurrently, concurrentCons); } else if (syscatalog) { @@ -210,6 +245,12 @@ main(int argc, char *argv[]) exit(1); } + if (concurrentCons > 1) + { + pg_log_error("cannot use multiple jobs to reindex system catalogs"); + exit(1); + } + if (dbname == NULL) { if (getenv("PGDATABASE")) @@ -220,11 +261,23 @@ main(int argc, char *argv[]) dbname = get_user_name_or_exit(progname); } - reindex_system_catalogs(dbname, host, port, username, prompt_password, - progname, echo, verbose, concurrently); + reindex_one_database(dbname, REINDEX_SYSTEM, NULL, host, + port, username, prompt_password, progname, + echo, verbose, concurrently, 1); } else { + /* + * Index-level REINDEX is not supported with multiple jobs as we + * cannot control the concurrent processing of multiple indexes + * depending on the same relation. + */ + if (concurrentCons > 1 && indexes.head != NULL) + { + pg_log_error("cannot use multiple jobs to reindex indexes"); + exit(1); + } + if (dbname == NULL) { if (getenv("PGDATABASE")) @@ -236,58 +289,49 @@ main(int argc, char *argv[]) } if (schemas.head != NULL) - { - SimpleStringListCell *cell; - - for (cell = schemas.head; cell; cell = cell->next) - { - reindex_one_database(cell->val, dbname, "SCHEMA", host, port, - username, prompt_password, progname, echo, verbose, concurrently); - } - } + reindex_one_database(dbname, REINDEX_SCHEMA, &schemas, host, + port, username, prompt_password, progname, + echo, verbose, concurrently, concurrentCons); if (indexes.head != NULL) - { - SimpleStringListCell *cell; + reindex_one_database(dbname, REINDEX_INDEX, &indexes, host, + port, username, prompt_password, progname, + echo, verbose, concurrently, 1); - for (cell = indexes.head; cell; cell = cell->next) - { - reindex_one_database(cell->val, dbname, "INDEX", host, port, - username, prompt_password, progname, echo, verbose, concurrently); - } - } if (tables.head != NULL) - { - SimpleStringListCell *cell; - - for (cell = tables.head; cell; cell = cell->next) - { - reindex_one_database(cell->val, dbname, "TABLE", host, port, - username, prompt_password, progname, echo, verbose, concurrently); - } - } + reindex_one_database(dbname, REINDEX_TABLE, &tables, host, + port, username, prompt_password, progname, + echo, verbose, concurrently, + concurrentCons); /* * reindex database only if neither index nor table nor schema is * specified */ if (indexes.head == NULL && tables.head == NULL && schemas.head == NULL) - reindex_one_database(NULL, dbname, "DATABASE", host, port, - username, prompt_password, progname, echo, verbose, concurrently); + reindex_one_database(dbname, REINDEX_DATABASE, NULL, host, + port, username, prompt_password, progname, + echo, verbose, concurrently, concurrentCons); } exit(0); } static void -reindex_one_database(const char *name, const char *dbname, const char *type, - const char *host, const char *port, const char *username, +reindex_one_database(const char *dbname, ReindexType type, + SimpleStringList *user_list, const char *host, + const char *port, const char *username, enum trivalue prompt_password, const char *progname, bool echo, - bool verbose, bool concurrently) + bool verbose, bool concurrently, int concurrentCons) { - PQExpBufferData sql; - PGconn *conn; + SimpleStringListCell *cell; + bool parallel = concurrentCons > 1; + SimpleStringList *process_list = user_list; + ReindexType process_type = type; + ParallelSlot *slots; + bool failed = false; + int items_count = 0; conn = connectDatabase(dbname, host, port, username, prompt_password, progname, echo, false, false); @@ -304,6 +348,158 @@ reindex_one_database(const char *name, const char *dbname, const char *type, exit(1); } + if (!parallel) + { + switch (process_type) + { + case REINDEX_DATABASE: + case REINDEX_SYSTEM: + + /* + * Database and system reindexes only need to work on the + * database itself, so build a list with a single entry. + */ + Assert(user_list == NULL); + process_list = pg_malloc0(sizeof(SimpleStringList)); + simple_string_list_append(process_list, PQdb(conn)); + break; + + case REINDEX_INDEX: + case REINDEX_SCHEMA: + case REINDEX_TABLE: + Assert(user_list != NULL); + break; + } + } + else + { + switch (process_type) + { + case REINDEX_DATABASE: + + /* + * Database-wide parallel reindex requires special processing. + * If multiple jobs were asked, we have to reindex system + * catalogs first as they cannot be processed in parallel. + */ + if (concurrently) + pg_log_warning("cannot reindex system catalogs concurrently, skipping all"); + else + run_reindex_command(conn, REINDEX_SYSTEM, PQdb(conn), echo, + verbose, concurrently, false); + + /* Build a list of relations from the database */ + process_list = get_parallel_object_list(conn, process_type, + user_list, echo); + process_type = REINDEX_TABLE; + + /* Bail out if nothing to process */ + if (process_list == NULL) + return; + break; + + case REINDEX_SCHEMA: + Assert(user_list != NULL); + + /* Build a list of relations from all the schemas */ + process_list = get_parallel_object_list(conn, process_type, + user_list, echo); + process_type = REINDEX_TABLE; + + /* Bail out if nothing to process */ + if (process_list == NULL) + return; + break; + + case REINDEX_SYSTEM: + case REINDEX_INDEX: + /* not supported */ + Assert(false); + break; + + case REINDEX_TABLE: + + /* + * Fall through. The list of items for tables is already + * created. + */ + break; + } + } + + /* + * Adjust the number of concurrent connections depending on the items in + * the list. We choose the minimum between the number of concurrent + * connections and the number of items in the list. + */ + for (cell = process_list->head; cell; cell = cell->next) + { + items_count++; + + /* no need to continue if there are more elements than jobs */ + if (items_count >= concurrentCons) + break; + } + concurrentCons = Min(concurrentCons, items_count); + Assert(concurrentCons > 0); + + Assert(process_list != NULL); + + slots = ParallelSlotsSetup(dbname, host, port, username, prompt_password, + progname, echo, conn, concurrentCons); + + cell = process_list->head; + do + { + const char *objname = cell->val; + ParallelSlot *free_slot = NULL; + + if (CancelRequested) + { + failed = true; + goto finish; + } + + free_slot = ParallelSlotsGetIdle(slots, concurrentCons); + if (!free_slot) + { + failed = true; + goto finish; + } + + run_reindex_command(free_slot->connection, process_type, objname, + echo, verbose, concurrently, true); + + cell = cell->next; + } while (cell != NULL); + + if (!ParallelSlotsWaitCompletion(slots, concurrentCons)) + failed = true; + +finish: + if (process_list != user_list) + { + simple_string_list_destroy(process_list); + pg_free(process_list); + } + + ParallelSlotsTerminate(slots, concurrentCons); + pfree(slots); + + if (failed) + exit(1); +} + +static void +run_reindex_command(PGconn *conn, ReindexType type, const char *name, + bool echo, bool verbose, bool concurrently, bool async) +{ + PQExpBufferData sql; + bool status; + + Assert(name); + + /* build the REINDEX query */ initPQExpBuffer(&sql); appendPQExpBufferStr(&sql, "REINDEX "); @@ -311,47 +507,218 @@ reindex_one_database(const char *name, const char *dbname, const char *type, if (verbose) appendPQExpBufferStr(&sql, "(VERBOSE) "); - appendPQExpBufferStr(&sql, type); - appendPQExpBufferChar(&sql, ' '); + /* object type */ + switch (type) + { + case REINDEX_DATABASE: + appendPQExpBufferStr(&sql, "DATABASE "); + break; + case REINDEX_INDEX: + appendPQExpBufferStr(&sql, "INDEX "); + break; + case REINDEX_SCHEMA: + appendPQExpBufferStr(&sql, "SCHEMA "); + break; + case REINDEX_SYSTEM: + appendPQExpBufferStr(&sql, "SYSTEM "); + break; + case REINDEX_TABLE: + appendPQExpBufferStr(&sql, "TABLE "); + break; + } + if (concurrently) appendPQExpBufferStr(&sql, "CONCURRENTLY "); - if (strcmp(type, "TABLE") == 0 || - strcmp(type, "INDEX") == 0) - appendQualifiedRelation(&sql, name, conn, progname, echo); - else if (strcmp(type, "SCHEMA") == 0) - appendPQExpBufferStr(&sql, name); - else if (strcmp(type, "DATABASE") == 0) - appendPQExpBufferStr(&sql, fmtId(PQdb(conn))); + + /* object name */ + switch (type) + { + case REINDEX_DATABASE: + case REINDEX_SYSTEM: + appendPQExpBufferStr(&sql, fmtId(name)); + break; + case REINDEX_INDEX: + case REINDEX_TABLE: + appendQualifiedRelation(&sql, name, conn, echo); + break; + case REINDEX_SCHEMA: + appendPQExpBufferStr(&sql, name); + break; + } + + /* finish the query */ appendPQExpBufferChar(&sql, ';'); - if (!executeMaintenanceCommand(conn, sql.data, echo)) + if (async) { - if (strcmp(type, "TABLE") == 0) - pg_log_error("reindexing of table \"%s\" in database \"%s\" failed: %s", - name, PQdb(conn), PQerrorMessage(conn)); - else if (strcmp(type, "INDEX") == 0) - pg_log_error("reindexing of index \"%s\" in database \"%s\" failed: %s", - name, PQdb(conn), PQerrorMessage(conn)); - else if (strcmp(type, "SCHEMA") == 0) - pg_log_error("reindexing of schema \"%s\" in database \"%s\" failed: %s", - name, PQdb(conn), PQerrorMessage(conn)); - else - pg_log_error("reindexing of database \"%s\" failed: %s", - PQdb(conn), PQerrorMessage(conn)); - PQfinish(conn); - exit(1); + if (echo) + printf("%s\n", sql.data); + + status = PQsendQuery(conn, sql.data) == 1; + } + else + status = executeMaintenanceCommand(conn, sql.data, echo); + + if (!status) + { + switch (type) + { + case REINDEX_DATABASE: + pg_log_error("reindexing of database \"%s\" failed: %s", + PQdb(conn), PQerrorMessage(conn)); + break; + case REINDEX_INDEX: + pg_log_error("reindexing of index \"%s\" in database \"%s\" failed: %s", + name, PQdb(conn), PQerrorMessage(conn)); + break; + case REINDEX_SCHEMA: + pg_log_error("reindexing of schema \"%s\" in database \"%s\" failed: %s", + name, PQdb(conn), PQerrorMessage(conn)); + break; + case REINDEX_SYSTEM: + pg_log_error("reindexing of system catalogs on database \"%s\" failed: %s", + PQdb(conn), PQerrorMessage(conn)); + break; + case REINDEX_TABLE: + pg_log_error("reindexing of table \"%s\" in database \"%s\" failed: %s", + name, PQdb(conn), PQerrorMessage(conn)); + break; + } + if (!async) + { + PQfinish(conn); + exit(1); + } } - PQfinish(conn); termPQExpBuffer(&sql); } +/* + * Prepare the list of objects to process by querying the catalogs. + * + * This function will return a SimpleStringList object containing the entire + * list of tables in the given database that should be processed by a parallel + * database-wide reindex (excluding system tables), or NULL if there's no such + * table. + */ +static SimpleStringList * +get_parallel_object_list(PGconn *conn, ReindexType type, + SimpleStringList *user_list, bool echo) +{ + PQExpBufferData catalog_query; + PQExpBufferData buf; + PGresult *res; + SimpleStringList *tables; + int ntups, + i; + + initPQExpBuffer(&catalog_query); + + /* + * The queries here are using a safe search_path, so there's no need to + * fully qualify everything. + */ + switch (type) + { + case REINDEX_DATABASE: + Assert(user_list == NULL); + appendPQExpBuffer(&catalog_query, + "SELECT c.relname, ns.nspname\n" + " FROM pg_catalog.pg_class c\n" + " JOIN pg_catalog.pg_namespace ns" + " ON c.relnamespace = ns.oid\n" + " WHERE ns.nspname != 'pg_catalog'\n" + " AND c.relkind IN (" + CppAsString2(RELKIND_RELATION) ", " + CppAsString2(RELKIND_MATVIEW) ")\n" + " ORDER BY c.relpages DESC;"); + break; + + case REINDEX_SCHEMA: + { + SimpleStringListCell *cell; + bool nsp_listed = false; + + Assert(user_list != NULL); + + /* + * All the tables from all the listed schemas are grabbed at + * once. + */ + appendPQExpBuffer(&catalog_query, + "SELECT c.relname, ns.nspname\n" + " FROM pg_catalog.pg_class c\n" + " JOIN pg_catalog.pg_namespace ns" + " ON c.relnamespace = ns.oid\n" + " WHERE c.relkind IN (" + CppAsString2(RELKIND_RELATION) ", " + CppAsString2(RELKIND_MATVIEW) ")\n" + " AND ns.nspname IN ("); + + for (cell = user_list->head; cell; cell = cell->next) + { + const char *nspname = cell->val; + + if (nsp_listed) + appendPQExpBuffer(&catalog_query, ", "); + else + nsp_listed = true; + + appendStringLiteralConn(&catalog_query, nspname, conn); + } + + appendPQExpBuffer(&catalog_query, ")\n" + " ORDER BY c.relpages DESC;"); + } + break; + + case REINDEX_SYSTEM: + case REINDEX_INDEX: + case REINDEX_TABLE: + Assert(false); + break; + } + + res = executeQuery(conn, catalog_query.data, echo); + termPQExpBuffer(&catalog_query); + + /* + * If no rows are returned, there are no matching tables, so we are done. + */ + ntups = PQntuples(res); + if (ntups == 0) + { + PQclear(res); + PQfinish(conn); + return NULL; + } + + tables = pg_malloc0(sizeof(SimpleStringList)); + + /* Build qualified identifiers for each table */ + initPQExpBuffer(&buf); + for (i = 0; i < ntups; i++) + { + appendPQExpBufferStr(&buf, + fmtQualifiedId(PQgetvalue(res, i, 1), + PQgetvalue(res, i, 0))); + + simple_string_list_append(tables, buf.data); + resetPQExpBuffer(&buf); + } + termPQExpBuffer(&buf); + PQclear(res); + + return tables; +} + static void reindex_all_databases(const char *maintenance_db, const char *host, const char *port, const char *username, enum trivalue prompt_password, const char *progname, bool echo, bool quiet, bool verbose, - bool concurrently) + bool concurrently, int concurrentCons) { PGconn *conn; PGresult *result; @@ -360,7 +727,7 @@ reindex_all_databases(const char *maintenance_db, conn = connectMaintenanceDatabase(maintenance_db, host, port, username, prompt_password, progname, echo); - result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", progname, echo); + result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", echo); PQfinish(conn); initPQExpBuffer(&connstr); @@ -375,53 +742,19 @@ reindex_all_databases(const char *maintenance_db, } resetPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, dbname); - reindex_one_database(NULL, connstr.data, "DATABASE", host, + reindex_one_database(connstr.data, REINDEX_DATABASE, NULL, host, port, username, prompt_password, - progname, echo, verbose, concurrently); + progname, echo, verbose, concurrently, + concurrentCons); } termPQExpBuffer(&connstr); PQclear(result); } -static void -reindex_system_catalogs(const char *dbname, const char *host, const char *port, - const char *username, enum trivalue prompt_password, - const char *progname, bool echo, bool verbose, bool concurrently) -{ - PGconn *conn; - PQExpBufferData sql; - - conn = connectDatabase(dbname, host, port, username, prompt_password, - progname, echo, false, false); - - initPQExpBuffer(&sql); - - appendPQExpBuffer(&sql, "REINDEX"); - - if (verbose) - appendPQExpBuffer(&sql, " (VERBOSE)"); - - appendPQExpBufferStr(&sql, " SYSTEM "); - if (concurrently) - appendPQExpBuffer(&sql, "CONCURRENTLY "); - appendPQExpBufferStr(&sql, fmtId(PQdb(conn))); - appendPQExpBufferChar(&sql, ';'); - - if (!executeMaintenanceCommand(conn, sql.data, echo)) - { - pg_log_error("reindexing of system catalogs failed: %s", - PQerrorMessage(conn)); - PQfinish(conn); - exit(1); - } - PQfinish(conn); - termPQExpBuffer(&sql); -} - static void help(const char *progname) { @@ -434,6 +767,7 @@ help(const char *progname) printf(_(" -d, --dbname=DBNAME database to reindex\n")); printf(_(" -e, --echo show the commands being sent to the server\n")); printf(_(" -i, --index=INDEX recreate specific index(es) only\n")); + printf(_(" -j, --jobs=NUM use this many concurrent connections to reindex\n")); printf(_(" -q, --quiet don't write any messages\n")); printf(_(" -s, --system reindex system catalogs\n")); printf(_(" -S, --schema=SCHEMA reindex specific schema(s) only\n")); diff --git a/src/bin/scripts/scripts_parallel.c b/src/bin/scripts/scripts_parallel.c new file mode 100644 index 000000000000..ffdc4e49ef81 --- /dev/null +++ b/src/bin/scripts/scripts_parallel.c @@ -0,0 +1,283 @@ +/*------------------------------------------------------------------------- + * + * scripts_parallel.c + * Parallel support for bin/scripts/ + * + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/bin/scripts/scripts_parallel.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include "common.h" +#include "common/logging.h" +#include "scripts_parallel.h" + +static void init_slot(ParallelSlot *slot, PGconn *conn); +static int select_loop(int maxFd, fd_set *workerset, bool *aborting); + +static void +init_slot(ParallelSlot *slot, PGconn *conn) +{ + slot->connection = conn; + /* Initially assume connection is idle */ + slot->isFree = true; +} + +/* + * Loop on select() until a descriptor from the given set becomes readable. + * + * If we get a cancel request while we're waiting, we forego all further + * processing and set the *aborting flag to true. The return value must be + * ignored in this case. Otherwise, *aborting is set to false. + */ +static int +select_loop(int maxFd, fd_set *workerset, bool *aborting) +{ + int i; + fd_set saveSet = *workerset; + + if (CancelRequested) + { + *aborting = true; + return -1; + } + else + *aborting = false; + + for (;;) + { + /* + * On Windows, we need to check once in a while for cancel requests; + * on other platforms we rely on select() returning when interrupted. + */ + struct timeval *tvp; +#ifdef WIN32 + struct timeval tv = {0, 1000000}; + + tvp = &tv; +#else + tvp = NULL; +#endif + + *workerset = saveSet; + i = select(maxFd + 1, workerset, NULL, NULL, tvp); + +#ifdef WIN32 + if (i == SOCKET_ERROR) + { + i = -1; + + if (WSAGetLastError() == WSAEINTR) + errno = EINTR; + } +#endif + + if (i < 0 && errno == EINTR) + continue; /* ignore this */ + if (i < 0 || CancelRequested) + *aborting = true; /* but not this */ + if (i == 0) + continue; /* timeout (Win32 only) */ + break; + } + + return i; +} + +/* + * ParallelSlotsGetIdle + * Return a connection slot that is ready to execute a command. + * + * This returns the first slot we find that is marked isFree, if one is; + * otherwise, we loop on select() until one socket becomes available. When + * this happens, we read the whole set and mark as free all sockets that + * become available. If an error occurs, NULL is returned. + */ +ParallelSlot * +ParallelSlotsGetIdle(ParallelSlot *slots, int numslots) +{ + int i; + int firstFree = -1; + + /* + * Look for any connection currently free. If there is one, mark it as + * taken and let the caller know the slot to use. + */ + for (i = 0; i < numslots; i++) + { + if (slots[i].isFree) + { + slots[i].isFree = false; + return slots + i; + } + } + + /* + * No free slot found, so wait until one of the connections has finished + * its task and return the available slot. + */ + while (firstFree < 0) + { + fd_set slotset; + int maxFd = 0; + bool aborting; + + /* We must reconstruct the fd_set for each call to select_loop */ + FD_ZERO(&slotset); + + for (i = 0; i < numslots; i++) + { + int sock = PQsocket(slots[i].connection); + + /* + * We don't really expect any connections to lose their sockets + * after startup, but just in case, cope by ignoring them. + */ + if (sock < 0) + continue; + + FD_SET(sock, &slotset); + if (sock > maxFd) + maxFd = sock; + } + + SetCancelConn(slots->connection); + i = select_loop(maxFd, &slotset, &aborting); + ResetCancelConn(); + + if (aborting) + { + /* + * We set the cancel-receiving connection to the one in the zeroth + * slot above, so fetch the error from there. + */ + consumeQueryResult(slots->connection); + return NULL; + } + Assert(i != 0); + + for (i = 0; i < numslots; i++) + { + int sock = PQsocket(slots[i].connection); + + if (sock >= 0 && FD_ISSET(sock, &slotset)) + { + /* select() says input is available, so consume it */ + PQconsumeInput(slots[i].connection); + } + + /* Collect result(s) as long as any are available */ + while (!PQisBusy(slots[i].connection)) + { + PGresult *result = PQgetResult(slots[i].connection); + + if (result != NULL) + { + /* Check and discard the command result */ + if (!processQueryResult(slots[i].connection, result)) + return NULL; + } + else + { + /* This connection has become idle */ + slots[i].isFree = true; + if (firstFree < 0) + firstFree = i; + break; + } + } + } + } + + slots[firstFree].isFree = false; + return slots + firstFree; +} + +/* + * ParallelSlotsSetup + * Prepare a set of parallel slots to use on a given database. + * + * This creates and initializes a set of connections to the database + * using the information given by the caller, marking all parallel slots + * as free and ready to use. "conn" is an initial connection set up + * by the caller and is associated with the first slot in the parallel + * set. + */ +ParallelSlot * +ParallelSlotsSetup(const char *dbname, const char *host, const char *port, + const char *username, bool prompt_password, + const char *progname, bool echo, + PGconn *conn, int numslots) +{ + ParallelSlot *slots; + int i; + + Assert(conn != NULL); + + slots = (ParallelSlot *) pg_malloc(sizeof(ParallelSlot) * numslots); + init_slot(slots, conn); + if (numslots > 1) + { + for (i = 1; i < numslots; i++) + { + conn = connectDatabase(dbname, host, port, username, prompt_password, + progname, echo, false, true); + init_slot(slots + i, conn); + } + } + + return slots; +} + +/* + * ParallelSlotsTerminate + * Clean up a set of parallel slots + * + * Iterate through all connections in a given set of ParallelSlots and + * terminate all connections. + */ +void +ParallelSlotsTerminate(ParallelSlot *slots, int numslots) +{ + int i; + + for (i = 0; i < numslots; i++) + { + PGconn *conn = slots[i].connection; + + if (conn == NULL) + continue; + + disconnectDatabase(conn); + } +} + +/* + * ParallelSlotsWaitCompletion + * + * Wait for all connections to finish, returning false if at least one + * error has been found on the way. + */ +bool +ParallelSlotsWaitCompletion(ParallelSlot *slots, int numslots) +{ + int i; + + for (i = 0; i < numslots; i++) + { + if (!consumeQueryResult((slots + i)->connection)) + return false; + } + + return true; +} diff --git a/src/bin/scripts/scripts_parallel.h b/src/bin/scripts/scripts_parallel.h new file mode 100644 index 000000000000..f1a724a64f13 --- /dev/null +++ b/src/bin/scripts/scripts_parallel.h @@ -0,0 +1,36 @@ +/*------------------------------------------------------------------------- + * + * scripts_parallel.h + * Parallel support for bin/scripts/ + * + * Copyright (c) 2003-2019, PostgreSQL Global Development Group + * + * src/bin/scripts/scripts_parallel.h + * + *------------------------------------------------------------------------- + */ +#ifndef SCRIPTS_PARALLEL_H +#define SCRIPTS_PARALLEL_H + + +typedef struct ParallelSlot +{ + PGconn *connection; /* One connection */ + bool isFree; /* Is it known to be idle? */ +} ParallelSlot; + +extern ParallelSlot *ParallelSlotsGetIdle(ParallelSlot *slots, int numslots); + +extern ParallelSlot *ParallelSlotsSetup(const char *dbname, const char *host, + const char *port, + const char *username, + bool prompt_password, + const char *progname, bool echo, + PGconn *conn, int numslots); + +extern void ParallelSlotsTerminate(ParallelSlot *slots, int numslots); + +extern bool ParallelSlotsWaitCompletion(ParallelSlot *slots, int numslots); + + +#endif /* SCRIPTS_PARALLEL_H */ diff --git a/src/bin/scripts/t/090_reindexdb.pl b/src/bin/scripts/t/090_reindexdb.pl index 6540dfd950c6..c59f03c51f61 100644 --- a/src/bin/scripts/t/090_reindexdb.pl +++ b/src/bin/scripts/t/090_reindexdb.pl @@ -3,7 +3,7 @@ use PostgresNode; use TestLib; -use Test::More tests => 34; +use Test::More tests => 44; program_help_ok('reindexdb'); program_version_ok('reindexdb'); @@ -82,3 +82,45 @@ $node->command_ok( [qw(reindexdb --echo --system dbname=template1)], 'reindexdb system with connection string'); + +# parallel processing +$node->safe_psql( + 'postgres', q| + CREATE SCHEMA s1; + CREATE TABLE s1.t1(id integer); + CREATE INDEX ON s1.t1(id); + CREATE SCHEMA s2; + CREATE TABLE s2.t2(id integer); + CREATE INDEX ON s2.t2(id); + -- empty schema + CREATE SCHEMA s3; +|); + +$node->command_fails( + [ 'reindexdb', '-j', '2', '-s', 'postgres' ], + 'parallel reindexdb cannot process system catalogs'); +$node->command_fails( + [ 'reindexdb', '-j', '2', '-i', 'i1', 'postgres' ], + 'parallel reindexdb cannot process indexes'); +$node->issues_sql_like( + [ 'reindexdb', '-j', '2', 'postgres' ], + qr/statement:\ REINDEX SYSTEM postgres; +.*statement:\ REINDEX TABLE public\.test1/s, + 'parallel reindexdb for database issues REINDEX SYSTEM first'); +# Note that the ordering of the commands is not stable, so the second +# command for s2.t2 is not checked after. +$node->issues_sql_like( + [ 'reindexdb', '-j', '2', '-S', 's1', '-S', 's2', 'postgres' ], + qr/statement:\ REINDEX TABLE s1.t1;/, + 'parallel reindexdb for schemas does a per-table REINDEX'); +$node->command_ok( + ['reindexdb', '-j', '2', '-S', 's3'], + 'parallel reindexdb with empty schema'); +$node->command_checks_all( + [ 'reindexdb', '-j', '2', '--concurrently', '-d', 'postgres' ], + 0, + [qr/^$/], + [ + qr/^reindexdb: warning: cannot reindex system catalogs concurrently, skipping all/s + ], + 'parallel reindexdb for system with --concurrently skips catalogs'); diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index 9533be085485..2e52b774f6ae 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -12,10 +12,6 @@ #include "postgres_fe.h" -#ifdef HAVE_SYS_SELECT_H -#include -#endif - #include "catalog/pg_class_d.h" #include "common.h" @@ -23,15 +19,8 @@ #include "fe_utils/connect.h" #include "fe_utils/simple_list.h" #include "fe_utils/string_utils.h" +#include "scripts_parallel.h" -#define ERRCODE_UNDEFINED_TABLE "42P01" - -/* Parallel vacuuming stuff */ -typedef struct ParallelSlot -{ - PGconn *connection; /* One connection */ - bool isFree; /* Is it known to be idle? */ -} ParallelSlot; /* vacuum options controlled by user flags */ typedef struct vacuumingOptions @@ -68,21 +57,7 @@ static void prepare_vacuum_command(PQExpBuffer sql, int serverVersion, vacuumingOptions *vacopts, const char *table); static void run_vacuum_command(PGconn *conn, const char *sql, bool echo, - const char *table, const char *progname, bool async); - -static ParallelSlot *GetIdleSlot(ParallelSlot slots[], int numslots, - const char *progname); - -static bool ProcessQueryResult(PGconn *conn, PGresult *result, - const char *progname); - -static bool GetQueryResult(PGconn *conn, const char *progname); - -static void DisconnectDatabase(ParallelSlot *slot); - -static int select_loop(int maxFd, fd_set *workerset, bool *aborting); - -static void init_slot(ParallelSlot *slot, PGconn *conn); + const char *table); static void help(const char *progname); @@ -476,16 +451,16 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, if (!tables_listed) { - appendPQExpBuffer(&catalog_query, - "WITH listed_tables (table_oid, column_list) " - "AS (\n VALUES ("); + appendPQExpBufferStr(&catalog_query, + "WITH listed_tables (table_oid, column_list) " + "AS (\n VALUES ("); tables_listed = true; } else - appendPQExpBuffer(&catalog_query, ",\n ("); + appendPQExpBufferStr(&catalog_query, ",\n ("); appendStringLiteralConn(&catalog_query, just_table, conn); - appendPQExpBuffer(&catalog_query, "::pg_catalog.regclass, "); + appendPQExpBufferStr(&catalog_query, "::pg_catalog.regclass, "); if (just_columns && just_columns[0] != '\0') appendStringLiteralConn(&catalog_query, just_columns, conn); @@ -499,24 +474,24 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, /* Finish formatting the CTE */ if (tables_listed) - appendPQExpBuffer(&catalog_query, "\n)\n"); + appendPQExpBufferStr(&catalog_query, "\n)\n"); - appendPQExpBuffer(&catalog_query, "SELECT c.relname, ns.nspname"); + appendPQExpBufferStr(&catalog_query, "SELECT c.relname, ns.nspname"); if (tables_listed) - appendPQExpBuffer(&catalog_query, ", listed_tables.column_list"); + appendPQExpBufferStr(&catalog_query, ", listed_tables.column_list"); - appendPQExpBuffer(&catalog_query, - " FROM pg_catalog.pg_class c\n" - " JOIN pg_catalog.pg_namespace ns" - " ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n" - " LEFT JOIN pg_catalog.pg_class t" - " ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid\n"); + appendPQExpBufferStr(&catalog_query, + " FROM pg_catalog.pg_class c\n" + " JOIN pg_catalog.pg_namespace ns" + " ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n" + " LEFT JOIN pg_catalog.pg_class t" + " ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid\n"); /* Used to match the tables listed by the user */ if (tables_listed) - appendPQExpBuffer(&catalog_query, " JOIN listed_tables" - " ON listed_tables.table_oid OPERATOR(pg_catalog.=) c.oid\n"); + appendPQExpBufferStr(&catalog_query, " JOIN listed_tables" + " ON listed_tables.table_oid OPERATOR(pg_catalog.=) c.oid\n"); /* * If no tables were listed, filter for the relevant relation types. If @@ -526,9 +501,9 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, */ if (!tables_listed) { - appendPQExpBuffer(&catalog_query, " WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array[" - CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) "])\n"); + appendPQExpBufferStr(&catalog_query, " WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array[" + CppAsString2(RELKIND_RELATION) ", " + CppAsString2(RELKIND_MATVIEW) "])\n"); has_where = true; } @@ -567,12 +542,11 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, * Execute the catalog query. We use the default search_path for this * query for consistency with table lookups done elsewhere by the user. */ - appendPQExpBuffer(&catalog_query, " ORDER BY c.relpages DESC;"); - executeCommand(conn, "RESET search_path;", progname, echo); - res = executeQuery(conn, catalog_query.data, progname, echo); + appendPQExpBufferStr(&catalog_query, " ORDER BY c.relpages DESC;"); + executeCommand(conn, "RESET search_path;", echo); + res = executeQuery(conn, catalog_query.data, echo); termPQExpBuffer(&catalog_query); - PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, - progname, echo)); + PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo)); /* * If no rows are returned, there are no matching tables, so we are done. @@ -624,18 +598,9 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, */ if (concurrentCons <= 0) concurrentCons = 1; - slots = (ParallelSlot *) pg_malloc(sizeof(ParallelSlot) * concurrentCons); - init_slot(slots, conn); - if (parallel) - { - for (i = 1; i < concurrentCons; i++) - { - conn = connectDatabase(dbname, host, port, username, prompt_password, - progname, echo, false, true); - init_slot(slots + i, conn); - } - } + slots = ParallelSlotsSetup(dbname, host, port, username, prompt_password, + progname, echo, conn, concurrentCons); /* * Prepare all the connections to run the appropriate analyze stage, if @@ -649,7 +614,7 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, for (j = 0; j < concurrentCons; j++) executeCommand((slots + j)->connection, - stage_commands[stage], progname, echo); + stage_commands[stage], echo); } initPQExpBuffer(&sql); @@ -666,63 +631,32 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, goto finish; } - /* - * Get the connection slot to use. If in parallel mode, here we wait - * for one connection to become available if none already is. In - * non-parallel mode we simply use the only slot we have, which we - * know to be free. - */ - if (parallel) + free_slot = ParallelSlotsGetIdle(slots, concurrentCons); + if (!free_slot) { - /* - * Get a free slot, waiting until one becomes free if none - * currently is. - */ - free_slot = GetIdleSlot(slots, concurrentCons, progname); - if (!free_slot) - { - failed = true; - goto finish; - } - - free_slot->isFree = false; + failed = true; + goto finish; } - else - free_slot = slots; prepare_vacuum_command(&sql, PQserverVersion(free_slot->connection), vacopts, tabname); /* - * Execute the vacuum. If not in parallel mode, this terminates the - * program in case of an error. (The parallel case handles query - * errors in ProcessQueryResult through GetIdleSlot.) + * Execute the vacuum. All errors are handled in processQueryResult + * through ParallelSlotsGetIdle. */ run_vacuum_command(free_slot->connection, sql.data, - echo, tabname, progname, parallel); + echo, tabname); cell = cell->next; } while (cell != NULL); - if (parallel) - { - int j; - - /* wait for all connections to finish */ - for (j = 0; j < concurrentCons; j++) - { - if (!GetQueryResult((slots + j)->connection, progname)) - { - failed = true; - goto finish; - } - } - } + if (!ParallelSlotsWaitCompletion(slots, concurrentCons)) + failed = true; finish: - for (i = 0; i < concurrentCons; i++) - DisconnectDatabase(slots + i); - pfree(slots); + ParallelSlotsTerminate(slots, concurrentCons); + pg_free(slots); termPQExpBuffer(&sql); @@ -756,7 +690,7 @@ vacuum_all_databases(vacuumingOptions *vacopts, prompt_password, progname, echo); result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", - progname, echo); + echo); PQfinish(conn); initPQExpBuffer(&connstr); @@ -775,7 +709,7 @@ vacuum_all_databases(vacuumingOptions *vacopts, for (i = 0; i < PQntuples(result); i++) { resetPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, PQgetvalue(result, i, 0)); vacuum_one_database(connstr.data, vacopts, @@ -792,7 +726,7 @@ vacuum_all_databases(vacuumingOptions *vacopts, for (i = 0; i < PQntuples(result); i++) { resetPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, PQgetvalue(result, i, 0)); vacuum_one_database(connstr.data, vacopts, @@ -914,27 +848,21 @@ prepare_vacuum_command(PQExpBuffer sql, int serverVersion, } /* - * Send a vacuum/analyze command to the server. In async mode, return after - * sending the command; else, wait for it to finish. + * Send a vacuum/analyze command to the server, returning after sending the + * command. * - * Any errors during command execution are reported to stderr. If async is - * false, this function exits the program after reporting the error. + * Any errors during command execution are reported to stderr. */ static void run_vacuum_command(PGconn *conn, const char *sql, bool echo, - const char *table, const char *progname, bool async) + const char *table) { bool status; - if (async) - { - if (echo) - printf("%s\n", sql); + if (echo) + printf("%s\n", sql); - status = PQsendQuery(conn, sql) == 1; - } - else - status = executeMaintenanceCommand(conn, sql, echo); + status = PQsendQuery(conn, sql) == 1; if (!status) { @@ -944,271 +872,9 @@ run_vacuum_command(PGconn *conn, const char *sql, bool echo, else pg_log_error("vacuuming of database \"%s\" failed: %s", PQdb(conn), PQerrorMessage(conn)); - - if (!async) - { - PQfinish(conn); - exit(1); - } } } -/* - * GetIdleSlot - * Return a connection slot that is ready to execute a command. - * - * We return the first slot we find that is marked isFree, if one is; - * otherwise, we loop on select() until one socket becomes available. When - * this happens, we read the whole set and mark as free all sockets that become - * available. - * - * If an error occurs, NULL is returned. - */ -static ParallelSlot * -GetIdleSlot(ParallelSlot slots[], int numslots, - const char *progname) -{ - int i; - int firstFree = -1; - - /* Any connection already known free? */ - for (i = 0; i < numslots; i++) - { - if (slots[i].isFree) - return slots + i; - } - - /* - * No free slot found, so wait until one of the connections has finished - * its task and return the available slot. - */ - while (firstFree < 0) - { - fd_set slotset; - int maxFd = 0; - bool aborting; - - /* We must reconstruct the fd_set for each call to select_loop */ - FD_ZERO(&slotset); - - for (i = 0; i < numslots; i++) - { - int sock = PQsocket(slots[i].connection); - - /* - * We don't really expect any connections to lose their sockets - * after startup, but just in case, cope by ignoring them. - */ - if (sock < 0) - continue; - - FD_SET(sock, &slotset); - if (sock > maxFd) - maxFd = sock; - } - - SetCancelConn(slots->connection); - i = select_loop(maxFd, &slotset, &aborting); - ResetCancelConn(); - - if (aborting) - { - /* - * We set the cancel-receiving connection to the one in the zeroth - * slot above, so fetch the error from there. - */ - GetQueryResult(slots->connection, progname); - return NULL; - } - Assert(i != 0); - - for (i = 0; i < numslots; i++) - { - int sock = PQsocket(slots[i].connection); - - if (sock >= 0 && FD_ISSET(sock, &slotset)) - { - /* select() says input is available, so consume it */ - PQconsumeInput(slots[i].connection); - } - - /* Collect result(s) as long as any are available */ - while (!PQisBusy(slots[i].connection)) - { - PGresult *result = PQgetResult(slots[i].connection); - - if (result != NULL) - { - /* Check and discard the command result */ - if (!ProcessQueryResult(slots[i].connection, result, - progname)) - return NULL; - } - else - { - /* This connection has become idle */ - slots[i].isFree = true; - if (firstFree < 0) - firstFree = i; - break; - } - } - } - } - - return slots + firstFree; -} - -/* - * ProcessQueryResult - * - * Process (and delete) a query result. Returns true if there's no error, - * false otherwise -- but errors about trying to vacuum a missing relation - * are reported and subsequently ignored. - */ -static bool -ProcessQueryResult(PGconn *conn, PGresult *result, const char *progname) -{ - /* - * If it's an error, report it. Errors about a missing table are harmless - * so we continue processing; but die for other errors. - */ - if (PQresultStatus(result) != PGRES_COMMAND_OK) - { - char *sqlState = PQresultErrorField(result, PG_DIAG_SQLSTATE); - - pg_log_error("vacuuming of database \"%s\" failed: %s", - PQdb(conn), PQerrorMessage(conn)); - - if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) != 0) - { - PQclear(result); - return false; - } - } - - PQclear(result); - return true; -} - -/* - * GetQueryResult - * - * Pump the conn till it's dry of results; return false if any are errors. - * Note that this will block if the conn is busy. - */ -static bool -GetQueryResult(PGconn *conn, const char *progname) -{ - bool ok = true; - PGresult *result; - - SetCancelConn(conn); - while ((result = PQgetResult(conn)) != NULL) - { - if (!ProcessQueryResult(conn, result, progname)) - ok = false; - } - ResetCancelConn(); - return ok; -} - -/* - * DisconnectDatabase - * Disconnect the connection associated with the given slot - */ -static void -DisconnectDatabase(ParallelSlot *slot) -{ - char errbuf[256]; - - if (!slot->connection) - return; - - if (PQtransactionStatus(slot->connection) == PQTRANS_ACTIVE) - { - PGcancel *cancel; - - if ((cancel = PQgetCancel(slot->connection))) - { - (void) PQcancel(cancel, errbuf, sizeof(errbuf)); - PQfreeCancel(cancel); - } - } - - PQfinish(slot->connection); - slot->connection = NULL; -} - -/* - * Loop on select() until a descriptor from the given set becomes readable. - * - * If we get a cancel request while we're waiting, we forego all further - * processing and set the *aborting flag to true. The return value must be - * ignored in this case. Otherwise, *aborting is set to false. - */ -static int -select_loop(int maxFd, fd_set *workerset, bool *aborting) -{ - int i; - fd_set saveSet = *workerset; - - if (CancelRequested) - { - *aborting = true; - return -1; - } - else - *aborting = false; - - for (;;) - { - /* - * On Windows, we need to check once in a while for cancel requests; - * on other platforms we rely on select() returning when interrupted. - */ - struct timeval *tvp; -#ifdef WIN32 - struct timeval tv = {0, 1000000}; - - tvp = &tv; -#else - tvp = NULL; -#endif - - *workerset = saveSet; - i = select(maxFd + 1, workerset, NULL, NULL, tvp); - -#ifdef WIN32 - if (i == SOCKET_ERROR) - { - i = -1; - - if (WSAGetLastError() == WSAEINTR) - errno = EINTR; - } -#endif - - if (i < 0 && errno == EINTR) - continue; /* ignore this */ - if (i < 0 || CancelRequested) - *aborting = true; /* but not this */ - if (i == 0) - continue; /* timeout (Win32 only) */ - break; - } - - return i; -} - -static void -init_slot(ParallelSlot *slot, PGconn *conn) -{ - slot->connection = conn; - /* Initially assume connection is idle */ - slot->isFree = true; -} - static void help(const char *progname) { diff --git a/src/common/base64.c b/src/common/base64.c index 55c8983f97d5..57ec06c3a95d 100644 --- a/src/common/base64.c +++ b/src/common/base64.c @@ -42,10 +42,11 @@ static const int8 b64lookup[128] = { * pg_b64_encode * * Encode into base64 the given string. Returns the length of the encoded - * string. + * string, and -1 in the event of an error with the result buffer zeroed + * for safety. */ int -pg_b64_encode(const char *src, int len, char *dst) +pg_b64_encode(const char *src, int len, char *dst, int dstlen) { char *p; const char *s, @@ -65,6 +66,13 @@ pg_b64_encode(const char *src, int len, char *dst) /* write it out */ if (pos < 0) { + /* + * Leave if there is an overflow in the area allocated for the + * encoded string. + */ + if ((p - dst + 4) > dstlen) + goto error; + *p++ = _base64[(buf >> 18) & 0x3f]; *p++ = _base64[(buf >> 12) & 0x3f]; *p++ = _base64[(buf >> 6) & 0x3f]; @@ -76,23 +84,36 @@ pg_b64_encode(const char *src, int len, char *dst) } if (pos != 2) { + /* + * Leave if there is an overflow in the area allocated for the encoded + * string. + */ + if ((p - dst + 4) > dstlen) + goto error; + *p++ = _base64[(buf >> 18) & 0x3f]; *p++ = _base64[(buf >> 12) & 0x3f]; *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '='; *p++ = '='; } + Assert((p - dst) <= dstlen); return p - dst; + +error: + memset(dst, 0, dstlen); + return -1; } /* * pg_b64_decode * * Decode the given base64 string. Returns the length of the decoded - * string on success, and -1 in the event of an error. + * string on success, and -1 in the event of an error with the result + * buffer zeroed for safety. */ int -pg_b64_decode(const char *src, int len, char *dst) +pg_b64_decode(const char *src, int len, char *dst, int dstlen) { const char *srcend = src + len, *s = src; @@ -109,7 +130,7 @@ pg_b64_decode(const char *src, int len, char *dst) /* Leave if a whitespace is found */ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') - return -1; + goto error; if (c == '=') { @@ -126,7 +147,7 @@ pg_b64_decode(const char *src, int len, char *dst) * Unexpected "=" character found while decoding base64 * sequence. */ - return -1; + goto error; } } b = 0; @@ -139,7 +160,7 @@ pg_b64_decode(const char *src, int len, char *dst) if (b < 0) { /* invalid symbol found */ - return -1; + goto error; } } /* add it to buffer */ @@ -147,11 +168,28 @@ pg_b64_decode(const char *src, int len, char *dst) pos++; if (pos == 4) { + /* + * Leave if there is an overflow in the area allocated for the + * decoded string. + */ + if ((p - dst + 1) > dstlen) + goto error; *p++ = (buf >> 16) & 255; + if (end == 0 || end > 1) + { + /* overflow check */ + if ((p - dst + 1) > dstlen) + goto error; *p++ = (buf >> 8) & 255; + } if (end == 0 || end > 2) + { + /* overflow check */ + if ((p - dst + 1) > dstlen) + goto error; *p++ = buf & 255; + } buf = 0; pos = 0; } @@ -163,10 +201,15 @@ pg_b64_decode(const char *src, int len, char *dst) * base64 end sequence is invalid. Input data is missing padding, is * truncated or is otherwise corrupted. */ - return -1; + goto error; } + Assert((p - dst) <= dstlen); return p - dst; + +error: + memset(dst, 0, dstlen); + return -1; } /* diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c index 2c28e51e1112..39c27c922926 100644 --- a/src/common/controldata_utils.c +++ b/src/common/controldata_utils.c @@ -106,13 +106,13 @@ get_controlfile(const char *DataDir, bool *crc_ok_p) } #ifndef FRONTEND - if (CloseTransientFile(fd)) + if (CloseTransientFile(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", ControlFilePath))); #else - if (close(fd)) + if (close(fd) != 0) { pg_log_fatal("could not close file \"%s\": %m", ControlFilePath); exit(EXIT_FAILURE); @@ -248,7 +248,7 @@ update_controlfile(const char *DataDir, #endif } - if (close(fd) < 0) + if (close(fd) != 0) { #ifndef FRONTEND ereport(PANIC, diff --git a/src/common/logging.c b/src/common/logging.c index f247554a32b9..895da7150e7a 100644 --- a/src/common/logging.c +++ b/src/common/logging.c @@ -216,6 +216,8 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a buf = pg_malloc_extended(required_len, MCXT_ALLOC_NO_OOM); + errno = save_errno; /* malloc might change errno */ + if (!buf) { /* memory trouble, just print what we can and get out of here */ diff --git a/src/common/md5.c b/src/common/md5.c index 24b96eb31495..55eaeed82866 100644 --- a/src/common/md5.c +++ b/src/common/md5.c @@ -277,7 +277,7 @@ bytesToHex(uint8 b[16], char *s) * * OUTPUT hexsum the MD5 sum as a '\0'-terminated string of * hexadecimal digits. an MD5 sum is 16 bytes long. - * each byte is represented by two heaxadecimal + * each byte is represented by two hexadecimal * characters. you thus need to provide an array * of 33 characters, including the trailing '\0'. * diff --git a/src/common/scram-common.c b/src/common/scram-common.c index c30dfc97dcaa..dff9723e67fc 100644 --- a/src/common/scram-common.c +++ b/src/common/scram-common.c @@ -198,6 +198,10 @@ scram_build_verifier(const char *salt, int saltlen, int iterations, char *result; char *p; int maxlen; + int encoded_salt_len; + int encoded_stored_len; + int encoded_server_len; + int encoded_result; if (iterations <= 0) iterations = SCRAM_DEFAULT_ITERATIONS; @@ -215,11 +219,15 @@ scram_build_verifier(const char *salt, int saltlen, int iterations, * SCRAM-SHA-256$:$: *---------- */ + encoded_salt_len = pg_b64_enc_len(saltlen); + encoded_stored_len = pg_b64_enc_len(SCRAM_KEY_LEN); + encoded_server_len = pg_b64_enc_len(SCRAM_KEY_LEN); + maxlen = strlen("SCRAM-SHA-256") + 1 + 10 + 1 /* iteration count */ - + pg_b64_enc_len(saltlen) + 1 /* Base64-encoded salt */ - + pg_b64_enc_len(SCRAM_KEY_LEN) + 1 /* Base64-encoded StoredKey */ - + pg_b64_enc_len(SCRAM_KEY_LEN) + 1; /* Base64-encoded ServerKey */ + + encoded_salt_len + 1 /* Base64-encoded salt */ + + encoded_stored_len + 1 /* Base64-encoded StoredKey */ + + encoded_server_len + 1; /* Base64-encoded ServerKey */ #ifdef FRONTEND result = malloc(maxlen); @@ -231,11 +239,50 @@ scram_build_verifier(const char *salt, int saltlen, int iterations, p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations); - p += pg_b64_encode(salt, saltlen, p); + /* salt */ + encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len); + if (encoded_result < 0) + { +#ifdef FRONTEND + free(result); + return NULL; +#else + elog(ERROR, "could not encode salt"); +#endif + } + p += encoded_result; *(p++) = '$'; - p += pg_b64_encode((char *) stored_key, SCRAM_KEY_LEN, p); + + /* stored key */ + encoded_result = pg_b64_encode((char *) stored_key, SCRAM_KEY_LEN, p, + encoded_stored_len); + if (encoded_result < 0) + { +#ifdef FRONTEND + free(result); + return NULL; +#else + elog(ERROR, "could not encode stored key"); +#endif + } + + p += encoded_result; *(p++) = ':'; - p += pg_b64_encode((char *) server_key, SCRAM_KEY_LEN, p); + + /* server key */ + encoded_result = pg_b64_encode((char *) server_key, SCRAM_KEY_LEN, p, + encoded_server_len); + if (encoded_result < 0) + { +#ifdef FRONTEND + free(result); + return NULL; +#else + elog(ERROR, "could not encode server key"); +#endif + } + + p += encoded_result; *(p++) = '\0'; Assert(p - result <= maxlen); diff --git a/src/common/unicode_norm.c b/src/common/unicode_norm.c index 6281f2222fe2..89c553321286 100644 --- a/src/common/unicode_norm.c +++ b/src/common/unicode_norm.c @@ -178,7 +178,7 @@ recompose_code(uint32 start, uint32 code, uint32 *result) ((start - SBASE) % TCOUNT) == 0 && code >= TBASE && code < (TBASE + TCOUNT)) { - /* make syllable of from LVT */ + /* make syllable of form LVT */ uint32 tindex = code - TBASE; *result = start + tindex; diff --git a/src/fe_utils/simple_list.c b/src/fe_utils/simple_list.c index 2232e8db73ef..b5f45126af76 100644 --- a/src/fe_utils/simple_list.c +++ b/src/fe_utils/simple_list.c @@ -99,6 +99,44 @@ simple_string_list_member(SimpleStringList *list, const char *val) return false; } +/* + * Destroy an OID list + */ +void +simple_oid_list_destroy(SimpleOidList *list) +{ + SimpleOidListCell *cell; + + cell = list->head; + while (cell != NULL) + { + SimpleOidListCell *next; + + next = cell->next; + pg_free(cell); + cell = next; + } +} + +/* + * Destroy a string list + */ +void +simple_string_list_destroy(SimpleStringList *list) +{ + SimpleStringListCell *cell; + + cell = list->head; + while (cell != NULL) + { + SimpleStringListCell *next; + + next = cell->next; + pg_free(cell); + cell = next; + } +} + /* * Find first not-touched list entry, if there is one. */ diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c index 7897186bc890..cd1f59150ea8 100644 --- a/src/fe_utils/string_utils.c +++ b/src/fe_utils/string_utils.c @@ -623,10 +623,10 @@ appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname) PQExpBufferData connstr; initPQExpBuffer(&connstr); - appendPQExpBuffer(&connstr, "dbname="); + appendPQExpBufferStr(&connstr, "dbname="); appendConnStrVal(&connstr, dbname); - appendPQExpBuffer(buf, "-reuse-previous=on "); + appendPQExpBufferStr(buf, "-reuse-previous=on "); /* * As long as the name does not contain a newline, SQL identifier diff --git a/src/include/access/ginblock.h b/src/include/access/ginblock.h index 5fd397c13604..e7d49b152eb9 100644 --- a/src/include/access/ginblock.h +++ b/src/include/access/ginblock.h @@ -171,9 +171,6 @@ typedef struct GinMetaPageData GinItemPointerGetBlockNumber(p) == (BlockNumber)0) #define ItemPointerSetMax(p) \ ItemPointerSet((p), InvalidBlockNumber, (OffsetNumber)0xffff) -#define ItemPointerIsMax(p) \ - (GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \ - GinItemPointerGetBlockNumber(p) == InvalidBlockNumber) #define ItemPointerSetLossyPage(p, b) \ ItemPointerSet((p), (b), (OffsetNumber)0xffff) #define ItemPointerIsLossyPage(p) \ diff --git a/src/include/access/ginxlog.h b/src/include/access/ginxlog.h index 2c5d743caceb..7ef3bcf76587 100644 --- a/src/include/access/ginxlog.h +++ b/src/include/access/ginxlog.h @@ -127,7 +127,7 @@ typedef struct ginxlogSplit /* * Vacuum simply WAL-logs the whole page, when anything is modified. This - * is functionally identical to heap_newpage records, but is kept separate for + * is functionally identical to XLOG_FPI records, but is kept separate for * debugging purposes. (When inspecting the WAL stream, it's easier to see * what's going on when GIN vacuum records are marked as such, not as heap * records.) This is currently only used for entry tree leaf pages. diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 6902f4115b7f..8292956cc099 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -16,6 +16,7 @@ #ifndef GIST_H #define GIST_H +#include "access/transam.h" #include "access/xlog.h" #include "access/xlogdefs.h" #include "storage/block.h" @@ -140,8 +141,6 @@ typedef struct GISTENTRY #define GIST_LEAF(entry) (GistPageIsLeaf((entry)->page)) #define GistPageIsDeleted(page) ( GistPageGetOpaque(page)->flags & F_DELETED) -#define GistPageSetDeleted(page) ( GistPageGetOpaque(page)->flags |= F_DELETED) -#define GistPageSetNonDeleted(page) ( GistPageGetOpaque(page)->flags &= ~F_DELETED) #define GistTuplesDeleted(page) ( GistPageGetOpaque(page)->flags & F_TUPLES_DELETED) #define GistMarkTuplesDeleted(page) ( GistPageGetOpaque(page)->flags |= F_TUPLES_DELETED) @@ -158,9 +157,45 @@ typedef struct GISTENTRY #define GistPageGetNSN(page) ( PageXLogRecPtrGet(GistPageGetOpaque(page)->nsn)) #define GistPageSetNSN(page, val) ( PageXLogRecPtrSet(GistPageGetOpaque(page)->nsn, val)) -/* For deleted pages we store last xid which could see the page in scan */ -#define GistPageGetDeleteXid(page) ( ((PageHeader) (page))->pd_prune_xid ) -#define GistPageSetDeleteXid(page, val) ( ((PageHeader) (page))->pd_prune_xid = val) + +/* + * On a deleted page, we store this struct. A deleted page doesn't contain any + * tuples, so we don't use the normal page layout with line pointers. Instead, + * this struct is stored right after the standard page header. pd_lower points + * to the end of this struct. If we add fields to this struct in the future, we + * can distinguish the old and new formats by pd_lower. + */ +typedef struct GISTDeletedPageContents +{ + /* last xid which could see the page in a scan */ + FullTransactionId deleteXid; +} GISTDeletedPageContents; + +static inline void +GistPageSetDeleted(Page page, FullTransactionId deletexid) +{ + Assert(PageIsEmpty(page)); + + GistPageGetOpaque(page)->flags |= F_DELETED; + ((PageHeader) page)->pd_lower = MAXALIGN(SizeOfPageHeaderData) + sizeof(GISTDeletedPageContents); + + ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid = deletexid; +} + +static inline FullTransactionId +GistPageGetDeleteXid(Page page) +{ + Assert(GistPageIsDeleted(page)); + + /* Is the deleteXid field present? */ + if (((PageHeader) page)->pd_lower >= MAXALIGN(SizeOfPageHeaderData) + + offsetof(GISTDeletedPageContents, deleteXid) + sizeof(FullTransactionId)) + { + return ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid; + } + else + return FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId); +} /* * Vector of GISTENTRY structs; user-defined methods union and picksplit diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index d89061f75cca..44fbaecdf689 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -418,7 +418,7 @@ extern void freeGISTstate(GISTSTATE *giststate); extern void gistdoinsert(Relation r, IndexTuple itup, Size freespace, - GISTSTATE *GISTstate, + GISTSTATE *giststate, Relation heapRel, bool is_build); @@ -435,7 +435,7 @@ extern bool gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, OffsetNumber oldoffnum, BlockNumber *newblkno, Buffer leftchildbuf, List **splitinfo, - bool markleftchild, + bool markfollowright, Relation heapRel, bool is_build); @@ -444,11 +444,11 @@ extern SplitedPageLayout *gistSplit(Relation r, Page page, IndexTuple *itup, /* gistxlog.c */ extern XLogRecPtr gistXLogPageDelete(Buffer buffer, - TransactionId xid, Buffer parentBuffer, + FullTransactionId xid, Buffer parentBuffer, OffsetNumber downlinkOffset); extern void gistXLogPageReuse(Relation rel, BlockNumber blkno, - TransactionId latestRemovedXid); + FullTransactionId latestRemovedXid); extern XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, @@ -491,8 +491,7 @@ extern bool gistPageRecyclable(Page page); extern void gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off); extern IndexTuple *gistextractpage(Page page, int *len /* out */ ); -extern IndexTuple *gistjoinvector( - IndexTuple *itvec, int *len, +extern IndexTuple *gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen); extern IndexTupleData *gistfillitupvec(IndexTuple *vec, int veclen, int *memlen); diff --git a/src/include/access/gistxlog.h b/src/include/access/gistxlog.h index 969a5376b5e7..e44922d915cc 100644 --- a/src/include/access/gistxlog.h +++ b/src/include/access/gistxlog.h @@ -83,7 +83,7 @@ typedef struct gistxlogPageSplit */ typedef struct gistxlogPageDelete { - TransactionId deleteXid; /* last Xid which could see page in scan */ + FullTransactionId deleteXid; /* last Xid which could see page in scan */ OffsetNumber downlinkOffset; /* Offset of downlink referencing this * page */ } gistxlogPageDelete; @@ -98,10 +98,10 @@ typedef struct gistxlogPageReuse { RelFileNode node; BlockNumber block; - TransactionId latestRemovedXid; + FullTransactionId latestRemovedFullXid; } gistxlogPageReuse; -#define SizeOfGistxlogPageReuse (offsetof(gistxlogPageReuse, latestRemovedXid) + sizeof(TransactionId)) +#define SizeOfGistxlogPageReuse (offsetof(gistxlogPageReuse, latestRemovedFullXid) + sizeof(FullTransactionId)) extern void gist_redo(XLogReaderState *record); extern void gist_desc(StringInfo buf, XLogReaderState *record); diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 605b53e0500a..b0900af2e163 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -112,7 +112,7 @@ extern TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot, ParallelTableScanDesc parallel_scan, uint32 flags); extern void heap_setscanlimits(TableScanDesc scan, BlockNumber startBlk, - BlockNumber endBlk); + BlockNumber numBlks); extern void heapgetpage(TableScanDesc scan, BlockNumber page); extern void heap_rescan(TableScanDesc scan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode); diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h index ca5c989cfacc..b63469907b26 100644 --- a/src/include/access/heapam_xlog.h +++ b/src/include/access/heapam_xlog.h @@ -195,14 +195,14 @@ typedef struct xl_multi_insert_tuple * * Backup blk 0: new page * - * If XLOG_HEAP_PREFIX_FROM_OLD or XLOG_HEAP_SUFFIX_FROM_OLD flags are set, + * If XLH_UPDATE_PREFIX_FROM_OLD or XLH_UPDATE_SUFFIX_FROM_OLD flags are set, * the prefix and/or suffix come first, as one or two uint16s. * * After that, xl_heap_header and new tuple data follow. The new tuple * data doesn't include the prefix and suffix, which are copied from the * old tuple on replay. * - * If HEAP_CONTAINS_NEW_TUPLE_DATA flag is given, the tuple data is + * If XLH_UPDATE_CONTAINS_NEW_TUPLE flag is given, the tuple data is * included even if a full-page image was taken. * * Backup blk 1: old page, if different. (no data, just a reference to the blk) @@ -217,8 +217,8 @@ typedef struct xl_heap_update OffsetNumber new_offnum; /* new tuple's offset */ /* - * If XLOG_HEAP_CONTAINS_OLD_TUPLE or XLOG_HEAP_CONTAINS_OLD_KEY flags are - * set, a xl_heap_header struct and tuple data for the old tuple follows. + * If XLH_UPDATE_CONTAINS_OLD_TUPLE or XLH_UPDATE_CONTAINS_OLD_KEY flags + * are set, xl_heap_header and tuple data for the old tuple follow. */ } xl_heap_update; diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index 7945f6c9a107..69ab85fd0e16 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -97,12 +97,12 @@ typedef BTPageOpaqueData *BTPageOpaque; typedef struct BTMetaPageData { uint32 btm_magic; /* should contain BTREE_MAGIC */ - uint32 btm_version; /* should contain BTREE_VERSION */ + uint32 btm_version; /* nbtree version (always <= BTREE_VERSION) */ BlockNumber btm_root; /* current root location */ uint32 btm_level; /* tree level of the root page */ BlockNumber btm_fastroot; /* current "fast" root location */ uint32 btm_fastlevel; /* tree level of the "fast" root page */ - /* following fields are available since page version 3 */ + /* remaining fields only valid when btm_version >= BTREE_NOVAC_VERSION */ TransactionId btm_oldest_btpo_xact; /* oldest btpo_xact among all deleted * pages */ float8 btm_last_cleanup_num_heap_tuples; /* number of heap tuples diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h index 4a389387325c..e0d1178f2457 100644 --- a/src/include/access/spgist_private.h +++ b/src/include/access/spgist_private.h @@ -328,7 +328,7 @@ typedef struct SpGistLeafTupleData { unsigned int tupstate:2, /* LIVE/REDIRECT/DEAD/PLACEHOLDER */ size:30; /* large enough for any palloc'able value */ - OffsetNumber nextOffset; /* next tuple in chain, or InvalidOffset */ + OffsetNumber nextOffset; /* next tuple in chain, or InvalidOffsetNumber */ ItemPointerData heapPtr; /* TID of represented heap tuple */ /* leaf datum follows */ } SpGistLeafTupleData; diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index f07ec3ec9cd4..802e05ae4e02 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -315,7 +315,7 @@ typedef struct TableAmRoutine * * *all_dead, if all_dead is not NULL, should be set to true by * index_fetch_tuple iff it is guaranteed that no backend needs to see - * that tuple. Index AMs can use that do avoid returning that tid in + * that tuple. Index AMs can use that to avoid returning that tid in * future searches. */ bool (*index_fetch_tuple) (struct IndexFetchTableData *scan, @@ -469,8 +469,8 @@ typedef struct TableAmRoutine * * Note that only the subset of the relcache filled by * RelationBuildLocalRelation() can be relied upon and that the relation's - * catalog entries either will either not yet exist (new relation), or - * will still reference the old relfilenode. + * catalog entries will either not yet exist (new relation), or will still + * reference the old relfilenode. * * As output *freezeXid, *minmulti must be set to the values appropriate * for pg_class.{relfrozenxid, relminmxid}. For AMs that don't need those @@ -517,9 +517,9 @@ typedef struct TableAmRoutine double *tups_recently_dead); /* - * React to VACUUM command on the relation. The VACUUM might be user - * triggered or by autovacuum. The specific actions performed by the AM - * will depend heavily on the individual AM. + * React to VACUUM command on the relation. The VACUUM can be + * triggered by a user or by autovacuum. The specific actions + * performed by the AM will depend heavily on the individual AM. * * On entry a transaction is already established, and the relation is * locked with a ShareUpdateExclusive lock. @@ -625,7 +625,7 @@ typedef struct TableAmRoutine * See table_relation_estimate_size(). * * While block oriented, it shouldn't be too hard for an AM that doesn't - * doesn't internally use blocks to convert into a usable representation. + * internally use blocks to convert into a usable representation. * * This differs from the relation_size callback by returning size * estimates (both relation size and tuple count) for planning purposes, @@ -695,7 +695,7 @@ typedef struct TableAmRoutine * false if the sample scan is finished, true otherwise. `scan` was * started via table_beginscan_sampling(). * - * Typically this will first determine the target block by call the + * Typically this will first determine the target block by calling the * TsmRoutine's NextSampleBlock() callback if not NULL, or alternatively * perform a sequential scan over all blocks. The determined block is * then typically read and pinned. @@ -713,7 +713,7 @@ typedef struct TableAmRoutine * * Currently it is required to implement this interface, as there's no * alternative way (contrary e.g. to bitmap scans) to implement sample - * scans. If infeasible to implement the AM may raise an error. + * scans. If infeasible to implement, the AM may raise an error. */ bool (*scan_sample_next_block) (TableScanDesc scan, struct SampleScanState *scanstate); @@ -1046,7 +1046,7 @@ table_index_fetch_end(struct IndexFetchTableData *scan) * * *all_dead, if all_dead is not NULL, will be set to true by * table_index_fetch_tuple() iff it is guaranteed that no backend needs to see - * that tuple. Index AMs can use that do avoid returning that tid in future + * that tuple. Index AMs can use that to avoid returning that tid in future * searches. * * The difference between this function and table_fetch_row_version is that @@ -1111,8 +1111,8 @@ table_index_fetch_tuple_exists(Relation rel, * true, false otherwise. * * See table_index_fetch_tuple's comment about what the difference between - * these functions is. This function is the correct to use outside of - * index entry->table tuple lookups. + * these functions is. It is correct to use this function outside of index + * entry->table tuple lookups. */ static inline bool table_tuple_fetch_row_version(Relation rel, @@ -1211,9 +1211,8 @@ table_dml_finish(Relation rel) /* * Insert a tuple from a slot into table AM routine. * - * The options bitmask allows to specify options that allow to change the - * behaviour of the AM. Several options might be ignored by AMs not supporting - * them. + * The options bitmask allows the caller to specify options that may change the + * behaviour of the AM. The AM will ignore options that it does not support. * * If the TABLE_INSERT_SKIP_WAL option is specified, the new tuple doesn't * need to be logged to WAL, even for a non-temp relation. It is the AMs @@ -1221,8 +1220,9 @@ table_dml_finish(Relation rel) * * If the TABLE_INSERT_SKIP_FSM option is specified, AMs are free to not reuse * free space in the relation. This can save some cycles when we know the - * relation is new and doesn't contain useful amounts of free space. It's - * commonly passed directly to RelationGetBufferForTuple, see for more info. + * relation is new and doesn't contain useful amounts of free space. + * TABLE_INSERT_SKIP_FSM is commonly passed directly to + * RelationGetBufferForTuple. See that method for more information. * * TABLE_INSERT_FROZEN should only be specified for inserts into * relfilenodes created during the current subtransaction and when @@ -1238,7 +1238,6 @@ table_dml_finish(Relation rel) * Note that most of these options will be applied when inserting into the * heap's TOAST table, too, if the tuple requires any out-of-line data. * - * * The BulkInsertState object (if any; bistate can be NULL for default * behavior) is also just passed through to RelationGetBufferForTuple. If * `bistate` is provided, table_finish_bulk_insert() needs to be called. @@ -1510,13 +1509,13 @@ table_relation_copy_data(Relation rel, const RelFileNode *newrnode) * Additional Input parameters: * - use_sort - if true, the table contents are sorted appropriate for * `OldIndex`; if false and OldIndex is not InvalidOid, the data is copied - * in that index's order; if false and OidIndex is InvalidOid, no sorting is + * in that index's order; if false and OldIndex is InvalidOid, no sorting is * performed - * - OidIndex - see use_sort + * - OldIndex - see use_sort * - OldestXmin - computed by vacuum_set_xid_limits(), even when * not needed for the relation's AM - * - *xid_cutoff - dito - * - *multi_cutoff - dito + * - *xid_cutoff - ditto + * - *multi_cutoff - ditto * * Output parameters: * - *xid_cutoff - rel's new relfrozenxid value, may be invalid @@ -1543,10 +1542,10 @@ table_relation_copy_for_cluster(Relation OldTable, Relation NewTable, } /* - * Perform VACUUM on the relation. The VACUUM can be user-triggered or by + * Perform VACUUM on the relation. The VACUUM can be triggered by a user or by * autovacuum. The specific actions performed by the AM will depend heavily on * the individual AM. - + * * On entry a transaction needs to already been established, and the * table is locked with a ShareUpdateExclusive lock. * @@ -1853,6 +1852,20 @@ extern void table_block_parallelscan_startblock_init(Relation rel, ParallelBlockTableScanDesc pbscan); +/* ---------------------------------------------------------------------------- + * Helper functions to implement relation sizing for block oriented AMs. + * ---------------------------------------------------------------------------- + */ + +extern uint64 table_block_relation_size(Relation rel, ForkNumber forkNumber); +extern void table_block_relation_estimate_size(Relation rel, + int32 *attr_widths, + BlockNumber *pages, + double *tuples, + double *allvisfrac, + Size overhead_bytes_per_tuple, + Size usable_bytes_per_page); + /* ---------------------------------------------------------------------------- * Functions in tableamapi.c * ---------------------------------------------------------------------------- diff --git a/src/include/access/timeline.h b/src/include/access/timeline.h index a6dc2edb8939..e83a73a3f1e8 100644 --- a/src/include/access/timeline.h +++ b/src/include/access/timeline.h @@ -36,7 +36,7 @@ extern void writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI, XLogRecPtr switchpoint, char *reason); extern void writeTimeLineHistoryFile(TimeLineID tli, char *content, int size); extern void restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end); -extern bool tliInHistory(TimeLineID tli, List *expectedTLIs); +extern bool tliInHistory(TimeLineID tli, List *expectedTLEs); extern TimeLineID tliOfPointInHistory(XLogRecPtr ptr, List *history); extern XLogRecPtr tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI); diff --git a/src/include/access/xact.h b/src/include/access/xact.h index d621cf146384..2354992ac01e 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -308,9 +308,9 @@ typedef struct xl_xact_abort /* xl_xact_xinfo follows if XLOG_XACT_HAS_INFO */ /* xl_xact_dbinfo follows if XINFO_HAS_DBINFO */ - /* xl_xact_subxacts follows if HAS_SUBXACT */ - /* xl_xact_relfilenodes follows if HAS_RELFILENODES */ - /* xl_xact_deldbs follows if HAS_DELDBS */ + /* xl_xact_subxacts follows if XINFO_HAS_SUBXACT */ + /* xl_xact_relfilenodes follows if XINFO_HAS_RELFILENODES */ + /* xl_xact_deldbs follows if XACT_XINFO_HAS_DELDBS */ /* No invalidation messages needed. */ /* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */ /* twophase_gid follows if XINFO_HAS_GID. As a null-terminated string. */ diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 370f29f6ef79..43a3360f89f9 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -344,7 +344,7 @@ extern bool RestoreArchivedFile(char *path, const char *xlogfname, const char *recovername, off_t expectedSize, bool cleanupEnabled); extern void ExecuteRecoveryCommand(const char *command, const char *commandName, - bool failOnerror); + bool failOnSignal); extern void KeepFileRestoredFromArchive(const char *path, const char *xlogfname); extern void XLogArchiveNotify(const char *xlog); extern void XLogArchiveNotifySeg(XLogSegNo segno); diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h index 73ed37b2cd6a..e5905438fce3 100644 --- a/src/include/access/xlogreader.h +++ b/src/include/access/xlogreader.h @@ -25,6 +25,10 @@ #ifndef XLOGREADER_H #define XLOGREADER_H +#ifndef FRONTEND +#include "access/transam.h" +#endif + #include "access/xlogrecord.h" typedef struct XLogReaderState XLogReaderState; @@ -255,6 +259,10 @@ extern bool DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, #define XLogRecBlockImageApply(decoder, block_id) \ ((decoder)->blocks[block_id].apply_image) +#ifndef FRONTEND +extern FullTransactionId XLogRecGetFullXid(XLogReaderState *record); +#endif + extern bool RestoreBlockImage(XLogReaderState *recoder, uint8 block_id, char *dst); extern char *XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len); extern bool XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id, diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index bc0e8794107f..5c097ce4d67e 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -159,7 +159,7 @@ extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, - bool ignore_self); + bool reverse_self); extern ObjectClass getObjectClass(const ObjectAddress *object); @@ -214,8 +214,8 @@ extern long changeDependenciesOn(Oid refClassId, Oid oldRefObjectId, extern Oid getExtensionOfObject(Oid classId, Oid objectId); extern bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId); -extern List *getOwnedSequences(Oid relid, AttrNumber attnum); -extern Oid getOwnedSequence(Oid relid, AttrNumber attnum); +extern List *getOwnedSequences(Oid relid); +extern Oid getIdentitySequence(Oid relid, AttrNumber attnum, bool missing_ok); extern Oid get_constraint_index(Oid constraintId); diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat index f0847ecdaa2d..75bc06c0150d 100644 --- a/src/include/catalog/pg_aggregate.dat +++ b/src/include/catalog/pg_aggregate.dat @@ -146,6 +146,9 @@ { aggfnoid => 'max(inet)', aggtransfn => 'network_larger', aggcombinefn => 'network_larger', aggsortop => '>(inet,inet)', aggtranstype => 'inet' }, +{ aggfnoid => 'max(pg_lsn)', aggtransfn => 'pg_lsn_larger', + aggcombinefn => 'pg_lsn_larger', aggsortop => '>(pg_lsn,pg_lsn)', + aggtranstype => 'pg_lsn' }, # min { aggfnoid => 'min(int8)', aggtransfn => 'int8smaller', @@ -208,6 +211,9 @@ { aggfnoid => 'min(inet)', aggtransfn => 'network_smaller', aggcombinefn => 'network_smaller', aggsortop => '<(inet,inet)', aggtranstype => 'inet' }, +{ aggfnoid => 'min(pg_lsn)', aggtransfn => 'pg_lsn_smaller', + aggcombinefn => 'pg_lsn_smaller', aggsortop => '<(pg_lsn,pg_lsn)', + aggtranstype => 'pg_lsn' }, # count { aggfnoid => 'count(any)', aggtransfn => 'int8inc_any', diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 2b96b184a0b1..449ec5c7872a 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1081,6 +1081,9 @@ amopstrategy => '13', amopopr => '~(box,box)', amopmethod => 'gist' }, { amopfamily => 'gist/box_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '14', amopopr => '@(box,box)', amopmethod => 'gist' }, +{ amopfamily => 'gist/box_ops', amoplefttype => 'box', amoprighttype => 'point', + amopstrategy => '15', amoppurpose => 'o', amopopr => '<->(box,point)', + amopmethod => 'gist', amopsortfamily => 'btree/float_ops' }, # gist point_ops { amopfamily => 'gist/point_ops', amoplefttype => 'point', @@ -1543,6 +1546,10 @@ amopstrategy => '11', amopopr => '|>>(box,box)', amopmethod => 'spgist' }, { amopfamily => 'spgist/box_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '12', amopopr => '|&>(box,box)', amopmethod => 'spgist' }, +{ amopfamily => 'spgist/box_ops', amoplefttype => 'box', + amoprighttype => 'point', amopstrategy => '15', amoppurpose => 'o', + amopopr => '<->(box,point)', amopmethod => 'spgist', + amopsortfamily => 'btree/float_ops' }, # SP-GiST poly_ops (supports polygons) { amopfamily => 'spgist/poly_ops', amoplefttype => 'polygon', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index ab7306650357..d976dff35f53 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -423,6 +423,8 @@ amprocrighttype => 'box', amprocnum => '6', amproc => 'gist_box_picksplit' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '7', amproc => 'gist_box_same' }, +{ amprocfamily => 'gist/box_ops', amproclefttype => 'box', + amprocrighttype => 'box', amprocnum => '8', amproc => 'gist_box_distance' }, { amprocfamily => 'gist/poly_ops', amproclefttype => 'polygon', amprocrighttype => 'polygon', amprocnum => '1', amproc => 'gist_poly_consistent' }, diff --git a/src/include/catalog/pg_conversion.dat b/src/include/catalog/pg_conversion.dat index 17eff20e2f72..2dacc59addd6 100644 --- a/src/include/catalog/pg_conversion.dat +++ b/src/include/catalog/pg_conversion.dat @@ -15,12 +15,6 @@ [ -{ oid => '4400', descr => 'conversion for SQL_ASCII to MULE_INTERNAL', - conname => 'ascii_to_mic', conforencoding => 'PG_SQL_ASCII', - contoencoding => 'PG_MULE_INTERNAL', conproc => 'ascii_to_mic' }, -{ oid => '4401', descr => 'conversion for MULE_INTERNAL to SQL_ASCII', - conname => 'mic_to_ascii', conforencoding => 'PG_MULE_INTERNAL', - contoencoding => 'PG_SQL_ASCII', conproc => 'mic_to_ascii' }, { oid => '4402', descr => 'conversion for KOI8R to MULE_INTERNAL', conname => 'koi8_r_to_mic', conforencoding => 'PG_KOI8R', contoencoding => 'PG_MULE_INTERNAL', conproc => 'koi8r_to_mic' }, @@ -165,12 +159,6 @@ { oid => '4449', descr => 'conversion for MULE_INTERNAL to LATIN4', conname => 'mic_to_iso_8859_4', conforencoding => 'PG_MULE_INTERNAL', contoencoding => 'PG_LATIN4', conproc => 'mic_to_latin4' }, -{ oid => '4450', descr => 'conversion for SQL_ASCII to UTF8', - conname => 'ascii_to_utf8', conforencoding => 'PG_SQL_ASCII', - contoencoding => 'PG_UTF8', conproc => 'ascii_to_utf8' }, -{ oid => '4451', descr => 'conversion for UTF8 to SQL_ASCII', - conname => 'utf8_to_ascii', conforencoding => 'PG_UTF8', - contoencoding => 'PG_SQL_ASCII', conproc => 'utf8_to_ascii' }, { oid => '4452', descr => 'conversion for BIG5 to UTF8', conname => 'big5_to_utf8', conforencoding => 'PG_BIG5', contoencoding => 'PG_UTF8', conproc => 'big5_to_utf8' }, diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat index 66a9ff32a142..58b9487c4511 100644 --- a/src/include/catalog/pg_operator.dat +++ b/src/include/catalog/pg_operator.dat @@ -671,22 +671,40 @@ { oid => '613', descr => 'distance between', oprname => '<->', oprleft => 'point', oprright => 'line', - oprresult => 'float8', oprcode => 'dist_pl' }, + oprresult => 'float8', oprcom => '<->(line,point)',oprcode => 'dist_pl' }, +{ oid => '760', descr => 'distance between', + oprname => '<->', oprleft => 'line', oprright => 'point', + oprresult => 'float8', oprcom => '<->(point,line)', oprcode => 'dist_lp' }, { oid => '614', descr => 'distance between', oprname => '<->', oprleft => 'point', oprright => 'lseg', - oprresult => 'float8', oprcode => 'dist_ps' }, + oprresult => 'float8', oprcom => '<->(lseg,point)',oprcode => 'dist_ps' }, +{ oid => '761', descr => 'distance between', + oprname => '<->', oprleft => 'lseg', oprright => 'point', + oprresult => 'float8', oprcom => '<->(point,lseg)', oprcode => 'dist_sp' }, { oid => '615', descr => 'distance between', oprname => '<->', oprleft => 'point', oprright => 'box', - oprresult => 'float8', oprcode => 'dist_pb' }, + oprresult => 'float8', oprcom => '<->(box,point)', oprcode => 'dist_pb' }, +{ oid => '606', descr => 'distance between', + oprname => '<->', oprleft => 'box', oprright => 'point', + oprresult => 'float8', oprcom => '<->(point,box)', oprcode => 'dist_bp' }, { oid => '616', descr => 'distance between', oprname => '<->', oprleft => 'lseg', oprright => 'line', - oprresult => 'float8', oprcode => 'dist_sl' }, + oprresult => 'float8', oprcom => '<->(line,lseg)', oprcode => 'dist_sl' }, +{ oid => '762', descr => 'distance between', + oprname => '<->', oprleft => 'line', oprright => 'lseg', + oprresult => 'float8', oprcom => '<->(lseg,line)', oprcode => 'dist_ls' }, { oid => '617', descr => 'distance between', oprname => '<->', oprleft => 'lseg', oprright => 'box', oprresult => 'float8', - oprcode => 'dist_sb' }, + oprcom => '<->(box,lseg)', oprcode => 'dist_sb' }, +{ oid => '763', descr => 'distance between', + oprname => '<->', oprleft => 'box', oprright => 'lseg', oprresult => 'float8', + oprcom => '<->(lseg,box)', oprcode => 'dist_bs' }, { oid => '618', descr => 'distance between', oprname => '<->', oprleft => 'point', oprright => 'path', - oprresult => 'float8', oprcode => 'dist_ppath' }, + oprresult => 'float8', oprcom => '<->(path,point)', oprcode => 'dist_ppath' }, +{ oid => '784', descr => 'distance between', + oprname => '<->', oprleft => 'path', oprright => 'point', + oprresult => 'float8', oprcom => '<->(point,path)', oprcode => 'dist_pathp' }, { oid => '620', descr => 'equal', oid_symbol => 'Float4EqualOperator', @@ -1714,12 +1732,20 @@ oprcode => 'dist_polyp' }, { oid => '1523', descr => 'distance between', oprname => '<->', oprleft => 'circle', oprright => 'polygon', - oprresult => 'float8', oprcode => 'dist_cpoly' }, + oprresult => 'float8', oprcom => '<->(polygon,circle)', + oprcode => 'dist_cpoly' }, +{ oid => '1383', descr => 'distance between', + oprname => '<->', oprleft => 'polygon', oprright => 'circle', + oprresult => 'float8', oprcom => '<->(circle,polygon)', + oprcode => 'dist_polyc' }, # additional geometric operators - thomas 1997-07-09 { oid => '1524', descr => 'distance between', oprname => '<->', oprleft => 'line', oprright => 'box', oprresult => 'float8', - oprcode => 'dist_lb' }, + oprcom => '<->(box,line)', oprcode => 'dist_lb' }, +{ oid => '1382', descr => 'distance between', + oprname => '<->', oprleft => 'box', oprright => 'line', oprresult => 'float8', + oprcom => '<->(line,box)', oprcode => 'dist_bl' }, { oid => '1525', descr => 'intersect', oprname => '?#', oprleft => 'lseg', oprright => 'lseg', oprresult => 'bool', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index ea406de8982b..5de6c6e4649e 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1075,12 +1075,21 @@ { oid => '363', proname => 'dist_ps', prorettype => 'float8', proargtypes => 'point lseg', prosrc => 'dist_ps' }, +{ oid => '380', + proname => 'dist_sp', prorettype => 'float8', proargtypes => 'lseg point', + prosrc => 'dist_sp' }, { oid => '364', proname => 'dist_pb', prorettype => 'float8', proargtypes => 'point box', prosrc => 'dist_pb' }, +{ oid => '357', + proname => 'dist_bp', prorettype => 'float8', proargtypes => 'box point', + prosrc => 'dist_bp' }, { oid => '365', proname => 'dist_sb', prorettype => 'float8', proargtypes => 'lseg box', prosrc => 'dist_sb' }, +{ oid => '381', + proname => 'dist_bs', prorettype => 'float8', proargtypes => 'box lseg', + prosrc => 'dist_bs' }, { oid => '366', proname => 'close_ps', prorettype => 'point', proargtypes => 'point lseg', prosrc => 'close_ps' }, @@ -1099,6 +1108,9 @@ { oid => '371', proname => 'dist_ppath', prorettype => 'float8', proargtypes => 'point path', prosrc => 'dist_ppath' }, +{ oid => '421', + proname => 'dist_pathp', prorettype => 'float8', proargtypes => 'path point', + prosrc => 'dist_pathp' }, { oid => '372', proname => 'on_sb', prorettype => 'bool', proargtypes => 'lseg box', prosrc => 'on_sb' }, @@ -1416,15 +1428,28 @@ { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', prosrc => 'dist_pl' }, +{ oid => '702', + proname => 'dist_lp', prorettype => 'float8', proargtypes => 'line point', + prosrc => 'dist_lp' }, { oid => '726', proname => 'dist_lb', prorettype => 'float8', proargtypes => 'line box', prosrc => 'dist_lb' }, +{ oid => '703', + proname => 'dist_bl', prorettype => 'float8', proargtypes => 'box line', + prosrc => 'dist_bl' }, { oid => '727', proname => 'dist_sl', prorettype => 'float8', proargtypes => 'lseg line', prosrc => 'dist_sl' }, +{ oid => '704', + proname => 'dist_ls', prorettype => 'float8', proargtypes => 'line lseg', + prosrc => 'dist_ls' }, + { oid => '728', proname => 'dist_cpoly', prorettype => 'float8', proargtypes => 'circle polygon', prosrc => 'dist_cpoly' }, +{ oid => '785', + proname => 'dist_polyc', prorettype => 'float8', + proargtypes => 'polygon circle', prosrc => 'dist_polyc' }, { oid => '729', proname => 'poly_distance', prorettype => 'float8', proargtypes => 'polygon polygon', prosrc => 'poly_distance' }, @@ -5098,7 +5123,7 @@ { oid => '3427', descr => 'details about MCV list items', proname => 'pg_mcv_list_items', prorows => '1000', proretset => 't', provolatile => 's', prorettype => 'record', proargtypes => 'pg_mcv_list', - proallargtypes => '{pg_mcv_list,int4,text,_bool,float8,float8}', + proallargtypes => '{pg_mcv_list,int4,_text,_bool,float8,float8}', proargmodes => '{i,o,o,o,o,o}', proargnames => '{mcv_list,index,values,nulls,frequency,base_frequency}', prosrc => 'pg_stats_ext_mcvlist_items' }, @@ -6307,6 +6332,9 @@ { oid => '3564', descr => 'maximum value of all inet input values', proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'inet', proargtypes => 'inet', prosrc => 'aggregate_dummy' }, +{ oid => '4189', descr => 'maximum value of all pg_lsn input values', + proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'pg_lsn', + proargtypes => 'pg_lsn', prosrc => 'aggregate_dummy' }, { oid => '2131', descr => 'minimum value of all bigint input values', proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'int8', @@ -6371,6 +6399,9 @@ { oid => '3565', descr => 'minimum value of all inet input values', proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'inet', proargtypes => 'inet', prosrc => 'aggregate_dummy' }, +{ oid => '4190', descr => 'minimum value of all pg_lsn input values', + proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'pg_lsn', + proargtypes => 'pg_lsn', prosrc => 'aggregate_dummy' }, # count has two forms: count(any) and count(*) { oid => '2147', @@ -7907,6 +7938,10 @@ { oid => '2584', descr => 'GiST support', proname => 'gist_box_same', prorettype => 'internal', proargtypes => 'box box internal', prosrc => 'gist_box_same' }, +{ oid => '3998', descr => 'GiST support', + proname => 'gist_box_distance', prorettype => 'float8', + proargtypes => 'internal box int2 oid internal', + prosrc => 'gist_box_distance' }, { oid => '2585', descr => 'GiST support', proname => 'gist_poly_consistent', prorettype => 'bool', proargtypes => 'internal polygon int2 oid internal', @@ -8435,6 +8470,9 @@ { oid => '3412', descr => 'hash', proname => 'uuid_hash_extended', prorettype => 'int8', proargtypes => 'uuid int8', prosrc => 'uuid_hash_extended' }, +{ oid => '3432', descr => 'generate random UUID', + proname => 'gen_random_uuid', proleakproof => 't', prorettype => 'uuid', + proargtypes => '', prosrc => 'gen_random_uuid' }, # pg_lsn { oid => '3229', descr => 'I/O', @@ -8479,6 +8517,12 @@ { oid => '3413', descr => 'hash', proname => 'pg_lsn_hash_extended', prorettype => 'int8', proargtypes => 'pg_lsn int8', prosrc => 'pg_lsn_hash_extended' }, +{ oid => '4187', descr => 'larger of two', + proname => 'pg_lsn_larger', prorettype => 'pg_lsn', + proargtypes => 'pg_lsn pg_lsn', prosrc => 'pg_lsn_larger' }, +{ oid => '4188', descr => 'smaller of two', + proname => 'pg_lsn_smaller', prorettype => 'pg_lsn', + proargtypes => 'pg_lsn pg_lsn', prosrc => 'pg_lsn_smaller' }, # enum related procs { oid => '3504', descr => 'I/O', @@ -10175,16 +10219,6 @@ prosrc => 'binary_upgrade_set_next_pg_namespace_oid' }, # conversion functions -{ oid => '4300', - descr => 'internal conversion function for SQL_ASCII to MULE_INTERNAL', - proname => 'ascii_to_mic', prolang => 'c', prorettype => 'void', - proargtypes => 'int4 int4 cstring internal int4', prosrc => 'ascii_to_mic', - probin => '$libdir/ascii_and_mic' }, -{ oid => '4301', - descr => 'internal conversion function for MULE_INTERNAL to SQL_ASCII', - proname => 'mic_to_ascii', prolang => 'c', prorettype => 'void', - proargtypes => 'int4 int4 cstring internal int4', prosrc => 'mic_to_ascii', - probin => '$libdir/ascii_and_mic' }, { oid => '4302', descr => 'internal conversion function for KOI8R to MULE_INTERNAL', proname => 'koi8r_to_mic', prolang => 'c', prorettype => 'void', @@ -10417,16 +10451,6 @@ proname => 'mic_to_latin4', prolang => 'c', prorettype => 'void', proargtypes => 'int4 int4 cstring internal int4', prosrc => 'mic_to_latin4', probin => '$libdir/latin_and_mic' }, -{ oid => '4350', - descr => 'internal conversion function for SQL_ASCII to UTF8', - proname => 'ascii_to_utf8', prolang => 'c', prorettype => 'void', - proargtypes => 'int4 int4 cstring internal int4', prosrc => 'ascii_to_utf8', - probin => '$libdir/utf8_and_ascii' }, -{ oid => '4351', - descr => 'internal conversion function for UTF8 to SQL_ASCII', - proname => 'utf8_to_ascii', prolang => 'c', prorettype => 'void', - proargtypes => 'int4 int4 cstring internal int4', prosrc => 'utf8_to_ascii', - probin => '$libdir/utf8_and_ascii' }, { oid => '4352', descr => 'internal conversion function for BIG5 to UTF8', proname => 'big5_to_utf8', prolang => 'c', prorettype => 'void', proargtypes => 'int4 int4 cstring internal int4', prosrc => 'big5_to_utf8', diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h index 28bf21153d89..154c8157ee22 100644 --- a/src/include/commands/dbcommands.h +++ b/src/include/commands/dbcommands.h @@ -26,7 +26,7 @@ extern Oid AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTop extern Oid AlterDatabaseSet(AlterDatabaseSetStmt *stmt); extern ObjectAddress AlterDatabaseOwner(const char *dbname, Oid newOwnerId); -extern Oid get_database_oid(const char *dbname, bool missingok); +extern Oid get_database_oid(const char *dbname, bool missing_ok); extern char *get_database_name(Oid dbid); extern void check_encoding_locale_matches(int encoding, const char *collate, const char *ctype); diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index e820ed34872e..85017a96c1b9 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -79,8 +79,6 @@ extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass); extern ObjectAddress renameatt(RenameStmt *stmt); -extern ObjectAddress renameatt_type(RenameStmt *stmt); - extern ObjectAddress RenameConstraint(RenameStmt *stmt); extern ObjectAddress RenameRelation(RenameStmt *stmt); diff --git a/src/include/common/base64.h b/src/include/common/base64.h index 1bae5ec9664c..c30b17348338 100644 --- a/src/include/common/base64.h +++ b/src/include/common/base64.h @@ -11,8 +11,8 @@ #define BASE64_H /* base 64 */ -extern int pg_b64_encode(const char *src, int len, char *dst); -extern int pg_b64_decode(const char *src, int len, char *dst); +extern int pg_b64_encode(const char *src, int len, char *dst, int dstlen); +extern int pg_b64_decode(const char *src, int len, char *dst, int dstlen); extern int pg_b64_enc_len(int srclen); extern int pg_b64_dec_len(int srclen); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 2885818f60f2..304382bcecae 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -360,7 +360,7 @@ ExecEvalExprSwitchContext(ExprState *state, * ExecProject * * Projects a tuple based on projection info and stores it in the slot passed - * to ExecBuildProjectInfo(). + * to ExecBuildProjectionInfo(). * * Note: the result is always a virtual tuple; therefore it may reference * the contents of the exprContext's scan tuples and/or temporary results diff --git a/src/include/executor/nodeAgg.h b/src/include/executor/nodeAgg.h index d56742807263..324bf273e005 100644 --- a/src/include/executor/nodeAgg.h +++ b/src/include/executor/nodeAgg.h @@ -308,7 +308,7 @@ typedef struct AggStatePerHashData int numhashGrpCols; /* number of columns in hash table */ int largestGrpColIdx; /* largest col required for hashing */ AttrNumber *hashGrpColIdxInput; /* hash col indices in input slot */ - AttrNumber *hashGrpColIdxHash; /* indices in hashtbl tuples */ + AttrNumber *hashGrpColIdxHash; /* indices in hash table tuples */ Agg *aggnode; /* original Agg node, for numGroups etc. */ } AggStatePerHashData; diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h index d59454a82cef..3068c988ba4f 100644 --- a/src/include/executor/spi.h +++ b/src/include/executor/spi.h @@ -21,11 +21,14 @@ typedef struct SPITupleTable { - MemoryContext tuptabcxt; /* memory context of result table */ - uint64 alloced; /* # of alloced vals */ - uint64 free; /* # of free vals */ + /* Public members */ TupleDesc tupdesc; /* tuple descriptor */ - HeapTuple *vals; /* tuples */ + HeapTuple *vals; /* array of tuples */ + uint64 numvals; /* number of valid tuples */ + + /* Private members, not intended for external callers */ + uint64 alloced; /* allocated length of vals array */ + MemoryContext tuptabcxt; /* memory context of result table */ slist_node next; /* link for internal bookkeeping */ SubTransactionId subid; /* subxact in which tuptable was created */ } SPITupleTable; diff --git a/src/include/executor/tablefunc.h b/src/include/executor/tablefunc.h index 926bd570145c..838c2c2ca297 100644 --- a/src/include/executor/tablefunc.h +++ b/src/include/executor/tablefunc.h @@ -20,11 +20,11 @@ struct TableFuncScanState; * TableFuncRoutine holds function pointers used for generating content of * table-producer functions, such as XMLTABLE. * - * InitBuilder initialize table builder private objects. The output tuple + * InitOpaque initializes table builder private objects. The output tuple * descriptor, input functions for the columns, and typioparams are passed * from executor state. * - * SetDoc is called to define the input document. The table builder may + * SetDocument is called to define the input document. The table builder may * apply additional transformations not exposed outside the table builder * context. * @@ -45,7 +45,7 @@ struct TableFuncScanState; * builder context such that each subsequent GetValue call returns the values * for the indicated column for the row being processed. * - * DestroyBuilder shall release all resources associated with a table builder + * DestroyOpaque shall release all resources associated with a table builder * context. It may be called either because all rows have been consumed, or * because an error occurred while processing the table expression. */ diff --git a/src/include/fe_utils/simple_list.h b/src/include/fe_utils/simple_list.h index 04dfa2cecfb4..103fd88c74fc 100644 --- a/src/include/fe_utils/simple_list.h +++ b/src/include/fe_utils/simple_list.h @@ -57,9 +57,11 @@ typedef struct SimplePtrList extern void simple_oid_list_append(SimpleOidList *list, Oid val); extern bool simple_oid_list_member(SimpleOidList *list, Oid val); +extern void simple_oid_list_destroy(SimpleOidList *list); extern void simple_string_list_append(SimpleStringList *list, const char *val); extern bool simple_string_list_member(SimpleStringList *list, const char *val); +extern void simple_string_list_destroy(SimpleStringList *list); extern const char *simple_string_list_not_touched(SimpleStringList *list); diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index 802be53cfc94..fe8d24ce0629 100644 --- a/src/include/foreign/foreign.h +++ b/src/include/foreign/foreign.h @@ -21,17 +21,6 @@ (OidIsValid(userid) ? GetUserNameFromId(userid, false) : "public") -/* - * Generic option types for validation. - * NB! These are treated as flags, so use only powers of two here. - */ -typedef enum -{ - ServerOpt = 1, /* options applicable to SERVER */ - UserMappingOpt = 2, /* options for USER MAPPING */ - FdwOpt = 4 /* options for FOREIGN DATA WRAPPER */ -} GenericOptionFlags; - typedef struct ForeignDataWrapper { Oid fdwid; /* FDW Oid */ diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index af3a1d31aa16..947342d723e5 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -210,19 +210,20 @@ typedef struct Port * Hardcoded DH parameters, used in ephemeral DH keying. (See also * README.SSL for more details on EDH.) * - * If you want to create your own hardcoded DH parameters - * for fun and profit, review "Assigned Number for SKIP - * Protocols" (http://www.skip-vpn.org/spec/numbers.html) - * for suggestions. + * This is the 2048-bit DH parameter from RFC 3526. The generation of the + * prime is specified in RFC 2412 Appendix E, which also discusses the + * design choice of the generator. Note that when loaded with OpenSSL + * this causes DH_check() to fail on DH_NOT_SUITABLE_GENERATOR, where + * leaking a bit is preferred. */ #define FILE_DH2048 \ "-----BEGIN DH PARAMETERS-----\n\ -MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\ -89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\ -T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\ -zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\ -Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\ -CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\ +MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n\ +IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft\n\ +awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n\ +mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n\ +fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n\ +5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==\n\ -----END DH PARAMETERS-----\n" /* diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h index ecde1dd00b1c..48f3c1bcd9c1 100644 --- a/src/include/mb/pg_wchar.h +++ b/src/include/mb/pg_wchar.h @@ -544,7 +544,7 @@ extern int pg_mbstrlen_with_len(const char *mbstr, int len); extern int pg_mbcliplen(const char *mbstr, int len, int limit); extern int pg_encoding_mbcliplen(int encoding, const char *mbstr, int len, int limit); -extern int pg_mbcharcliplen(const char *mbstr, int len, int imit); +extern int pg_mbcharcliplen(const char *mbstr, int len, int limit); extern int pg_encoding_max_length(int encoding); extern int pg_database_encoding_max_length(void); extern mbcharacter_incrementer pg_database_encoding_character_incrementer(void); @@ -616,8 +616,6 @@ extern void report_untranslatable_char(int src_encoding, int dest_encoding, extern void local2local(const unsigned char *l, unsigned char *p, int len, int src_encoding, int dest_encoding, const unsigned char *tab); -extern void pg_ascii2mic(const unsigned char *l, unsigned char *p, int len); -extern void pg_mic2ascii(const unsigned char *mic, unsigned char *p, int len); extern void latin2mic(const unsigned char *l, unsigned char *p, int len, int lc, int encoding); extern void mic2latin(const unsigned char *mic, unsigned char *p, int len, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index bc49c7b1f9eb..47c789eddaa8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -623,7 +623,7 @@ typedef struct EState * and with which options. es_jit is created on-demand when JITing is * performed. * - * es_jit_combined_instr is the combined, on demand allocated, + * es_jit_worker_instr is the combined, on demand allocated, * instrumentation from all workers. The leader's instrumentation is kept * separate, and is combined on demand by ExplainPrintJITSummary(). */ diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index ad7b41d4aa60..8032bb7aa2a2 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * makefuncs.h - * prototypes for the creator functions (for primitive nodes) + * prototypes for the creator functions of various nodes * * * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group @@ -14,6 +14,7 @@ #ifndef MAKEFUNC_H #define MAKEFUNC_H +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" @@ -92,6 +93,10 @@ extern Node *make_and_qual(Node *qual1, Node *qual2); extern Expr *make_ands_explicit(List *andclauses); extern List *make_ands_implicit(Expr *clause); +extern IndexInfo *makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, + List *expressions, List *predicates, + bool unique, bool isready, bool concurrent); + extern DefElem *makeDefElem(char *name, Node *arg, int location); extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg, DefElemAction defaction, int location); diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index a0ed3a9f4fe1..40062dfb80bf 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -338,6 +338,8 @@ struct PlannerInfo List *non_eq_clauses; /* list of non-equivalence clauses */ + bool ec_merging_done; /* set true once ECs are canonical */ + List *canon_pathkeys; /* list of "canonical" PathKeys */ List *list_cteplaninfo; /* list of CtePlannerInfo, one for each CTE */ @@ -676,6 +678,8 @@ static inline void planner_subplan_put_plan(struct PlannerInfo *root, SubPlan *s * pages - number of disk pages in relation (zero if not a table) * tuples - number of tuples in relation (not considering restrictions) * allvisfrac - fraction of disk pages that are marked all-visible + * eclass_indexes - EquivalenceClasses that mention this rel (filled + * only after EC merging is complete) * subroot - PlannerInfo for subquery (NULL if it's not a subquery) * subplan_params - list of PlannerParamItems to be passed to subquery * @@ -855,6 +859,8 @@ typedef struct RelOptInfo struct GpPolicy *cdbpolicy; /* distribution of stored tuples */ Oid relam; /* form_pg_class access method */ double allvisfrac; + Bitmapset *eclass_indexes; /* Indexes in PlannerInfo's eq_classes list of + * ECs that mention this rel */ PlannerInfo *subroot; /* if subquery (in GPDB: or CTE) */ List *subplan_params; /* if subquery */ int rel_parallel_workers; /* wanted number of parallel workers */ diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index 6eb67f975e94..d7585eed6269 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -1,19 +1,19 @@ /*------------------------------------------------------------------------- * * pg_list.h - * interface for PostgreSQL generic linked list package + * interface for PostgreSQL generic list package * - * This package implements singly-linked homogeneous lists. + * Once upon a time, parts of Postgres were written in Lisp and used real + * cons-cell lists for major data structures. When that code was rewritten + * in C, we initially had a faithful emulation of cons-cell lists, which + * unsurprisingly was a performance bottleneck. A couple of major rewrites + * later, these data structures are actually simple expansible arrays; + * but the "List" name and a lot of the notation survives. * - * It is important to have constant-time length, append, and prepend - * operations. To achieve this, we deal with two distinct data - * structures: - * - * 1. A set of "list cells": each cell contains a data field and - * a link to the next cell in the list or NULL. - * 2. A single structure containing metadata about the list: the - * type of the list, pointers to the head and tail cells, and - * the length of the list. + * One important concession to the original implementation is that an empty + * list is always represented by a null pointer (preferentially written NIL). + * Non-empty lists have a header, which will not be relocated as long as the + * list remains non-empty, and an expansible data array. * * We support three types of lists: * @@ -40,51 +40,131 @@ #include "nodes/nodes.h" -typedef struct ListCell ListCell; +typedef union ListCell +{ + void *ptr_value; + int int_value; + Oid oid_value; +} ListCell; typedef struct List { NodeTag type; /* T_List, T_IntList, or T_OidList */ - int length; - ListCell *head; - ListCell *tail; + int length; /* number of elements currently present */ + int max_length; /* allocated length of elements[] */ + ListCell *elements; /* re-allocatable array of cells */ + /* We may allocate some cells along with the List header: */ + ListCell initial_elements[FLEXIBLE_ARRAY_MEMBER]; + /* If elements == initial_elements, it's not a separate allocation */ } List; -struct ListCell -{ - union - { - void *ptr_value; - int int_value; - Oid oid_value; - } data; - ListCell *next; -}; - /* * The *only* valid representation of an empty list is NIL; in other - * words, a non-NIL list is guaranteed to have length >= 1 and - * head/tail != NULL + * words, a non-NIL list is guaranteed to have length >= 1. */ #define NIL ((List *) NULL) /* - * These routines are used frequently. However, we can't implement - * them as macros, since we want to avoid double-evaluation of macro - * arguments. + * State structs for various looping macros below. + */ +typedef struct ForEachState +{ + const List *l; /* list we're looping through */ + int i; /* current element index */ +} ForEachState; + +typedef struct ForBothState +{ + const List *l1; /* lists we're looping through */ + const List *l2; + int i; /* common element index */ +} ForBothState; + +typedef struct ForBothCellState +{ + const List *l1; /* lists we're looping through */ + const List *l2; + int i1; /* current element indexes */ + int i2; +} ForBothCellState; + +typedef struct ForThreeState +{ + const List *l1; /* lists we're looping through */ + const List *l2; + const List *l3; + int i; /* common element index */ +} ForThreeState; + +typedef struct ForFourState +{ + const List *l1; /* lists we're looping through */ + const List *l2; + const List *l3; + const List *l4; + int i; /* common element index */ +} ForFourState; + +typedef struct ForFiveState +{ + const List *l1; /* lists we're looping through */ + const List *l2; + const List *l3; + const List *l4; + const List *l5; + int i; /* common element index */ +} ForFiveState; + +/* + * These routines are small enough, and used often enough, to justify being + * inline. */ + +/* Fetch address of list's first cell; NULL if empty list */ static inline ListCell * list_head(const List *l) { - return l ? l->head : NULL; + return l ? &l->elements[0] : NULL; +} + +/* Fetch address of list's last cell; NULL if empty list */ +static inline ListCell * +list_tail(const List *l) +{ + return l ? &l->elements[l->length - 1] : NULL; +} + +/* Fetch address of list's second cell, if it has one, else NULL */ +static inline ListCell * +list_second_cell(const List *l) +{ + if (l && l->length >= 2) + return &l->elements[1]; + else + return NULL; +} + +/* Fetch address of list's third cell, if it has one, else NULL */ +static inline ListCell * +list_third_cell(const List *l) +{ + if (l && l->length >= 3) + return &l->elements[2]; + else + return NULL; } +/* Fetch address of list's fourth cell, if it has one, else NULL */ static inline ListCell * -list_tail(List *l) +list_fourth_cell(const List *l) { - return l ? l->tail : NULL; + if (l && l->length >= 4) + return &l->elements[3]; + else + return NULL; } +/* Fetch list's length */ static inline int list_length(const List *l) { @@ -92,6 +172,11 @@ list_length(const List *l) } /* + * Macros to access the data values within List cells. + * + * Note that with the exception of the "xxx_node" macros, these are + * lvalues and can be assigned to. + * * NB: There is an unfortunate legacy from a previous incarnation of * the List API: the macro lfirst() was used to mean "the data in this * cons cell". To avoid changing every usage of lfirst(), that meaning @@ -99,13 +184,12 @@ list_length(const List *l) * the data it contains; to get the data in the first cell of a * List, use linitial(). Worse, lsecond() is more closely related to * linitial() than lfirst(): given a List, lsecond() returns the data - * in the second cons cell. + * in the second list cell. */ -#define lnext(lc) ((lc)->next) -#define lfirst(lc) ((lc)->data.ptr_value) -#define lfirst_int(lc) ((lc)->data.int_value) -#define lfirst_oid(lc) ((lc)->data.oid_value) +#define lfirst(lc) ((lc)->ptr_value) +#define lfirst_int(lc) ((lc)->int_value) +#define lfirst_oid(lc) ((lc)->oid_value) #define lfirst_node(type,lc) castNode(type, lfirst(lc)) #define linitial(l) lfirst(list_head(l)) @@ -113,19 +197,19 @@ list_length(const List *l) #define linitial_oid(l) lfirst_oid(list_head(l)) #define linitial_node(type,l) castNode(type, linitial(l)) -#define lsecond(l) lfirst(lnext(list_head(l))) -#define lsecond_int(l) lfirst_int(lnext(list_head(l))) -#define lsecond_oid(l) lfirst_oid(lnext(list_head(l))) +#define lsecond(l) lfirst(list_second_cell(l)) +#define lsecond_int(l) lfirst_int(list_second_cell(l)) +#define lsecond_oid(l) lfirst_oid(list_second_cell(l)) #define lsecond_node(type,l) castNode(type, lsecond(l)) -#define lthird(l) lfirst(lnext(lnext(list_head(l)))) -#define lthird_int(l) lfirst_int(lnext(lnext(list_head(l)))) -#define lthird_oid(l) lfirst_oid(lnext(lnext(list_head(l)))) +#define lthird(l) lfirst(list_third_cell(l)) +#define lthird_int(l) lfirst_int(list_third_cell(l)) +#define lthird_oid(l) lfirst_oid(list_third_cell(l)) #define lthird_node(type,l) castNode(type, lthird(l)) -#define lfourth(l) lfirst(lnext(lnext(lnext(list_head(l))))) -#define lfourth_int(l) lfirst_int(lnext(lnext(lnext(list_head(l))))) -#define lfourth_oid(l) lfirst_oid(lnext(lnext(lnext(list_head(l))))) +#define lfourth(l) lfirst(list_fourth_cell(l)) +#define lfourth_int(l) lfirst_int(list_fourth_cell(l)) +#define lfourth_oid(l) lfirst_oid(list_fourth_cell(l)) #define lfourth_node(type,l) castNode(type, lfourth(l)) #define llast(l) lfirst(list_tail(l)) @@ -136,43 +220,194 @@ list_length(const List *l) /* * Convenience macros for building fixed-length lists */ -#define list_make1(x1) lcons(x1, NIL) -#define list_make2(x1,x2) lcons(x1, list_make1(x2)) -#define list_make3(x1,x2,x3) lcons(x1, list_make2(x2, x3)) -#define list_make4(x1,x2,x3,x4) lcons(x1, list_make3(x2, x3, x4)) -#define list_make5(x1,x2,x3,x4,x5) lcons(x1, list_make4(x2, x3, x4, x5)) - -#define list_make1_int(x1) lcons_int(x1, NIL) -#define list_make2_int(x1,x2) lcons_int(x1, list_make1_int(x2)) -#define list_make3_int(x1,x2,x3) lcons_int(x1, list_make2_int(x2, x3)) -#define list_make4_int(x1,x2,x3,x4) lcons_int(x1, list_make3_int(x2, x3, x4)) -#define list_make5_int(x1,x2,x3,x4,x5) lcons_int(x1, list_make4_int(x2, x3, x4, x5)) - -#define list_make1_oid(x1) lcons_oid(x1, NIL) -#define list_make2_oid(x1,x2) lcons_oid(x1, list_make1_oid(x2)) -#define list_make3_oid(x1,x2,x3) lcons_oid(x1, list_make2_oid(x2, x3)) -#define list_make4_oid(x1,x2,x3,x4) lcons_oid(x1, list_make3_oid(x2, x3, x4)) -#define list_make5_oid(x1,x2,x3,x4,x5) lcons_oid(x1, list_make4_oid(x2, x3, x4, x5)) +#define list_make_ptr_cell(v) ((ListCell) {.ptr_value = (v)}) +#define list_make_int_cell(v) ((ListCell) {.int_value = (v)}) +#define list_make_oid_cell(v) ((ListCell) {.oid_value = (v)}) + +#define list_make1(x1) \ + list_make1_impl(T_List, list_make_ptr_cell(x1)) +#define list_make2(x1,x2) \ + list_make2_impl(T_List, list_make_ptr_cell(x1), list_make_ptr_cell(x2)) +#define list_make3(x1,x2,x3) \ + list_make3_impl(T_List, list_make_ptr_cell(x1), list_make_ptr_cell(x2), \ + list_make_ptr_cell(x3)) +#define list_make4(x1,x2,x3,x4) \ + list_make4_impl(T_List, list_make_ptr_cell(x1), list_make_ptr_cell(x2), \ + list_make_ptr_cell(x3), list_make_ptr_cell(x4)) + +#define list_make1_int(x1) \ + list_make1_impl(T_IntList, list_make_int_cell(x1)) +#define list_make2_int(x1,x2) \ + list_make2_impl(T_IntList, list_make_int_cell(x1), list_make_int_cell(x2)) +#define list_make3_int(x1,x2,x3) \ + list_make3_impl(T_IntList, list_make_int_cell(x1), list_make_int_cell(x2), \ + list_make_int_cell(x3)) +#define list_make4_int(x1,x2,x3,x4) \ + list_make4_impl(T_IntList, list_make_int_cell(x1), list_make_int_cell(x2), \ + list_make_int_cell(x3), list_make_int_cell(x4)) + +#define list_make1_oid(x1) \ + list_make1_impl(T_OidList, list_make_oid_cell(x1)) +#define list_make2_oid(x1,x2) \ + list_make2_impl(T_OidList, list_make_oid_cell(x1), list_make_oid_cell(x2)) +#define list_make3_oid(x1,x2,x3) \ + list_make3_impl(T_OidList, list_make_oid_cell(x1), list_make_oid_cell(x2), \ + list_make_oid_cell(x3)) +#define list_make4_oid(x1,x2,x3,x4) \ + list_make4_impl(T_OidList, list_make_oid_cell(x1), list_make_oid_cell(x2), \ + list_make_oid_cell(x3), list_make_oid_cell(x4)) + +/* + * Locate the n'th cell (counting from 0) of the list. + * It is an assertion failure if there is no such cell. + */ +static inline ListCell * +list_nth_cell(const List *list, int n) +{ + Assert(list != NIL); + Assert(n >= 0 && n < list->length); + return &list->elements[n]; +} + +/* + * Return the pointer value contained in the n'th element of the + * specified list. (List elements begin at 0.) + */ +static inline void * +list_nth(const List *list, int n) +{ + Assert(IsA(list, List)); + return lfirst(list_nth_cell(list, n)); +} + +/* + * Return the integer value contained in the n'th element of the + * specified list. + */ +static inline int +list_nth_int(const List *list, int n) +{ + Assert(IsA(list, IntList)); + return lfirst_int(list_nth_cell(list, n)); +} + +/* + * Return the OID value contained in the n'th element of the specified + * list. + */ +static inline Oid +list_nth_oid(const List *list, int n) +{ + Assert(IsA(list, OidList)); + return lfirst_oid(list_nth_cell(list, n)); +} + +#define list_nth_node(type,list,n) castNode(type, list_nth(list, n)) + +/* + * Get the given ListCell's index (from 0) in the given List. + */ +static inline int +list_cell_number(const List *l, const ListCell *c) +{ + Assert(c >= &l->elements[0] && c < &l->elements[l->length]); + return c - l->elements; +} + +/* + * Get the address of the next cell after "c" within list "l", or NULL if none. + */ +static inline ListCell * +lnext(const List *l, const ListCell *c) +{ + Assert(c >= &l->elements[0] && c < &l->elements[l->length]); + c++; + if (c < &l->elements[l->length]) + return (ListCell *) c; + else + return NULL; +} /* * foreach - - * a convenience macro which loops through the list + * a convenience macro for looping through a list + * + * "cell" must be the name of a "ListCell *" variable; it's made to point + * to each List element in turn. "cell" will be NULL after normal exit from + * the loop, but an early "break" will leave it pointing at the current + * List element. + * + * Beware of changing the List object while the loop is iterating. + * The current semantics are that we examine successive list indices in + * each iteration, so that insertion or deletion of list elements could + * cause elements to be re-visited or skipped unexpectedly. Previous + * implementations of foreach() behaved differently. However, it's safe + * to append elements to the List (or in general, insert them after the + * current element); such new elements are guaranteed to be visited. + * Also, the current element of the List can be deleted, if you use + * foreach_delete_current() to do so. BUT: either of these actions will + * invalidate the "cell" pointer for the remainder of the current iteration. */ -#define foreach(cell, l) \ - for ((cell) = list_head(l); (cell) != NULL; (cell) = lnext(cell)) +#define foreach(cell, lst) \ + for (ForEachState cell##__state = {(lst), 0}; \ + (cell##__state.l != NIL && \ + cell##__state.i < cell##__state.l->length) ? \ + (cell = &cell##__state.l->elements[cell##__state.i], true) : \ + (cell = NULL, false); \ + cell##__state.i++) + +/* + * foreach_delete_current - + * delete the current list element from the List associated with a + * surrounding foreach() loop, returning the new List pointer. + * + * This is equivalent to list_delete_cell(), but it also adjusts the foreach + * loop's state so that no list elements will be missed. Do not delete + * elements from an active foreach loop's list in any other way! + */ +#define foreach_delete_current(lst, cell) \ + (cell##__state.i--, \ + (List *) (cell##__state.l = list_delete_cell(lst, cell))) + +/* + * foreach_current_index - + * get the zero-based list index of a surrounding foreach() loop's + * current element; pass the name of the "ListCell *" iterator variable. + * + * Beware of using this after foreach_delete_current(); the value will be + * out of sync for the rest of the current loop iteration. Anyway, since + * you just deleted the current element, the value is pretty meaningless. + */ +#define foreach_current_index(cell) (cell##__state.i) /* * for_each_cell - * a convenience macro which loops through a list starting from a * specified cell + * + * The caveats for foreach() apply equally here. */ -#define for_each_cell(cell, initcell) \ - for ((cell) = (initcell); (cell) != NULL; (cell) = lnext(cell)) +#define for_each_cell(cell, lst, initcell) \ + for (ForEachState cell##__state = for_each_cell_setup(lst, initcell); \ + (cell##__state.l != NIL && \ + cell##__state.i < cell##__state.l->length) ? \ + (cell = &cell##__state.l->elements[cell##__state.i], true) : \ + (cell = NULL, false); \ + cell##__state.i++) + +static inline ForEachState +for_each_cell_setup(List *lst, ListCell *initcell) +{ + ForEachState r = {lst, + initcell ? list_cell_number(lst, initcell) : list_length(lst)}; + + return r; +} #define foreach_with_count(cell, list, counter) \ for ((cell) = list_head(list), (counter)=0; \ (cell) != NULL; \ - (cell) = lnext(cell), ++(counter)) + (cell) = lnext(list, cell), ++(counter)) /* @@ -181,12 +416,22 @@ list_length(const List *l) * simultaneously. This macro loops through both lists at the same * time, stopping when either list runs out of elements. Depending * on the requirements of the call site, it may also be wise to - * assert that the lengths of the two lists are equal. + * assert that the lengths of the two lists are equal. (But, if they + * are not, some callers rely on the ending cell values being separately + * NULL or non-NULL as defined here; don't try to optimize that.) + * + * The caveats for foreach() apply equally here. */ #define forboth(cell1, list1, cell2, list2) \ - for ((cell1) = list_head(list1), (cell2) = list_head(list2); \ - (cell1) != NULL && (cell2) != NULL; \ - (cell1) = lnext(cell1), (cell2) = lnext(cell2)) + for (ForBothState cell1##__state = {(list1), (list2), 0}; \ + multi_for_advance_cell(cell1, cell1##__state, l1, i), \ + multi_for_advance_cell(cell2, cell1##__state, l2, i), \ + (cell1 != NULL && cell2 != NULL); \ + cell1##__state.i++) + +#define multi_for_advance_cell(cell, state, l, i) \ + (cell = (state.l != NIL && state.i < state.l->length) ? \ + &state.l->elements[state.i] : NULL) /* * for_both_cell - @@ -196,68 +441,92 @@ list_length(const List *l) * requirements of the call site, it may also be wise to assert that the * lengths of the two lists are equal, and initcell1 and initcell2 are at * the same position in the respective lists. + * + * The caveats for foreach() apply equally here. */ -#define for_both_cell(cell1, initcell1, cell2, initcell2) \ - for ((cell1) = (initcell1), (cell2) = (initcell2); \ - (cell1) != NULL && (cell2) != NULL; \ - (cell1) = lnext(cell1), (cell2) = lnext(cell2)) +#define for_both_cell(cell1, list1, initcell1, cell2, list2, initcell2) \ + for (ForBothCellState cell1##__state = \ + for_both_cell_setup(list1, initcell1, list2, initcell2); \ + multi_for_advance_cell(cell1, cell1##__state, l1, i1), \ + multi_for_advance_cell(cell2, cell1##__state, l2, i2), \ + (cell1 != NULL && cell2 != NULL); \ + cell1##__state.i1++, cell1##__state.i2++) + +static inline ForBothCellState +for_both_cell_setup(List *list1, ListCell *initcell1, + List *list2, ListCell *initcell2) +{ + ForBothCellState r = {list1, list2, + initcell1 ? list_cell_number(list1, initcell1) : list_length(list1), + initcell2 ? list_cell_number(list2, initcell2) : list_length(list2)}; + + return r; +} /* * forthree - * the same for three lists */ -#define forthree(cell1, list1, cell2, list2, cell3, list3) \ - for ((cell1) = list_head(list1), (cell2) = list_head(list2), (cell3) = list_head(list3); \ - (cell1) != NULL && (cell2) != NULL && (cell3) != NULL; \ - (cell1) = lnext(cell1), (cell2) = lnext(cell2), (cell3) = lnext(cell3)) +#define forthree(cell1, list1, cell2, list2, cell3, list3) \ + for (ForThreeState cell1##__state = {(list1), (list2), (list3), 0}; \ + multi_for_advance_cell(cell1, cell1##__state, l1, i), \ + multi_for_advance_cell(cell2, cell1##__state, l2, i), \ + multi_for_advance_cell(cell3, cell1##__state, l3, i), \ + (cell1 != NULL && cell2 != NULL && cell3 != NULL); \ + cell1##__state.i++) /* * forfour - * the same for four lists */ #define forfour(cell1, list1, cell2, list2, cell3, list3, cell4, list4) \ - for ((cell1) = list_head(list1), (cell2) = list_head(list2), \ - (cell3) = list_head(list3), (cell4) = list_head(list4); \ - (cell1) != NULL && (cell2) != NULL && \ - (cell3) != NULL && (cell4) != NULL; \ - (cell1) = lnext(cell1), (cell2) = lnext(cell2), \ - (cell3) = lnext(cell3), (cell4) = lnext(cell4)) + for (ForFourState cell1##__state = {(list1), (list2), (list3), (list4), 0}; \ + multi_for_advance_cell(cell1, cell1##__state, l1, i), \ + multi_for_advance_cell(cell2, cell1##__state, l2, i), \ + multi_for_advance_cell(cell3, cell1##__state, l3, i), \ + multi_for_advance_cell(cell4, cell1##__state, l4, i), \ + (cell1 != NULL && cell2 != NULL && cell3 != NULL && cell4 != NULL); \ + cell1##__state.i++) /* * forfive - * the same for five lists */ #define forfive(cell1, list1, cell2, list2, cell3, list3, cell4, list4, cell5, list5) \ - for ((cell1) = list_head(list1), (cell2) = list_head(list2), \ - (cell3) = list_head(list3), (cell4) = list_head(list4), \ - (cell5) = list_head(list5); \ - (cell1) != NULL && (cell2) != NULL && (cell3) != NULL && \ - (cell4) != NULL && (cell5) != NULL; \ - (cell1) = lnext(cell1), (cell2) = lnext(cell2), \ - (cell3) = lnext(cell3), (cell4) = lnext(cell4), \ - (cell5) = lnext(cell5)) + for (ForFiveState cell1##__state = {(list1), (list2), (list3), (list4), (list5), 0}; \ + multi_for_advance_cell(cell1, cell1##__state, l1, i), \ + multi_for_advance_cell(cell2, cell1##__state, l2, i), \ + multi_for_advance_cell(cell3, cell1##__state, l3, i), \ + multi_for_advance_cell(cell4, cell1##__state, l4, i), \ + multi_for_advance_cell(cell5, cell1##__state, l5, i), \ + (cell1 != NULL && cell2 != NULL && cell3 != NULL && \ + cell4 != NULL && cell5 != NULL); \ + cell1##__state.i++) + +/* Functions in src/backend/nodes/list.c */ + +extern List *list_make1_impl(NodeTag t, ListCell datum1); +extern List *list_make2_impl(NodeTag t, ListCell datum1, ListCell datum2); +extern List *list_make3_impl(NodeTag t, ListCell datum1, ListCell datum2, + ListCell datum3); +extern List *list_make4_impl(NodeTag t, ListCell datum1, ListCell datum2, + ListCell datum3, ListCell datum4); extern List *lappend(List *list, void *datum); extern List *lappend_int(List *list, int datum); extern List *lappend_oid(List *list, Oid datum); -extern ListCell *lappend_cell(List *list, ListCell *prev, void *datum); -extern ListCell *lappend_cell_int(List *list, ListCell *prev, int datum); -extern ListCell *lappend_cell_oid(List *list, ListCell *prev, Oid datum); +extern List *list_insert_nth(List *list, int pos, void *datum); +extern List *list_insert_nth_int(List *list, int pos, int datum); +extern List *list_insert_nth_oid(List *list, int pos, Oid datum); extern List *lcons(void *datum, List *list); extern List *lcons_int(int datum, List *list); extern List *lcons_oid(Oid datum, List *list); -extern List *list_concat(List *list1, List *list2); +extern List *list_concat(List *list1, const List *list2); extern List *list_truncate(List *list, int new_size); -extern ListCell *list_nth_cell(const List *list, int n); -extern void *list_nth(const List *list, int n); -extern int list_nth_int(const List *list, int n); -extern Oid list_nth_oid(const List *list, int n); -#define list_nth_node(type,list,n) castNode(type, list_nth(list, n)) - extern bool list_member(const List *list, const void *datum); extern bool list_member_ptr(const List *list, const void *datum); extern bool list_member_int(const List *list, int datum); @@ -268,7 +537,9 @@ extern List *list_delete_ptr(List *list, void *datum); extern List *list_delete_int(List *list, int datum); extern List *list_delete_oid(List *list, Oid datum); extern List *list_delete_first(List *list); -extern List *list_delete_cell(List *list, ListCell *cell, ListCell *prev); +extern List *list_delete_last(List *list); +extern List *list_delete_nth_cell(List *list, int n); +extern List *list_delete_cell(List *list, ListCell *cell); extern List *list_union(const List *list1, const List *list2); extern List *list_union_ptr(const List *list1, const List *list2); @@ -290,89 +561,25 @@ extern List *list_append_unique_ptr(List *list, void *datum); extern List *list_append_unique_int(List *list, int datum); extern List *list_append_unique_oid(List *list, Oid datum); -extern List *list_concat_unique(List *list1, List *list2); -extern List *list_concat_unique_ptr(List *list1, List *list2); -extern List *list_concat_unique_int(List *list1, List *list2); -extern List *list_concat_unique_oid(List *list1, List *list2); +extern List *list_concat_unique(List *list1, const List *list2); +extern List *list_concat_unique_ptr(List *list1, const List *list2); +extern List *list_concat_unique_int(List *list1, const List *list2); +extern List *list_concat_unique_oid(List *list1, const List *list2); + +extern void list_deduplicate_oid(List *list); extern void list_free(List *list); extern void list_free_deep(List *list); extern List *list_copy(const List *list); extern List *list_copy_tail(const List *list, int nskip); +extern List *list_copy_deep(const List *oldlist); extern void *list_nth_replace(List *list, int n, void *new_data); -typedef int (*list_qsort_comparator) (const void *a, const void *b); -extern List *list_qsort(const List *list, list_qsort_comparator cmp); - -/* - * To ease migration to the new list API, a set of compatibility - * macros are provided that reduce the impact of the list API changes - * as far as possible. Until client code has been rewritten to use the - * new list API, the ENABLE_LIST_COMPAT symbol can be defined before - * including pg_list.h - */ -#ifdef ENABLE_LIST_COMPAT - -#define lfirsti(lc) lfirst_int(lc) -#define lfirsto(lc) lfirst_oid(lc) - -#define makeList1(x1) list_make1(x1) -#define makeList2(x1, x2) list_make2(x1, x2) -#define makeList3(x1, x2, x3) list_make3(x1, x2, x3) -#define makeList4(x1, x2, x3, x4) list_make4(x1, x2, x3, x4) - -#define makeListi1(x1) list_make1_int(x1) -#define makeListi2(x1, x2) list_make2_int(x1, x2) - -#define makeListo1(x1) list_make1_oid(x1) -#define makeListo2(x1, x2) list_make2_oid(x1, x2) - -#define lconsi(datum, list) lcons_int(datum, list) -#define lconso(datum, list) lcons_oid(datum, list) - -#define lappendi(list, datum) lappend_int(list, datum) -#define lappendo(list, datum) lappend_oid(list, datum) - -#define nconc(l1, l2) list_concat(l1, l2) - -#define nth(n, list) list_nth(list, n) - -#define member(datum, list) list_member(list, datum) -#define ptrMember(datum, list) list_member_ptr(list, datum) -#define intMember(datum, list) list_member_int(list, datum) -#define oidMember(datum, list) list_member_oid(list, datum) - -/* - * Note that the old lremove() determined equality via pointer - * comparison, whereas the new list_delete() uses equal(); in order to - * keep the same behavior, we therefore need to map lremove() calls to - * list_delete_ptr() rather than list_delete() - */ -#define lremove(elem, list) list_delete_ptr(list, elem) -#define LispRemove(elem, list) list_delete(list, elem) -#define lremovei(elem, list) list_delete_int(list, elem) -#define lremoveo(elem, list) list_delete_oid(list, elem) - -#define ltruncate(n, list) list_truncate(list, n) - -#define set_union(l1, l2) list_union(l1, l2) -#define set_uniono(l1, l2) list_union_oid(l1, l2) -#define set_ptrUnion(l1, l2) list_union_ptr(l1, l2) - -#define set_difference(l1, l2) list_difference(l1, l2) -#define set_differenceo(l1, l2) list_difference_oid(l1, l2) -#define set_ptrDifference(l1, l2) list_difference_ptr(l1, l2) - -#define equali(l1, l2) equal(l1, l2) -#define equalo(l1, l2) equal(l1, l2) - -#define freeList(list) list_free(list) - -#define listCopy(list) list_copy(list) +typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b); +extern void list_sort(List *list, list_sort_comparator cmp); -extern int length(List *list); -#endif /* ENABLE_LIST_COMPAT */ +extern int list_oid_cmp(const ListCell *p1, const ListCell *p2); #endif /* PG_LIST_H */ diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.h index 8723bcef95ab..d1683084f8bb 100644 --- a/src/include/partitioning/partprune.h +++ b/src/include/partitioning/partprune.h @@ -60,10 +60,10 @@ typedef struct PartitionPruneContext } PartitionPruneContext; /* - * PruneCxtStateIdx() computes the correct index into the stepcmpfuncs[], - * exprstates[] and exprhasexecparam[] arrays for step step_id and - * partition key column keyno. (Note: there is code that assumes the - * entries for a given step are sequential, so this is not chosen freely.) + * PruneCxtStateIdx() computes the correct index into the stepcmpfuncs[] + * and exprstates[] arrays for step step_id and partition key column keyno. + * (Note: there is code that assumes the entries for a given step are + * sequential, so this is not chosen freely.) */ #define PruneCxtStateIdx(partnatts, step_id, keyno) \ ((partnatts) * (step_id) + (keyno)) diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 2c06f35a44f5..d3c0a9fb2cd7 100644 --- a/src/include/pg_config.h.win32 +++ b/src/include/pg_config.h.win32 @@ -195,15 +195,9 @@ /* Define to 1 if you have the header file. */ /* #undef HAVE_EDITLINE_READLINE_H */ -/* Define to 1 if you have the `fcvt' function. */ -#define HAVE_FCVT 1 - /* Define to 1 if you have the `fdatasync' function. */ /* #undef HAVE_FDATASYNC */ -/* Define to 1 if you have finite(). */ -#define HAVE_FINITE 1 - /* Define to 1 if you have the `fpclass' function. */ /* #undef HAVE_FPCLASS */ @@ -511,9 +505,6 @@ /* Define to 1 if `__ss_len' is member of `struct sockaddr_storage'. */ /* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN */ -/* Define to 1 if the system has the type `struct sockaddr_un'. */ -/* #undef HAVE_STRUCT_SOCKADDR_UN */ - /* Define to 1 if `tm_zone' is member of `struct tm'. */ /* #undef HAVE_STRUCT_TM_TM_ZONE */ @@ -523,9 +514,6 @@ /* Define to 1 if you have the `sync_file_range' function. */ /* #undef HAVE_SYNC_FILE_RANGE */ -/* Define to 1 if you have the `sysconf' function. */ -/* #undef HAVE_SYSCONF */ - /* Define to 1 if you have the syslog interface. */ /* #undef HAVE_SYSLOG */ @@ -687,10 +675,10 @@ #define PACKAGE_NAME "Greenplum Database" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "Greenplum Database 7.0.0-alpha" +#define PACKAGE_STRING "Greenplum Database 8.0.0-alpha" /* Define to the version of this package. */ -#define PACKAGE_VERSION "12beta2" +#define PACKAGE_VERSION "13devel" /* Define to the name of a signed 128-bit integer type. */ #undef PG_INT128_TYPE @@ -699,10 +687,10 @@ #define PG_INT64_TYPE long long int /* PostgreSQL version as a string */ -#define PG_VERSION "12beta2" +#define PG_VERSION "13devel" /* PostgreSQL version as a number */ -#define PG_VERSION_NUM 120000 +#define PG_VERSION_NUM 130000 /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "greenplum" diff --git a/src/include/port/atomics/generic-acc.h b/src/include/port/atomics/generic-acc.h index eec5063cbc4e..a64c841f4f17 100644 --- a/src/include/port/atomics/generic-acc.h +++ b/src/include/port/atomics/generic-acc.h @@ -68,7 +68,7 @@ pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, _Asm_mf(); /* * Notes: - * DOWN_MEM_FENCE | _UP_MEM_FENCE prevents reordering by the compiler + * _DOWN_MEM_FENCE | _UP_MEM_FENCE prevents reordering by the compiler */ current = _Asm_cmpxchg(_SZ_W, /* word */ _SEM_REL, diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index 8421f6be7be1..a9a8146b7020 100644 --- a/src/include/postmaster/postmaster.h +++ b/src/include/postmaster/postmaster.h @@ -60,7 +60,6 @@ extern void InitProcessGlobals(void); extern int MaxLivePostmasterChildren(void); -extern int GetNumShmemAttachedBgworkers(void); extern bool PostmasterMarkPIDForWorkerNotify(int); extern void ResetMirrorReadyFlag(void); diff --git a/src/include/replication/logical.h b/src/include/replication/logical.h index f76b9eb4df4c..31c796b76516 100644 --- a/src/include/replication/logical.h +++ b/src/include/replication/logical.h @@ -17,8 +17,7 @@ struct LogicalDecodingContext; -typedef void (*LogicalOutputPluginWriterWrite) ( - struct LogicalDecodingContext *lr, +typedef void (*LogicalOutputPluginWriterWrite) (struct LogicalDecodingContext *lr, XLogRecPtr Ptr, TransactionId xid, bool last_write @@ -26,8 +25,7 @@ typedef void (*LogicalOutputPluginWriterWrite) ( typedef LogicalOutputPluginWriterWrite LogicalOutputPluginWriterPrepareWrite; -typedef void (*LogicalOutputPluginWriterUpdateProgress) ( - struct LogicalDecodingContext *lr, +typedef void (*LogicalOutputPluginWriterUpdateProgress) (struct LogicalDecodingContext *lr, XLogRecPtr Ptr, TransactionId xid ); @@ -102,8 +100,7 @@ extern LogicalDecodingContext *CreateInitDecodingContext(char *plugin, LogicalOutputPluginWriterPrepareWrite prepare_write, LogicalOutputPluginWriterWrite do_write, LogicalOutputPluginWriterUpdateProgress update_progress); -extern LogicalDecodingContext *CreateDecodingContext( - XLogRecPtr start_lsn, +extern LogicalDecodingContext *CreateDecodingContext(XLogRecPtr start_lsn, List *output_plugin_options, bool fast_forward, XLogPageReadCB read_page, diff --git a/src/include/replication/logicalproto.h b/src/include/replication/logicalproto.h index 9c0000cc5935..3fc430af013d 100644 --- a/src/include/replication/logicalproto.h +++ b/src/include/replication/logicalproto.h @@ -19,8 +19,8 @@ /* * Protocol capabilities * - * LOGICAL_PROTO_VERSION_NUM is our native protocol and the greatest version - * we can support. PGLOGICAL_PROTO_MIN_VERSION_NUM is the oldest version we + * LOGICALREP_PROTO_VERSION_NUM is our native protocol and the greatest version + * we can support. LOGICALREP_PROTO_MIN_VERSION_NUM is the oldest version we * have backwards compatibility for. The client requests protocol version at * connect time. */ @@ -106,4 +106,4 @@ extern LogicalRepRelation *logicalrep_read_rel(StringInfo in); extern void logicalrep_write_typ(StringInfo out, Oid typoid); extern void logicalrep_read_typ(StringInfo out, LogicalRepTyp *ltyp); -#endif /* LOGICALREP_PROTO_H */ +#endif /* LOGICAL_PROTO_H */ diff --git a/src/include/replication/reorderbuffer.h b/src/include/replication/reorderbuffer.h index 735e6d32b86c..4c06a78c11f0 100644 --- a/src/include/replication/reorderbuffer.h +++ b/src/include/replication/reorderbuffer.h @@ -292,34 +292,29 @@ typedef struct ReorderBufferTXN typedef struct ReorderBuffer ReorderBuffer; /* change callback signature */ -typedef void (*ReorderBufferApplyChangeCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferApplyChangeCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change); /* truncate callback signature */ -typedef void (*ReorderBufferApplyTruncateCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferApplyTruncateCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, int nrelations, Relation relations[], ReorderBufferChange *change); /* begin callback signature */ -typedef void (*ReorderBufferBeginCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferBeginCB) (ReorderBuffer *rb, ReorderBufferTXN *txn); /* commit callback signature */ -typedef void (*ReorderBufferCommitCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferCommitCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr commit_lsn); /* message callback signature */ -typedef void (*ReorderBufferMessageCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferMessageCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr message_lsn, bool transactional, diff --git a/src/include/snowball/libstemmer/api.h b/src/include/snowball/libstemmer/api.h index 8b997f0c2986..7ed7995f9e1f 100644 --- a/src/include/snowball/libstemmer/api.h +++ b/src/include/snowball/libstemmer/api.h @@ -19,8 +19,15 @@ struct SN_env { unsigned char * B; }; +#ifdef __cplusplus +extern "C" { +#endif + extern struct SN_env * SN_create_env(int S_size, int I_size, int B_size); extern void SN_close_env(struct SN_env * z, int S_size); extern int SN_set_current(struct SN_env * z, int size, const symbol * s); +#ifdef __cplusplus +} +#endif diff --git a/src/include/snowball/libstemmer/stem_UTF_8_greek.h b/src/include/snowball/libstemmer/stem_UTF_8_greek.h new file mode 100644 index 000000000000..bf1cc6ca01ff --- /dev/null +++ b/src/include/snowball/libstemmer/stem_UTF_8_greek.h @@ -0,0 +1,16 @@ +/* This file was generated automatically by the Snowball to ISO C compiler */ +/* http://snowballstem.org/ */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct SN_env * greek_UTF_8_create_env(void); +extern void greek_UTF_8_close_env(struct SN_env * z); + +extern int greek_UTF_8_stem(struct SN_env * z); + +#ifdef __cplusplus +} +#endif + diff --git a/src/include/statistics/extended_stats_internal.h b/src/include/statistics/extended_stats_internal.h index fb03c52f5049..8433c34f6d7d 100644 --- a/src/include/statistics/extended_stats_internal.h +++ b/src/include/statistics/extended_stats_internal.h @@ -36,6 +36,7 @@ typedef struct DimensionInfo { int nvalues; /* number of deduplicated values */ int nbytes; /* number of bytes (serialized) */ + int nbytes_aligned; /* size of deserialized data with alignment */ int typlen; /* pg_type.typlen */ bool typbyval; /* pg_type.typbyval */ } DimensionInfo; @@ -96,6 +97,8 @@ extern SortItem *build_sorted_items(int numrows, int *nitems, HeapTuple *rows, TupleDesc tdesc, MultiSortSupport mss, int numattrs, AttrNumber *attnums); +extern bool examine_opclause_expression(OpExpr *expr, Var **varp, + Const **cstp, bool *varonleftp); extern Selectivity mcv_clauselist_selectivity(PlannerInfo *root, StatisticExtInfo *stat, diff --git a/src/include/statistics/statistics.h b/src/include/statistics/statistics.h index cb7bc630e98b..3b7da3be6094 100644 --- a/src/include/statistics/statistics.h +++ b/src/include/statistics/statistics.h @@ -22,7 +22,7 @@ #define STATS_NDISTINCT_MAGIC 0xA352BFA4 /* struct identifier */ #define STATS_NDISTINCT_TYPE_BASIC 1 /* struct version */ -/* MVDistinctItem represents a single combination of columns */ +/* MVNDistinctItem represents a single combination of columns */ typedef struct MVNDistinctItem { double ndistinct; /* ndistinct value for this combination */ diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h index 95f4096958f8..82f740859e1c 100644 --- a/src/include/storage/bufpage.h +++ b/src/include/storage/bufpage.h @@ -320,7 +320,7 @@ typedef PageHeaderData *PageHeader; * This is intended to catch use of the pointer before page initialization. * It is implemented as a function due to the limitations of the MSVC * compiler, which choked on doing all these tests within another macro. We - * return true so that MacroAssert() can be used while still getting the + * return true so that AssertMacro() can be used while still getting the * specifics from the macro failure within this function. */ static inline bool diff --git a/src/include/storage/condition_variable.h b/src/include/storage/condition_variable.h index 2a0249392ccc..ee06e051ce12 100644 --- a/src/include/storage/condition_variable.h +++ b/src/include/storage/condition_variable.h @@ -43,6 +43,8 @@ extern void ConditionVariableInit(ConditionVariable *cv); * the condition variable. */ extern void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info); +extern bool ConditionVariableTimedSleep(ConditionVariable *cv, long timeout, + uint32 wait_event_info); extern void ConditionVariableCancelSleep(void); /* diff --git a/src/include/storage/fsm_internals.h b/src/include/storage/fsm_internals.h index 0e27db07f2f3..0ec5a7966f67 100644 --- a/src/include/storage/fsm_internals.h +++ b/src/include/storage/fsm_internals.h @@ -1,6 +1,6 @@ /*------------------------------------------------------------------------- * - * fsm_internal.h + * fsm_internals.h * internal functions for free space map * * diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h index 4dbe4d396767..f3acbda68892 100644 --- a/src/include/storage/lock.h +++ b/src/include/storage/lock.h @@ -669,4 +669,4 @@ extern bool VirtualXactLock(VirtualTransactionId vxid, bool wait); /* Check whether a waiter's request lockmode conflict with the holder's hold mask */ extern bool CheckWaitLockModeConflictHoldMask(LOCKTAG tag, LOCKMODE waitLockMode, LOCKMASK holderMask); -#endif /* LOCK_H */ +#endif /* LOCK_H_ */ diff --git a/src/include/storage/lockdefs.h b/src/include/storage/lockdefs.h index 3c2c47dc6ae7..bda08edcfdb1 100644 --- a/src/include/storage/lockdefs.h +++ b/src/include/storage/lockdefs.h @@ -56,4 +56,4 @@ typedef struct xl_standby_lock Oid relOid; /* OID of table */ } xl_standby_lock; -#endif /* LOCKDEF_H_ */ +#endif /* LOCKDEFS_H_ */ diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index f9d008629ecb..6eae2afc6645 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -133,7 +133,7 @@ typedef enum LWLockMode { LW_EXCLUSIVE, LW_SHARED, - LW_WAIT_UNTIL_FREE /* A special mode used in PGPROC->lwlockMode, + LW_WAIT_UNTIL_FREE /* A special mode used in PGPROC->lwWaitMode, * when waiting for lock to become free. Not * to be used as LWLockAcquire argument */ } LWLockMode; diff --git a/src/include/storage/md.h b/src/include/storage/md.h index a92479b27cb0..d8e170ec62c0 100644 --- a/src/include/storage/md.h +++ b/src/include/storage/md.h @@ -21,6 +21,7 @@ /* md storage manager functionality */ extern void mdinit(void); +extern void mdopen(SMgrRelation reln); extern void mdclose(SMgrRelation reln, ForkNumber forknum); extern void mdcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo); extern void mdcreate_ao(RelFileNodeBackend rnode, int32 segmentFileNum, bool isRedo); diff --git a/src/include/storage/predicate_internals.h b/src/include/storage/predicate_internals.h index de7a846cf353..a1b981302d96 100644 --- a/src/include/storage/predicate_internals.h +++ b/src/include/storage/predicate_internals.h @@ -52,7 +52,7 @@ typedef uint64 SerCommitSeqNo; * * Eligibility for cleanup of committed transactions is generally determined * by comparing the transaction's finishedBefore field to - * SerializableGlobalXmin. + * SxactGlobalXmin. */ typedef struct SERIALIZABLEXACT { diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h index ef1954f9ec2b..a0f1a730b600 100644 --- a/src/include/storage/sinval.h +++ b/src/include/storage/sinval.h @@ -129,8 +129,7 @@ extern volatile sig_atomic_t catchupInterruptPending; extern void SendSharedInvalidMessages(const SharedInvalidationMessage *msgs, int n); -extern void ReceiveSharedInvalidMessages( - void (*invalFunction) (SharedInvalidationMessage *msg), +extern void ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *msg), void (*resetFunction) (void)); /* signal handler for catchup events (PROCSIG_CATCHUP_INTERRUPT) */ diff --git a/src/include/utils/dsa.h b/src/include/utils/dsa.h index ddd3cef8c2fc..991b62d28c4c 100644 --- a/src/include/utils/dsa.h +++ b/src/include/utils/dsa.h @@ -99,8 +99,6 @@ typedef pg_atomic_uint64 dsa_pointer_atomic; */ typedef dsm_handle dsa_handle; -extern void dsa_startup(void); - extern dsa_area *dsa_create(int tranche_id); extern dsa_area *dsa_create_in_place(void *place, size_t size, int tranche_id, dsm_segment *segment); diff --git a/src/include/utils/formatting.h b/src/include/utils/formatting.h index 5b275dc98504..741c5f4809fa 100644 --- a/src/include/utils/formatting.h +++ b/src/include/utils/formatting.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1999-2019, PostgreSQL Global Development Group * * The PostgreSQL routines for a DateTime/int/float/numeric formatting, - * inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines. + * inspired by the Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines. * * Karel Zak * diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 454490bd3d1b..9c90380b8d42 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -632,8 +632,7 @@ extern IndexCheckType gp_indexcheck_insert; extern void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source); -extern void DefineCustomBoolVariable( - const char *name, +extern void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, @@ -644,8 +643,7 @@ extern void DefineCustomBoolVariable( GucBoolAssignHook assign_hook, GucShowHook show_hook); -extern void DefineCustomIntVariable( - const char *name, +extern void DefineCustomIntVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, @@ -658,8 +656,7 @@ extern void DefineCustomIntVariable( GucIntAssignHook assign_hook, GucShowHook show_hook); -extern void DefineCustomRealVariable( - const char *name, +extern void DefineCustomRealVariable(const char *name, const char *short_desc, const char *long_desc, double *valueAddr, @@ -672,8 +669,7 @@ extern void DefineCustomRealVariable( GucRealAssignHook assign_hook, GucShowHook show_hook); -extern void DefineCustomStringVariable( - const char *name, +extern void DefineCustomStringVariable(const char *name, const char *short_desc, const char *long_desc, char **valueAddr, @@ -684,8 +680,7 @@ extern void DefineCustomStringVariable( GucStringAssignHook assign_hook, GucShowHook show_hook); -extern void DefineCustomEnumVariable( - const char *name, +extern void DefineCustomEnumVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h index 2fe7d32fec2d..ac52b75f51d8 100644 --- a/src/include/utils/jsonb.h +++ b/src/include/utils/jsonb.h @@ -367,7 +367,7 @@ extern JsonbValue *findJsonbValueFromContainer(JsonbContainer *sheader, extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader, uint32 i); extern JsonbValue *pushJsonbValue(JsonbParseState **pstate, - JsonbIteratorToken seq, JsonbValue *jbVal); + JsonbIteratorToken seq, JsonbValue *jbval); extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container); extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 80c8b11fde3f..184e66914459 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -159,13 +159,6 @@ typedef struct RelationData * those with lefttype and righttype equal to the opclass's opcintype. The * arrays are indexed by support function number, which is a sufficient * identifier given that restriction. - * - * Note: rd_amcache is available for index AMs to cache private data about - * an index. This must be just a cache since it may get reset at any time - * (in particular, it will get reset by a relcache inval message for the - * index). If used, it must point to a single memory chunk palloc'd in - * rd_indexcxt. A relcache reset will include freeing that chunk and - * setting rd_amcache = NULL. */ MemoryContext rd_indexcxt; /* private memory cxt for this stuff */ /* use "struct" here to avoid needing to include amapi.h: */ @@ -180,9 +173,19 @@ typedef struct RelationData Oid *rd_exclops; /* OIDs of exclusion operators, if any */ Oid *rd_exclprocs; /* OIDs of exclusion ops' procs, if any */ uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */ - void *rd_amcache; /* available for use by index AM */ Oid *rd_indcollation; /* OIDs of index collations */ + /* + * rd_amcache is available for index and table AMs to cache private data + * about the relation. This must be just a cache since it may get reset + * at any time (in particular, it will get reset by a relcache inval + * message for the relation). If used, it must point to a single memory + * chunk palloc'd in CacheMemoryContext, or in rd_indexcxt for an index + * relation. A relcache reset will include freeing that chunk and setting + * rd_amcache = NULL. + */ + void *rd_amcache; /* available for use by index/table AM */ + /* * foreign-table support * @@ -689,6 +692,5 @@ typedef struct ViewOptions extern void RelationIncrementReferenceCount(Relation rel); extern void RelationDecrementReferenceCount(Relation rel); extern bool RelationHasUnloggedIndex(Relation rel); -extern List *RelationGetRepsetList(Relation rel); #endif /* REL_H */ diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 165eee66f5de..75cf2c7fe9fa 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -62,7 +62,7 @@ typedef enum IndexAttrBitmapKind } IndexAttrBitmapKind; extern Bitmapset *RelationGetIndexAttrBitmap(Relation relation, - IndexAttrBitmapKind keyAttrs); + IndexAttrBitmapKind attrKind); extern void RelationGetExclusionInfo(Relation indexRelation, Oid **operators, diff --git a/src/include/utils/snapmgr.h b/src/include/utils/snapmgr.h index 0d2654ad3ec3..cb4a4750327c 100644 --- a/src/include/utils/snapmgr.h +++ b/src/include/utils/snapmgr.h @@ -13,6 +13,7 @@ #ifndef SNAPMGR_H #define SNAPMGR_H +#include "access/transam.h" #include "fmgr.h" #include "utils/relcache.h" #include "utils/resowner.h" @@ -121,6 +122,8 @@ extern void UnregisterSnapshot(Snapshot snapshot); extern Snapshot RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner); extern void UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner); +extern FullTransactionId GetFullRecentGlobalXmin(void); + extern void AtSubCommit_Snapshot(int level); extern void AtSubAbort_Snapshot(int level); extern void AtEOXact_Snapshot(bool isCommit, bool resetXmin); diff --git a/src/interfaces/ecpg/compatlib/exports.txt b/src/interfaces/ecpg/compatlib/exports.txt index e0cfd7a22d06..86e9ca164047 100644 --- a/src/interfaces/ecpg/compatlib/exports.txt +++ b/src/interfaces/ecpg/compatlib/exports.txt @@ -1,5 +1,5 @@ # src/interfaces/ecpg/compatlib/exports.txt -# Functions to be exported by ecpg_compatlib DLL +# Functions to be exported by libecpg_compat DLL ECPG_informix_get_var 1 ECPG_informix_set_var 2 decadd 3 diff --git a/src/interfaces/ecpg/compatlib/informix.c b/src/interfaces/ecpg/compatlib/informix.c index 13058cf7bf76..8fc0a467f7f5 100644 --- a/src/interfaces/ecpg/compatlib/informix.c +++ b/src/interfaces/ecpg/compatlib/informix.c @@ -811,7 +811,7 @@ rfmtlong(long lng_val, const char *fmt, char *outbuf) /* and fill the temp-string wit '0's up to there. */ dotpos = getRightMostDot(fmt); - /* start to parse the formatstring */ + /* start to parse the format-string */ temp[0] = '\0'; k = value.digits - 1; /* position in the value_string */ for (i = fmt_len - 1, j = 0; i >= 0; i--, j++) diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h index 6cb7ab1a192d..de8241bfc884 100644 --- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h +++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h @@ -195,11 +195,8 @@ char *ecpg_strdup(const char *, int); const char *ecpg_type_name(enum ECPGttype); int ecpg_dynamic_type(Oid); int sqlda_dynamic_type(Oid, enum COMPAT_MODE); -void ecpg_free_auto_mem(void); void ecpg_clear_auto_mem(void); -struct descriptor *ecpggetdescp(int, char *); - struct descriptor *ecpg_find_desc(int line, const char *name); struct prepared_statement *ecpg_find_prepared_statement(const char *, diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c index 3c0294e98aa8..4e2016c7aea6 100644 --- a/src/interfaces/ecpg/ecpglib/execute.c +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -1899,7 +1899,7 @@ ecpg_process_output(struct statement *stmt, bool clear_result) /* * execution should never reach this code because it is already - * handled in ECPGcheck_PQresult() + * handled in ecpg_check_PQresult() */ ecpg_log("ecpg_process_output on line %d: unknown execution status type\n", stmt->lineno); @@ -2073,9 +2073,9 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator, * offset - offset between ith and (i+1)th entry in an array, normally * that means sizeof(type) * ind_type - type of indicator variable - * ind_value - pointer to indicator variable + * ind_pointer - pointer to indicator variable * ind_varcharsize - empty - * ind_arraysize - arraysize of indicator array + * ind_arrsize - arraysize of indicator array * ind_offset - indicator offset *------ */ diff --git a/src/interfaces/ecpg/ecpglib/nls.mk b/src/interfaces/ecpg/ecpglib/nls.mk index 5ce97ead8e3b..8ea50ff19e3a 100644 --- a/src/interfaces/ecpg/ecpglib/nls.mk +++ b/src/interfaces/ecpg/ecpglib/nls.mk @@ -1,6 +1,6 @@ # src/interfaces/ecpg/ecpglib/nls.mk CATALOG_NAME = ecpglib AVAIL_LANGUAGES = cs de es fr it ja ko pl pt_BR ru sv tr vi zh_CN -GETTEXT_FILES = connect.c descriptor.c error.c execute.c misc.c +GETTEXT_FILES = connect.c cursor.c descriptor.c error.c execute.c misc.c GETTEXT_TRIGGERS = ecpg_gettext GETTEXT_FLAGS = ecpg_gettext:1:pass-c-format diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index 0dcf736390bd..6edff5c0c0dc 100644 --- a/src/interfaces/ecpg/ecpglib/prepare.c +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -754,7 +754,11 @@ ecpg_update_declare_statement(const char *declared_name, const char *cursor_name /* Find the declared node by declared name */ p = ecpg_find_declared_statement(declared_name); if (p) + { + if (p->cursor_name) + ecpg_free(p->cursor_name); p->cursor_name = ecpg_strdup(cursor_name, lineno); + } } /* diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index 4d0b5a9a8b38..56279fa84c98 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -63,8 +63,6 @@ char *ECPGprepared_statement(const char *, const char *, int); PGconn *ECPGget_PGconn(const char *); PGTransactionStatusType ECPGtransactionStatus(const char *); -char *ECPGerrmsg(void); - /* print an error message */ void sqlprint(void); diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c index 24fff28f0aed..e71defaa66a6 100644 --- a/src/interfaces/ecpg/pgtypeslib/dt_common.c +++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c @@ -6,6 +6,7 @@ #include #include +#include "common/string.h" #include "pgtypeslib_extern.h" #include "dt.h" #include "pgtypes_timestamp.h" @@ -171,7 +172,7 @@ static const datetkn datetktbl[] = { ghst #endif {"gilt", TZ, 43200}, /* Gilbert Islands Time */ - {"gmt", TZ, 0}, /* Greenwish Mean Time */ + {"gmt", TZ, 0}, /* Greenwich Mean Time */ {"gst", TZ, 36000}, /* Guam Std Time, USSR Zone 9 */ {"gyt", TZ, -14400}, /* Guyana Time */ {"h", UNITS, DTK_HOUR}, /* "hour" */ @@ -1110,7 +1111,7 @@ DecodeNumberField(int len, char *str, int fmask, for (i = 0; i < 6; i++) fstr[i] = *cp != '\0' ? *cp++ : '0'; fstr[i] = '\0'; - *fsec = strtol(fstr, NULL, 10); + *fsec = strtoint(fstr, NULL, 10); *cp = '\0'; len = strlen(str); } @@ -1201,7 +1202,7 @@ DecodeNumber(int flen, char *str, int fmask, *tmask = 0; - val = strtol(str, &cp, 10); + val = strtoint(str, &cp, 10); if (cp == str) return -1; @@ -1437,11 +1438,11 @@ DecodeTime(char *str, int *tmask, struct tm *tm, fsec_t *fsec) *tmask = DTK_TIME_M; - tm->tm_hour = strtol(str, &cp, 10); + tm->tm_hour = strtoint(str, &cp, 10); if (*cp != ':') return -1; str = cp + 1; - tm->tm_min = strtol(str, &cp, 10); + tm->tm_min = strtoint(str, &cp, 10); if (*cp == '\0') { tm->tm_sec = 0; @@ -1452,7 +1453,7 @@ DecodeTime(char *str, int *tmask, struct tm *tm, fsec_t *fsec) else { str = cp + 1; - tm->tm_sec = strtol(str, &cp, 10); + tm->tm_sec = strtoint(str, &cp, 10); if (*cp == '\0') *fsec = 0; else if (*cp == '.') @@ -1473,7 +1474,7 @@ DecodeTime(char *str, int *tmask, struct tm *tm, fsec_t *fsec) for (i = 0; i < 6; i++) fstr[i] = *cp != '\0' ? *cp++ : '0'; fstr[i] = '\0'; - *fsec = strtol(fstr, &cp, 10); + *fsec = strtoint(fstr, &cp, 10); if (*cp != '\0') return -1; } @@ -1505,20 +1506,20 @@ DecodeTimezone(char *str, int *tzp) int len; /* assume leading character is "+" or "-" */ - hr = strtol(str + 1, &cp, 10); + hr = strtoint(str + 1, &cp, 10); /* explicit delimiter? */ if (*cp == ':') - min = strtol(cp + 1, &cp, 10); + min = strtoint(cp + 1, &cp, 10); /* otherwise, might have run things together... */ else if (*cp == '\0' && (len = strlen(str)) > 3) { - min = strtol(str + len - 2, &cp, 10); + min = strtoint(str + len - 2, &cp, 10); if (min < 0 || min >= 60) return -1; *(str + len - 2) = '\0'; - hr = strtol(str + 1, &cp, 10); + hr = strtoint(str + 1, &cp, 10); if (hr < 0 || hr > 13) return -1; } @@ -1825,7 +1826,7 @@ DecodeDateTime(char **field, int *ftype, int nf, if (tzp == NULL) return -1; - val = strtol(field[i], &cp, 10); + val = strtoint(field[i], &cp, 10); if (*cp != '-') return -1; @@ -1960,7 +1961,7 @@ DecodeDateTime(char **field, int *ftype, int nf, char *cp; int val; - val = strtol(field[i], &cp, 10); + val = strtoint(field[i], &cp, 10); /* * only a few kinds are allowed to have an embedded diff --git a/src/interfaces/ecpg/pgtypeslib/timestamp.c b/src/interfaces/ecpg/pgtypeslib/timestamp.c index efd9f3aa7198..e830ee737e7e 100644 --- a/src/interfaces/ecpg/pgtypeslib/timestamp.c +++ b/src/interfaces/ecpg/pgtypeslib/timestamp.c @@ -275,8 +275,8 @@ PGTYPEStimestamp_to_asc(timestamp tstamp) *tm = &tt; char buf[MAXDATELEN + 1]; fsec_t fsec; - int DateStyle = 1; /* this defaults to ISO_DATES, shall we make - * it an option? */ + int DateStyle = 1; /* this defaults to USE_ISO_DATES, shall we + * make it an option? */ if (TIMESTAMP_NOT_FINITE(tstamp)) EncodeSpecialTimestamp(tstamp, buf); diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 20e3b4787468..2ec102362634 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -19,7 +19,6 @@ override CPPFLAGS := -I../include -I$(top_srcdir)/src/interfaces/ecpg/include \ -I. -I$(srcdir) \ -I$(top_srcdir)/src/interfaces/ecpg/ecpglib \ -I$(libpq_srcdir) \ - -DECPG_COMPILE \ $(CPPFLAGS) override CFLAGS += $(PTHREAD_CFLAGS) diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons index eaae03c017bf..fd2daa317888 100644 --- a/src/interfaces/ecpg/preproc/ecpg.addons +++ b/src/interfaces/ecpg/preproc/ecpg.addons @@ -52,7 +52,7 @@ ECPG: stmtExecuteStmt block /* It must be cut off double quotation because new_variable() double-quotes. */ str[strlen(str) - 1] = '\0'; - sprintf(length, "%d", (int) strlen(str)); + sprintf(length, "%zu", strlen(str)); add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator); } output_statement(cat_str(3, mm_strdup("execute"), mm_strdup("$0"), $1.type), 0, ECPGst_exec_with_exprlist); @@ -79,7 +79,7 @@ ECPG: stmtPrepareStmt block /* It must be cut off double quotation because new_variable() double-quotes. */ str[strlen(str) - 1] = '\0'; - sprintf(length, "%d", (int) strlen(str)); + sprintf(length, "%zu", strlen(str)); add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator); } output_statement(cat_str(5, mm_strdup("prepare"), mm_strdup("$0"), $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_prepare); diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index ccb94e3c0440..0aecb72ddb40 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -265,7 +265,7 @@ main(int argc, char *const argv[]) snprintf(informix_path, MAXPGPATH, "%s/informix/esql", pkginclude_path); add_include_path(informix_path); } - else if (strncmp(optarg, "ORACLE", strlen("ORACLE")) == 0) + else if (pg_strcasecmp(optarg, "ORACLE") == 0) { compat = ECPG_COMPAT_ORACLE; } @@ -276,11 +276,11 @@ main(int argc, char *const argv[]) } break; case 'r': - if (strcmp(optarg, "no_indicator") == 0) + if (pg_strcasecmp(optarg, "no_indicator") == 0) force_indicator = false; - else if (strcmp(optarg, "prepare") == 0) + else if (pg_strcasecmp(optarg, "prepare") == 0) auto_prepare = true; - else if (strcmp(optarg, "questionmarks") == 0) + else if (pg_strcasecmp(optarg, "questionmarks") == 0) questionmarks = true; else { diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer index 2450e747ad0d..2d1188ff41cf 100644 --- a/src/interfaces/ecpg/preproc/ecpg.trailer +++ b/src/interfaces/ecpg/preproc/ecpg.trailer @@ -1099,7 +1099,7 @@ UsingValue: UsingConst { char *length = mm_alloc(32); - sprintf(length, "%d", (int) strlen($1)); + sprintf(length, "%zu", strlen($1)); add_variable_to_head(&argsinsert, new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator); } | civar { $$ = EMPTY; } @@ -1226,7 +1226,7 @@ IntConstVar: Iconst { char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3); - sprintf(length, "%d", (int) strlen($1)); + sprintf(length, "%zu", strlen($1)); new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0); $$ = $1; } @@ -1272,7 +1272,7 @@ AllConstVar: ecpg_fconst { char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3); - sprintf(length, "%d", (int) strlen($1)); + sprintf(length, "%zu", strlen($1)); new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0); $$ = $1; } @@ -1287,7 +1287,7 @@ AllConstVar: ecpg_fconst char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3); char *var = cat2_str(mm_strdup("-"), $2); - sprintf(length, "%d", (int) strlen(var)); + sprintf(length, "%zu", strlen(var)); new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0); $$ = var; } @@ -1297,7 +1297,7 @@ AllConstVar: ecpg_fconst char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3); char *var = cat2_str(mm_strdup("-"), $2); - sprintf(length, "%d", (int) strlen(var)); + sprintf(length, "%zu", strlen(var)); new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0); $$ = var; } @@ -1308,7 +1308,7 @@ AllConstVar: ecpg_fconst char *var = $1 + 1; var[strlen(var) - 1] = '\0'; - sprintf(length, "%d", (int) strlen(var)); + sprintf(length, "%zu", strlen(var)); new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0); $$ = var; } diff --git a/src/interfaces/ecpg/preproc/preproc_extern.h b/src/interfaces/ecpg/preproc/preproc_extern.h index 552ffc627bc3..7de5cd50878d 100644 --- a/src/interfaces/ecpg/preproc/preproc_extern.h +++ b/src/interfaces/ecpg/preproc/preproc_extern.h @@ -30,7 +30,6 @@ extern int braces_open, struct_level, ecpg_internal_var; extern char *current_function; -extern char *descriptor_index; extern char *descriptor_name; extern char *connection; extern char *input_filename; diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index 84d327f2d857..0c6ed9348516 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -328,14 +328,23 @@ build_client_first_message(fe_scram_state *state) return NULL; } - state->client_nonce = malloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1); + encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN); + /* don't forget the zero-terminator */ + state->client_nonce = malloc(encoded_len + 1); if (state->client_nonce == NULL) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return NULL; } - encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->client_nonce); + encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, + state->client_nonce, encoded_len); + if (encoded_len < 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not encode nonce\n")); + return NULL; + } state->client_nonce[encoded_len] = '\0'; /* @@ -353,7 +362,7 @@ build_client_first_message(fe_scram_state *state) if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0) { Assert(conn->ssl_in_use); - appendPQExpBuffer(&buf, "p=tls-server-end-point"); + appendPQExpBufferStr(&buf, "p=tls-server-end-point"); } #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH else if (conn->ssl_in_use) @@ -361,7 +370,7 @@ build_client_first_message(fe_scram_state *state) /* * Client supports channel binding, but thinks the server does not. */ - appendPQExpBuffer(&buf, "y"); + appendPQExpBufferChar(&buf, 'y'); } #endif else @@ -369,7 +378,7 @@ build_client_first_message(fe_scram_state *state) /* * Client does not support channel binding. */ - appendPQExpBuffer(&buf, "n"); + appendPQExpBufferChar(&buf, 'n'); } if (PQExpBufferDataBroken(buf)) @@ -413,6 +422,7 @@ build_client_final_message(fe_scram_state *state) PGconn *conn = state->conn; uint8 client_proof[SCRAM_KEY_LEN]; char *result; + int encoded_len; initPQExpBuffer(&buf); @@ -432,6 +442,7 @@ build_client_final_message(fe_scram_state *state) size_t cbind_header_len; char *cbind_input; size_t cbind_input_len; + int encoded_cbind_len; /* Fetch hash data of server's SSL certificate */ cbind_data = @@ -444,7 +455,7 @@ build_client_final_message(fe_scram_state *state) return NULL; } - appendPQExpBuffer(&buf, "c="); + appendPQExpBufferStr(&buf, "c="); /* p=type,, */ cbind_header_len = strlen("p=tls-server-end-point,,"); @@ -458,13 +469,26 @@ build_client_final_message(fe_scram_state *state) memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len); memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len); - if (!enlargePQExpBuffer(&buf, pg_b64_enc_len(cbind_input_len))) + encoded_cbind_len = pg_b64_enc_len(cbind_input_len); + if (!enlargePQExpBuffer(&buf, encoded_cbind_len)) { free(cbind_data); free(cbind_input); goto oom_error; } - buf.len += pg_b64_encode(cbind_input, cbind_input_len, buf.data + buf.len); + encoded_cbind_len = pg_b64_encode(cbind_input, cbind_input_len, + buf.data + buf.len, + encoded_cbind_len); + if (encoded_cbind_len < 0) + { + free(cbind_data); + free(cbind_input); + termPQExpBuffer(&buf); + printfPQExpBuffer(&conn->errorMessage, + "could not encode cbind data for channel binding\n"); + return NULL; + } + buf.len += encoded_cbind_len; buf.data[buf.len] = '\0'; free(cbind_data); @@ -482,10 +506,10 @@ build_client_final_message(fe_scram_state *state) } #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH else if (conn->ssl_in_use) - appendPQExpBuffer(&buf, "c=eSws"); /* base64 of "y,," */ + appendPQExpBufferStr(&buf, "c=eSws"); /* base64 of "y,," */ #endif else - appendPQExpBuffer(&buf, "c=biws"); /* base64 of "n,," */ + appendPQExpBufferStr(&buf, "c=biws"); /* base64 of "n,," */ if (PQExpBufferDataBroken(buf)) goto oom_error; @@ -503,12 +527,22 @@ build_client_final_message(fe_scram_state *state) state->client_final_message_without_proof, client_proof); - appendPQExpBuffer(&buf, ",p="); - if (!enlargePQExpBuffer(&buf, pg_b64_enc_len(SCRAM_KEY_LEN))) + appendPQExpBufferStr(&buf, ",p="); + encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN); + if (!enlargePQExpBuffer(&buf, encoded_len)) goto oom_error; - buf.len += pg_b64_encode((char *) client_proof, - SCRAM_KEY_LEN, - buf.data + buf.len); + encoded_len = pg_b64_encode((char *) client_proof, + SCRAM_KEY_LEN, + buf.data + buf.len, + encoded_len); + if (encoded_len < 0) + { + termPQExpBuffer(&buf); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not encode client proof\n")); + return NULL; + } + buf.len += encoded_len; buf.data[buf.len] = '\0'; result = strdup(buf.data); @@ -536,6 +570,7 @@ read_server_first_message(fe_scram_state *state, char *input) char *endptr; char *encoded_salt; char *nonce; + int decoded_salt_len; state->server_first_message = strdup(input); if (state->server_first_message == NULL) @@ -577,7 +612,8 @@ read_server_first_message(fe_scram_state *state, char *input) /* read_attr_value() has generated an error string */ return false; } - state->salt = malloc(pg_b64_dec_len(strlen(encoded_salt))); + decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt)); + state->salt = malloc(decoded_salt_len); if (state->salt == NULL) { printfPQExpBuffer(&conn->errorMessage, @@ -586,7 +622,8 @@ read_server_first_message(fe_scram_state *state, char *input) } state->saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), - state->salt); + state->salt, + decoded_salt_len); if (state->saltlen < 0) { printfPQExpBuffer(&conn->errorMessage, @@ -670,7 +707,8 @@ read_server_final_message(fe_scram_state *state, char *input) server_signature_len = pg_b64_decode(encoded_server_signature, strlen(encoded_server_signature), - decoded_server_signature); + decoded_server_signature, + server_signature_len); if (server_signature_len != SCRAM_KEY_LEN) { free(decoded_server_signature); diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index c26ae254d518..92fd5e61e104 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -2867,8 +2867,8 @@ PQconnectPoll(PGconn *conn) } else if (!conn->gctx && conn->gssencmode[0] == 'r') { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("GSSAPI encryption required, but was impossible (possibly no ccache, no server support, or using a local socket)\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("GSSAPI encryption required, but was impossible (possibly no ccache, no server support, or using a local socket)\n")); goto error_return; } #endif @@ -5193,6 +5193,8 @@ parseServiceFile(const char *serviceFile, while ((line = fgets(buf, sizeof(buf), f)) != NULL) { + int len; + linenr++; if (strlen(line) >= sizeof(buf) - 1) @@ -5205,16 +5207,17 @@ parseServiceFile(const char *serviceFile, return 2; } - /* ignore EOL at end of line */ - if (strlen(line) && line[strlen(line) - 1] == '\n') - line[strlen(line) - 1] = 0; + /* ignore whitespace at end of line, especially the newline */ + len = strlen(line); + while (len > 0 && isspace((unsigned char) line[len - 1])) + line[--len] = '\0'; - /* ignore leading blanks */ + /* ignore leading whitespace too */ while (*line && isspace((unsigned char) line[0])) line++; /* ignore comments and empty lines */ - if (strlen(line) == 0 || line[0] == '#') + if (line[0] == '\0' || line[0] == '#') continue; /* Check for right groupname */ @@ -7083,14 +7086,10 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname, len = strlen(buf); - /* Remove trailing newline */ - if (len > 0 && buf[len - 1] == '\n') - { + /* Remove trailing newline, including \r in case we're on Windows */ + while (len > 0 && (buf[len - 1] == '\n' || + buf[len - 1] == '\r')) buf[--len] = '\0'; - /* Handle DOS-style line endings, too, even when not on Windows */ - if (len > 0 && buf[len - 1] == '\r') - buf[--len] = '\0'; - } if (len == 0) continue; diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 4cccaa4e3cb3..66e5bca53b5f 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -2279,7 +2279,7 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target) /* remember we are doing a Describe */ conn->queryclass = PGQUERY_DESCRIBE; - /* reset last-query string (not relevant now) */ + /* reset last_query string (not relevant now) */ if (conn->last_query) { free(conn->last_query); diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c index 6866d645380a..e755f69ab8ac 100644 --- a/src/interfaces/libpq/fe-lobj.c +++ b/src/interfaces/libpq/fe-lobj.c @@ -853,7 +853,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename) } /* if we already failed, don't overwrite that msg with a close error */ - if (close(fd) && result >= 0) + if (close(fd) != 0 && result >= 0) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not write to file \"%s\": %s\n"), diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 738de98fc20e..7a58fcf5c074 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -1154,7 +1154,7 @@ pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, /* If we couldn't allocate a PGresult, just say "out of memory" */ if (res == NULL) { - appendPQExpBuffer(msg, libpq_gettext("out of memory\n")); + appendPQExpBufferStr(msg, libpq_gettext("out of memory\n")); return; } @@ -1167,7 +1167,7 @@ pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, if (res->errMsg && res->errMsg[0]) appendPQExpBufferStr(msg, res->errMsg); else - appendPQExpBuffer(msg, libpq_gettext("no error message available\n")); + appendPQExpBufferStr(msg, libpq_gettext("no error message available\n")); return; } diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 2c97b7f165d1..f246578ce35c 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -738,7 +738,6 @@ extern int pqWriteReady(PGconn *conn); /* === in fe-secure.c === */ extern int pqsecure_initialize(PGconn *); -extern void pqsecure_destroy(void); extern PostgresPollingStatusType pqsecure_open_client(PGconn *); extern void pqsecure_close(PGconn *); extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len); diff --git a/src/interfaces/libpq/libpq.rc.in b/src/interfaces/libpq/libpq.rc.in index d15e731f0c84..896651a41157 100644 --- a/src/interfaces/libpq/libpq.rc.in +++ b/src/interfaces/libpq/libpq.rc.in @@ -1,8 +1,8 @@ #include VS_VERSION_INFO VERSIONINFO - FILEVERSION 12,0,0,0 - PRODUCTVERSION 12,0,0,0 + FILEVERSION 13,0,0,0 + PRODUCTVERSION 13,0,0,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0 FILEOS VOS__WINDOWS32 @@ -15,13 +15,13 @@ BEGIN BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "PostgreSQL Access Library\0" - VALUE "FileVersion", "12.0\0" + VALUE "FileVersion", "13.0\0" VALUE "InternalName", "libpq\0" VALUE "LegalCopyright", "Copyright (C) 2019\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "libpq.dll\0" VALUE "ProductName", "PostgreSQL\0" - VALUE "ProductVersion", "12.0\0" + VALUE "ProductVersion", "13.0\0" END END BLOCK "VarFileInfo" diff --git a/src/interfaces/libpq/nls.mk b/src/interfaces/libpq/nls.mk index 4196870b4961..28f3941ab920 100644 --- a/src/interfaces/libpq/nls.mk +++ b/src/interfaces/libpq/nls.mk @@ -1,6 +1,6 @@ # src/interfaces/libpq/nls.mk CATALOG_NAME = libpq AVAIL_LANGUAGES = cs de es fr he it ja ko pl pt_BR ru sv tr zh_CN zh_TW -GETTEXT_FILES = fe-auth.c fe-auth-scram.c fe-connect.c fe-exec.c fe-lobj.c fe-misc.c fe-protocol2.c fe-protocol3.c fe-secure.c fe-secure-common.c fe-secure-openssl.c win32.c +GETTEXT_FILES = fe-auth.c fe-auth-scram.c fe-connect.c fe-exec.c fe-gssapi-common.c fe-lobj.c fe-misc.c fe-protocol2.c fe-protocol3.c fe-secure.c fe-secure-common.c fe-secure-gssapi.c fe-secure-openssl.c win32.c GETTEXT_TRIGGERS = libpq_gettext pqInternalNotice:2 GETTEXT_FLAGS = libpq_gettext:1:pass-c-format pqInternalNotice:2:c-format diff --git a/src/makefiles/Makefile.freebsd b/src/makefiles/Makefile.freebsd index 98a6f50615ee..c462e2fd584f 100644 --- a/src/makefiles/Makefile.freebsd +++ b/src/makefiles/Makefile.freebsd @@ -1,9 +1,7 @@ AROPT = cr -ifdef ELF_SYSTEM export_dynamic = -Wl,-export-dynamic rpath = -Wl,-R'$(rpathdir)' -endif DLSUFFIX = .so @@ -20,14 +18,4 @@ endef # Rule for building a shared library from a single .o file %.so: %.o -ifdef ELF_SYSTEM $(CC) $(CFLAGS) $< $(LDFLAGS) $(LDFLAGS_SL) -shared -o $@ -else - $(LD) $(LDREL) $(LDOUT) $<.obj -x $< - @echo building shared object $@ - @rm -f $@.pic - @${AR} cq $@.pic $<.obj - ${RANLIB} $@.pic - @rm -f $@ - $(LD) -x -Bshareable -Bforcearchive -o $@ $@.pic -endif diff --git a/src/makefiles/Makefile.netbsd b/src/makefiles/Makefile.netbsd index 7bb9721fa5b8..15695fb65c73 100644 --- a/src/makefiles/Makefile.netbsd +++ b/src/makefiles/Makefile.netbsd @@ -1,11 +1,7 @@ AROPT = cr -ifdef ELF_SYSTEM export_dynamic = -Wl,-E rpath = -Wl,-R'$(rpathdir)' -else -rpath = -Wl,-R'$(rpathdir)' -endif DLSUFFIX = .so @@ -14,14 +10,4 @@ CFLAGS_SL = -fPIC -DPIC # Rule for building a shared library from a single .o file %.so: %.o -ifdef ELF_SYSTEM $(CC) $(CFLAGS) $< $(LDFLAGS) $(LDFLAGS_SL) -shared -o $@ -else - $(LD) $(LDREL) $(LDOUT) $<.obj -x $< - @echo building shared object $@ - @rm -f $@.pic - @${AR} cq $@.pic $<.obj - ${RANLIB} $@.pic - @rm -f $@ - $(LD) -x -Bshareable -Bforcearchive -o $@ $@.pic -endif diff --git a/src/makefiles/Makefile.openbsd b/src/makefiles/Makefile.openbsd index eda311087cb4..15695fb65c73 100644 --- a/src/makefiles/Makefile.openbsd +++ b/src/makefiles/Makefile.openbsd @@ -1,9 +1,7 @@ AROPT = cr -ifdef ELF_SYSTEM export_dynamic = -Wl,-E rpath = -Wl,-R'$(rpathdir)' -endif DLSUFFIX = .so @@ -12,14 +10,4 @@ CFLAGS_SL = -fPIC -DPIC # Rule for building a shared library from a single .o file %.so: %.o -ifdef ELF_SYSTEM $(CC) $(CFLAGS) $< $(LDFLAGS) $(LDFLAGS_SL) -shared -o $@ -else - $(LD) $(LDREL) $(LDOUT) $<.obj -x $< - @echo building shared object $@ - @rm -f $@.pic - @${AR} cq $@.pic $<.obj - ${RANLIB} $@.pic - @rm -f $@ - $(LD) -x -Bshareable -Bforcearchive -o $@ $@.pic -endif diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 2db13d303089..c480999c51d9 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -2091,7 +2091,7 @@ plperlu_validator(PG_FUNCTION_ARGS) /* - * Uses mksafefunc/mkunsafefunc to create a subroutine whose text is + * Uses mkfunc to create a subroutine whose text is * supplied in s, and returns a reference to it */ static void diff --git a/src/pl/plpgsql/src/expected/plpgsql_record.out b/src/pl/plpgsql/src/expected/plpgsql_record.out index 07cc70e59b38..3d129c5cc447 100644 --- a/src/pl/plpgsql/src/expected/plpgsql_record.out +++ b/src/pl/plpgsql/src/expected/plpgsql_record.out @@ -664,3 +664,16 @@ NOTICE: processing row 2 NOTICE: processing row 3 ERROR: value for domain ordered_texts violates check constraint "ordered_texts_check" CONTEXT: PL/pgSQL function inline_code_block line 8 at assignment +-- check coercion of a record result to named-composite function output type +create function compresult(int8) returns two_int8s language plpgsql as +$$ declare r record; begin r := row($1,$1); return r; end $$; +create table two_int8s_tab (f1 two_int8s); +insert into two_int8s_tab values (compresult(42)); +-- reconnect so we lose any local knowledge of anonymous record types +\c - +table two_int8s_tab; + f1 +--------- + (42,42) +(1 row) + diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 9059fe78d7a4..74b222bee68c 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -810,6 +810,31 @@ coerce_function_result_tuple(PLpgSQL_execstate *estate, TupleDesc tupdesc) estate->retval = PointerGetDatum(SPI_returntuple(rettup, tupdesc)); /* no need to free map, we're about to return anyway */ } + else if (!(tupdesc->tdtypeid == erh->er_decltypeid || + (tupdesc->tdtypeid == RECORDOID && + !ExpandedRecordIsDomain(erh)))) + { + /* + * The expanded record has the right physical tupdesc, but the + * wrong type ID. (Typically, the expanded record is RECORDOID + * but the function is declared to return a named composite type. + * As in exec_move_row_from_datum, we don't allow returning a + * composite-domain record from a function declared to return + * RECORD.) So we must flatten the record to a tuple datum and + * overwrite its type fields with the right thing. spi.c doesn't + * provide any easy way to deal with this case, so we end up + * duplicating the guts of datumCopy() :-( + */ + Size resultsize; + HeapTupleHeader tuphdr; + + resultsize = EOH_get_flat_size(&erh->hdr); + tuphdr = (HeapTupleHeader) SPI_palloc(resultsize); + EOH_flatten_into(&erh->hdr, (void *) tuphdr, resultsize); + HeapTupleHeaderSetTypeId(tuphdr, tupdesc->tdtypeid); + HeapTupleHeaderSetTypMod(tuphdr, tupdesc->tdtypmod); + estate->retval = PointerGetDatum(tuphdr); + } else { /* @@ -3271,10 +3296,10 @@ exec_stmt_return_next(PLpgSQL_execstate *estate, * reference; in particular, this path is always taken in functions with * one or more OUT parameters. * - * Unlike exec_statement_return, there's no special win here for R/W - * expanded values, since they'll have to get flattened to go into the - * tuplestore. Indeed, we'd better make them R/O to avoid any risk of the - * casting step changing them in-place. + * Unlike exec_stmt_return, there's no special win here for R/W expanded + * values, since they'll have to get flattened to go into the tuplestore. + * Indeed, we'd better make them R/O to avoid any risk of the casting step + * changing them in-place. */ if (stmt->retvarno >= 0) { @@ -3681,7 +3706,7 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) paramvalue, paramtypeid); appendStringInfoString(&ds, extval); - current_param = lnext(current_param); + current_param = lnext(stmt->params, current_param); exec_eval_cleanup(estate); } else diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y index dea95f42308c..a82930e81d9b 100644 --- a/src/pl/plpgsql/src/pl_gram.y +++ b/src/pl/plpgsql/src/pl_gram.y @@ -3691,7 +3691,7 @@ plpgsql_sql_error_callback(void *arg) internalerrposition(myerrpos + errpos - cbarg->leaderlen - 1); } - /* In any case, flush errposition --- we want internalerrpos only */ + /* In any case, flush errposition --- we want internalerrposition only */ errposition(0); } diff --git a/src/pl/plpgsql/src/sql/plpgsql_record.sql b/src/pl/plpgsql/src/sql/plpgsql_record.sql index 99f9bd434695..893bdbdaf9cd 100644 --- a/src/pl/plpgsql/src/sql/plpgsql_record.sql +++ b/src/pl/plpgsql/src/sql/plpgsql_record.sql @@ -450,3 +450,13 @@ begin d.f2 := r.b; end loop; end$$; + +-- check coercion of a record result to named-composite function output type +create function compresult(int8) returns two_int8s language plpgsql as +$$ declare r record; begin r := row($1,$1); return r; end $$; + +create table two_int8s_tab (f1 two_int8s); +insert into two_int8s_tab values (compresult(42)); +-- reconnect so we lose any local knowledge of anonymous record types +\c - +table two_int8s_tab; diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index ab9a1f1e0d9b..d3c7f541a708 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -1633,7 +1633,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, /************************************************************ * prefix procedure body with - * upvar #0 GD + * upvar #0 GD * and with appropriate setting of arguments ************************************************************/ Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1); diff --git a/src/port/sprompt.c b/src/port/sprompt.c index 146fb0004efd..02164d497a5d 100644 --- a/src/port/sprompt.c +++ b/src/port/sprompt.c @@ -144,9 +144,11 @@ simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo) } while (buflen > 0 && buf[buflen - 1] != '\n'); } - if (length > 0 && destination[length - 1] == '\n') - /* remove trailing newline */ - destination[length - 1] = '\0'; + /* strip trailing newline, including \r in case we're on Windows */ + while (length > 0 && + (destination[length - 1] == '\n' || + destination[length - 1] == '\r')) + destination[--length] = '\0'; if (!echo) { diff --git a/src/port/thread.c b/src/port/thread.c index 8487b9911305..c14f2727335d 100644 --- a/src/port/thread.c +++ b/src/port/thread.c @@ -83,7 +83,7 @@ pqGetpwuid(uid_t uid, struct passwd *resultbuf, char *buffer, /* * Wrapper around gethostbyname() or gethostbyname_r() to mimic * POSIX gethostbyname_r() behaviour, if it is not available or required. - * This function is called _only_ by our getaddinfo() portability function. + * This function is called _only_ by our getaddrinfo() portability function. */ #ifndef HAVE_GETADDRINFO int diff --git a/src/port/win32ver.rc b/src/port/win32ver.rc index 77a699dc48cb..5631ee91426a 100644 --- a/src/port/win32ver.rc +++ b/src/port/win32ver.rc @@ -2,8 +2,8 @@ #include "pg_config.h" VS_VERSION_INFO VERSIONINFO - FILEVERSION 12,0,0,0 - PRODUCTVERSION 12,0,0,0 + FILEVERSION 13,0,0,0 + PRODUCTVERSION 13,0,0,0 FILEFLAGSMASK 0x17L FILEFLAGS 0x0L FILEOS VOS_NT_WINDOWS32 diff --git a/src/test/isolation/expected/async-notify.out b/src/test/isolation/expected/async-notify.out index bf17b8253fbc..cd2e228bf0eb 100644 --- a/src/test/isolation/expected/async-notify.out +++ b/src/test/isolation/expected/async-notify.out @@ -93,9 +93,9 @@ nonzero f step bignotify: SELECT count(pg_notify('c1', s::text)) FROM generate_series(1, 10000) s; -count +count -10000 +10000 step usage: SELECT pg_notification_queue_usage() > 0 AS nonzero; nonzero diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c index 16150ea894ff..068e1d8fdc15 100644 --- a/src/test/isolation/isolationtester.c +++ b/src/test/isolation/isolationtester.c @@ -23,7 +23,8 @@ /* * conns[0] is the global setup, teardown, and watchdog connection. Additional - * connections represent spec-defined sessions. + * connections represent spec-defined sessions. We also track the backend + * PID, in numeric and string formats, for each connection. */ typedef struct IsoConnInfo { diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl index 6c02f2530b67..84a3300d964f 100644 --- a/src/test/ldap/t/001_auth.pl +++ b/src/test/ldap/t/001_auth.pl @@ -120,6 +120,22 @@ END append_to_file($ldap_pwfile, $ldap_rootpw); chmod 0600, $ldap_pwfile or die; +# wait until slapd accepts requests +my $retries = 0; +while (1) +{ + last + if ( + system_log( + "ldapsearch", "-h", $ldap_server, "-p", + $ldap_port, "-s", "base", "-b", + $ldap_basedn, "-D", $ldap_rootdn, "-y", + $ldap_pwfile, "-n", "'objectclass=*'") == 0); + die "cannot connect to slapd" if ++$retries >= 300; + note "waiting for slapd to accept requests..."; + Time::HiRes::usleep(1000000); +} + $ENV{'LDAPURI'} = $ldap_url; $ENV{'LDAPBINDDN'} = $ldap_rootdn; $ENV{'LDAPCONF'} = $ldap_conf; diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm index d2a9828dc621..6195c21c5984 100644 --- a/src/test/perl/TestLib.pm +++ b/src/test/perl/TestLib.pm @@ -430,7 +430,7 @@ sub command_exit_is # header file). IPC::Run's result function always returns exit code >> 8, # assuming the Unix convention, which will always return 0 on Windows as # long as the process was not terminated by an exception. To work around - # that, use $h->full_result on Windows instead. + # that, use $h->full_results on Windows instead. my $result = ($Config{osname} eq "MSWin32") ? ($h->full_results)[0] diff --git a/src/test/recovery/t/007_sync_rep.pl b/src/test/recovery/t/007_sync_rep.pl index bba47da17a4d..eccde8f551f0 100644 --- a/src/test/recovery/t/007_sync_rep.pl +++ b/src/test/recovery/t/007_sync_rep.pl @@ -27,6 +27,24 @@ sub test_sync_state return; } +# Start a standby and check that it is registered within the WAL sender +# array of the given primary. This polls the primary's pg_stat_replication +# until the standby is confirmed as registered. +sub start_standby_and_wait +{ + my ($master, $standby) = @_; + my $master_name = $master->name; + my $standby_name = $standby->name; + my $query = + "SELECT count(1) = 1 FROM pg_stat_replication WHERE application_name = '$standby_name'"; + + $standby->start; + + print("### Waiting for standby \"$standby_name\" on \"$master_name\"\n"); + $master->poll_query_until('postgres', $query); + return; +} + # Initialize master node my $node_master = get_new_node('master'); $node_master->init(allows_streaming => 1); @@ -36,23 +54,26 @@ sub test_sync_state # Take backup $node_master->backup($backup_name); +# Create all the standbys. Their status on the primary is checked to ensure +# the ordering of each one of them in the WAL sender array of the primary. + # Create standby1 linking to master my $node_standby_1 = get_new_node('standby1'); $node_standby_1->init_from_backup($node_master, $backup_name, has_streaming => 1); -$node_standby_1->start; +start_standby_and_wait($node_master, $node_standby_1); # Create standby2 linking to master my $node_standby_2 = get_new_node('standby2'); $node_standby_2->init_from_backup($node_master, $backup_name, has_streaming => 1); -$node_standby_2->start; +start_standby_and_wait($node_master, $node_standby_2); # Create standby3 linking to master my $node_standby_3 = get_new_node('standby3'); $node_standby_3->init_from_backup($node_master, $backup_name, has_streaming => 1); -$node_standby_3->start; +start_standby_and_wait($node_master, $node_standby_3); # Check that sync_state is determined correctly when # synchronous_standby_names is specified in old syntax. @@ -82,8 +103,10 @@ sub test_sync_state $node_standby_2->stop; $node_standby_3->stop; -$node_standby_2->start; -$node_standby_3->start; +# Make sure that each standby reports back to the primary in the wanted +# order. +start_standby_and_wait($node_master, $node_standby_2); +start_standby_and_wait($node_master, $node_standby_3); # Specify 2 as the number of sync standbys. # Check that two standbys are in 'sync' state. @@ -94,7 +117,7 @@ sub test_sync_state '2(standby1,standby2,standby3)'); # Start standby1 -$node_standby_1->start; +start_standby_and_wait($node_master, $node_standby_1); # Create standby4 linking to master my $node_standby_4 = get_new_node('standby4'); @@ -126,14 +149,16 @@ sub test_sync_state # The setting that * comes before another standby name is acceptable # but does not make sense in most cases. Check that sync_state is -# chosen properly even in case of that setting. -# The priority of standby2 should be 2 because it matches * first. +# chosen properly even in case of that setting. standby1 is selected +# as synchronous as it has the highest priority, and is followed by a +# second standby listed first in the WAL sender array, which is +# standby2 in this case. test_sync_state( $node_master, qq(standby1|1|sync standby2|2|sync standby3|2|potential standby4|2|potential), - 'asterisk comes before another standby name', + 'asterisk before another standby name', '2(standby1,*,standby2)'); # Check that the setting of '2(*)' chooses standby2 and standby3 that are stored diff --git a/src/test/recovery/t/011_crash_recovery.pl b/src/test/recovery/t/011_crash_recovery.pl index 5dc52412cadd..526a3481fb56 100644 --- a/src/test/recovery/t/011_crash_recovery.pl +++ b/src/test/recovery/t/011_crash_recovery.pl @@ -51,7 +51,7 @@ chomp($xid); is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]), - 'in progress', 'own xid is in-progres'); + 'in progress', 'own xid is in-progress'); # Crash and restart the postmaster $node->stop('immediate'); diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 67693b4788c1..10b27a509210 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1208,9 +1208,52 @@ explain (costs off) select * from t3 group by a,b,c; Optimizer: Postgres query optimizer (5 rows) -drop table t1; +create temp table t1c () inherits (t1); +-- Ensure we don't remove any columns when t1 has a child table +explain (costs off) select * from t1 group by a,b,c,d; + QUERY PLAN +------------------------------------- + HashAggregate + Group Key: t1.a, t1.b, t1.c, t1.d + -> Append + -> Seq Scan on t1 + -> Seq Scan on t1c +(5 rows) + +-- Okay to remove columns if we're only querying the parent. +explain (costs off) select * from only t1 group by a,b,c,d; + QUERY PLAN +---------------------- + HashAggregate + Group Key: a, b + -> Seq Scan on t1 +(3 rows) + +create temp table p_t1 ( + a int, + b int, + c int, + d int, + primary key(a,b) +) partition by list(a); +create temp table p_t1_1 partition of p_t1 for values in(1); +create temp table p_t1_2 partition of p_t1 for values in(2); +-- Ensure we can remove non-PK columns for partitioned tables. +explain (costs off) select * from p_t1 group by a,b,c,d; + QUERY PLAN +--------------------------------- + HashAggregate + Group Key: p_t1_1.a, p_t1_1.b + -> Append + -> Seq Scan on p_t1_1 + -> Seq Scan on p_t1_2 +(5 rows) + +drop table t1 cascade; +NOTICE: drop cascades to table t1c drop table t2; drop table t3; +drop table p_t1; -- -- Test combinations of DISTINCT and/or ORDER BY -- @@ -2414,10 +2457,10 @@ NOTICE: avg_transfn called with 3 -- this should not share the state due to different input columns. select my_avg(one),my_sum(two) from (values(1,2),(3,4)) t(one,two); -NOTICE: avg_transfn called with 2 NOTICE: avg_transfn called with 1 -NOTICE: avg_transfn called with 4 +NOTICE: avg_transfn called with 2 NOTICE: avg_transfn called with 3 +NOTICE: avg_transfn called with 4 my_avg | my_sum --------+-------- 2 | 6 diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index a3467d05de82..8bd815302086 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -1984,12 +1984,12 @@ create unique index on anothertab(f4); Indexes: "anothertab_pkey" PRIMARY KEY, btree (f1) "anothertab_f1_f4_key" UNIQUE CONSTRAINT, btree (f1, f4) - "anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2) - "anothertab_f4_idx" UNIQUE, btree (f4) "anothertab_f2_f3_idx" btree (f2, f3) + "anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2) "anothertab_f3_excl" EXCLUDE USING btree (f3 WITH =) "anothertab_f4_excl" EXCLUDE USING btree (f4 WITH =) WHERE (f4 IS NOT NULL) "anothertab_f4_excl1" EXCLUDE USING btree (f4 WITH =) WHERE (f5 > 0) + "anothertab_f4_idx" UNIQUE, btree (f4) Distributed Replicated -- In GPDB, you cannot change the type of a column that's part of a unique key @@ -2020,12 +2020,12 @@ alter table anothertab add constraint anothertab_f2_key unique (f2); Indexes: "anothertab_pkey" PRIMARY KEY, btree (f1) "anothertab_f1_f4_key" UNIQUE CONSTRAINT, btree (f1, f4) - "anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2) - "anothertab_f4_idx" UNIQUE, btree (f4) "anothertab_f2_f3_idx" btree (f2, f3) + "anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2) "anothertab_f3_excl" EXCLUDE USING btree (f3 WITH =) "anothertab_f4_excl" EXCLUDE USING btree (f4 WITH =) WHERE (f4 IS NOT NULL) "anothertab_f4_excl1" EXCLUDE USING btree (f4 WITH =) WHERE (f5 > 0) + "anothertab_f4_idx" UNIQUE, btree (f4) Distributed Replicated drop table anothertab; @@ -2813,7 +2813,7 @@ as 'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2 create operator alter1.=(procedure = alter1.same, leftarg = alter1.ctype, rightarg = alter1.ctype); create operator class alter1.ctype_hash_ops default for type alter1.ctype using hash as operator 1 alter1.=(alter1.ctype, alter1.ctype); -create conversion alter1.ascii_to_utf8 for 'sql_ascii' to 'utf8' from ascii_to_utf8; +create conversion alter1.latin1_to_utf8 for 'latin1' to 'utf8' from iso8859_1_to_utf8; create text search parser alter1.prs(start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); create text search configuration alter1.cfg(parser = alter1.prs); create text search template alter1.tmpl(init = dsimple_init, lexize = dsimple_lexize); @@ -2831,7 +2831,7 @@ alter operator alter1.=(alter1.ctype, alter1.ctype) set schema alter2; alter function alter1.same(alter1.ctype, alter1.ctype) set schema alter2; alter type alter1.ctype set schema alter1; -- no-op, same schema alter type alter1.ctype set schema alter2; -alter conversion alter1.ascii_to_utf8 set schema alter2; +alter conversion alter1.latin1_to_utf8 set schema alter2; alter text search parser alter1.prs set schema alter2; alter text search configuration alter1.cfg set schema alter2; alter text search template alter1.tmpl set schema alter2; @@ -2875,7 +2875,7 @@ drop cascades to type alter2.ctype drop cascades to function alter2.same(alter2.ctype,alter2.ctype) drop cascades to operator alter2.=(alter2.ctype,alter2.ctype) drop cascades to operator family alter2.ctype_hash_ops for access method hash -drop cascades to conversion alter2.ascii_to_utf8 +drop cascades to conversion alter2.latin1_to_utf8 drop cascades to text search parser alter2.prs drop cascades to text search configuration alter2.cfg drop cascades to text search template alter2.tmpl @@ -3616,13 +3616,13 @@ LINE 1: ALTER TABLE partitioned ADD EXCLUDE USING gist (a WITH &&); ^ -- cannot drop column that is part of the partition key ALTER TABLE partitioned DROP COLUMN a; -ERROR: cannot drop column named in partition key +ERROR: cannot drop column "a" because it is part of the partition key of relation "partitioned" ALTER TABLE partitioned ALTER COLUMN a TYPE char(5); -ERROR: cannot alter type of column named in partition key +ERROR: cannot alter column "a" because it is part of the partition key of relation "partitioned" ALTER TABLE partitioned DROP COLUMN b; -ERROR: cannot drop column referenced in partition key expression +ERROR: cannot drop column "b" because it is part of the partition key of relation "partitioned" ALTER TABLE partitioned ALTER COLUMN b TYPE char(5); -ERROR: cannot alter type of column referenced in partition key expression +ERROR: cannot alter column "b" because it is part of the partition key of relation "partitioned" -- partitioned table cannot participate in regular inheritance CREATE TABLE nonpartitioned ( a int, @@ -4107,9 +4107,9 @@ ERROR: cannot change inheritance of a partition -- partitioned tables; for example, part_5, which is list_parted2's -- partition, is partitioned on b; ALTER TABLE list_parted2 DROP COLUMN b; -ERROR: cannot drop column named in partition key +ERROR: cannot drop column "b" because it is part of the partition key of relation "part_5" ALTER TABLE list_parted2 ALTER COLUMN b TYPE text; -ERROR: cannot alter type of column named in partition key +ERROR: cannot alter column "b" because it is part of the partition key of relation "part_5" -- dropping non-partition key columns should be allowed on the parent table. ALTER TABLE list_parted DROP COLUMN b; SELECT * FROM list_parted; diff --git a/src/test/regress/expected/box.out b/src/test/regress/expected/box.out index e8191cab6e98..4c1b11b53b90 100644 --- a/src/test/regress/expected/box.out +++ b/src/test/regress/expected/box.out @@ -505,24 +505,34 @@ DROP INDEX box_spgist; -- -- Test the SP-GiST index on the larger volume of data -- -CREATE TABLE quad_box_tbl (b box); +CREATE TABLE quad_box_tbl (id int, b box); INSERT INTO quad_box_tbl - SELECT box(point(x * 10, y * 10), point(x * 10 + 5, y * 10 + 5)) - FROM generate_series(1, 100) x, - generate_series(1, 100) y; + SELECT (x - 1) * 100 + y, box(point(x * 10, y * 10), point(x * 10 + 5, y * 10 + 5)) + FROM generate_series(1, 100) x, + generate_series(1, 100) y; -- insert repeating data to test allTheSame INSERT INTO quad_box_tbl - SELECT '((200, 300),(210, 310))' - FROM generate_series(1, 1000); + SELECT i, '((200, 300),(210, 310))' + FROM generate_series(10001, 11000) AS i; INSERT INTO quad_box_tbl - VALUES - (NULL), - (NULL), - ('((-infinity,-infinity),(infinity,infinity))'), - ('((-infinity,100),(-infinity,500))'), - ('((-infinity,-infinity),(700,infinity))'); +VALUES + (11001, NULL), + (11002, NULL), + (11003, '((-infinity,-infinity),(infinity,infinity))'), + (11004, '((-infinity,100),(-infinity,500))'), + (11005, '((-infinity,-infinity),(700,infinity))'); CREATE INDEX quad_box_tbl_idx ON quad_box_tbl USING spgist(b); ANALYZE quad_box_tbl; +-- get reference results for ORDER BY distance from seq scan +SET enable_seqscan = ON; +SET enable_indexscan = OFF; +SET enable_bitmapscan = OFF; +CREATE TABLE quad_box_tbl_ord_seq1 AS +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl; +CREATE TABLE quad_box_tbl_ord_seq2 AS +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl WHERE b <@ box '((200,300),(500,600))'; SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = ON; @@ -604,6 +614,54 @@ SELECT count(*) FROM quad_box_tbl WHERE b ~= box '((200,300),(205,305))'; 1 (1 row) +-- test ORDER BY distance +SET enable_indexscan = ON; +SET enable_bitmapscan = OFF; +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl; + QUERY PLAN +--------------------------------------------------------- + WindowAgg + -> Index Scan using quad_box_tbl_idx on quad_box_tbl + Order By: (b <-> '(123,456)'::point) +(3 rows) + +CREATE TEMP TABLE quad_box_tbl_ord_idx1 AS +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl; +SELECT * +FROM quad_box_tbl_ord_seq1 seq FULL JOIN quad_box_tbl_ord_idx1 idx + ON seq.n = idx.n AND seq.id = idx.id AND + (seq.dist = idx.dist OR seq.dist IS NULL AND idx.dist IS NULL) +WHERE seq.id IS NULL OR idx.id IS NULL; + n | dist | id | n | dist | id +---+------+----+---+------+---- +(0 rows) + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl WHERE b <@ box '((200,300),(500,600))'; + QUERY PLAN +--------------------------------------------------------- + WindowAgg + -> Index Scan using quad_box_tbl_idx on quad_box_tbl + Index Cond: (b <@ '(500,600),(200,300)'::box) + Order By: (b <-> '(123,456)'::point) +(4 rows) + +CREATE TEMP TABLE quad_box_tbl_ord_idx2 AS +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl WHERE b <@ box '((200,300),(500,600))'; +SELECT * +FROM quad_box_tbl_ord_seq2 seq FULL JOIN quad_box_tbl_ord_idx2 idx + ON seq.n = idx.n AND seq.id = idx.id AND + (seq.dist = idx.dist OR seq.dist IS NULL AND idx.dist IS NULL) +WHERE seq.id IS NULL OR idx.id IS NULL; + n | dist | id | n | dist | id +---+------+----+---+------+---- +(0 rows) + RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 01bd9fb5dd79..51262e0bf44b 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -1,6 +1,13 @@ /* * This test is for ICU collations. */ +/* skip test if not UTF8 server encoding or no ICU collations installed */ +SELECT getdatabaseencoding() <> 'UTF8' OR + (SELECT count(*) FROM pg_collation WHERE collprovider = 'i') = 0 + AS skip_test \gset +\if :skip_test +\quit +\endif SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; SET search_path = collate_tests; diff --git a/src/test/regress/expected/collate.icu.utf8_1.out b/src/test/regress/expected/collate.icu.utf8_1.out new file mode 100644 index 000000000000..a6a33b39aba3 --- /dev/null +++ b/src/test/regress/expected/collate.icu.utf8_1.out @@ -0,0 +1,9 @@ +/* + * This test is for ICU collations. + */ +/* skip test if not UTF8 server encoding or no ICU collations installed */ +SELECT getdatabaseencoding() <> 'UTF8' OR + (SELECT count(*) FROM pg_collation WHERE collprovider = 'i') = 0 + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index 619688f851aa..ad56ff9caa37 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -3,6 +3,13 @@ * locales is installed. It must be run in a database with UTF-8 encoding, * because other encodings don't support all the characters used. */ +SELECT getdatabaseencoding() <> 'UTF8' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE', 'tr_TR') AND collencoding = pg_char_to_encoding('UTF8')) <> 4 OR + version() !~ 'linux-gnu' + AS skip_test \gset +\if :skip_test +\quit +\endif SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; SET search_path = collate_tests; diff --git a/src/test/regress/expected/collate.linux.utf8_1.out b/src/test/regress/expected/collate.linux.utf8_1.out new file mode 100644 index 000000000000..ede5fdb5dcc4 --- /dev/null +++ b/src/test/regress/expected/collate.linux.utf8_1.out @@ -0,0 +1,11 @@ +/* + * This test is for Linux/glibc systems and assumes that a full set of + * locales is installed. It must be run in a database with UTF-8 encoding, + * because other encodings don't support all the characters used. + */ +SELECT getdatabaseencoding() <> 'UTF8' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE', 'tr_TR') AND collencoding = pg_char_to_encoding('UTF8')) <> 4 OR + version() !~ 'linux-gnu' + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index d6eb55b0fe39..934dcec2bea5 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -1450,6 +1450,8 @@ Indexes: "concur_index3" UNIQUE, btree (dk, f2) INVALID "concur_heap_expr_idx" btree ((f2 || f1)) "concur_index1" btree (f2, f1) + "concur_index2" UNIQUE, btree (f1) + "concur_index3" UNIQUE, btree (f2) INVALID "concur_index4" btree (f2) WHERE f1 = 'a'::text "concur_index5" btree (f2) WHERE f1 = 'x'::text "std_index" btree (f2) @@ -1463,10 +1465,10 @@ REINDEX TABLE concur_heap; f1 | text | | | f2 | text | | | Indexes: - "concur_index2" UNIQUE, btree (f1) - "concur_index3" UNIQUE, btree (f2) "concur_heap_expr_idx" btree ((f2 || f1)) "concur_index1" btree (f2, f1) + "concur_index2" UNIQUE, btree (f1) + "concur_index3" UNIQUE, btree (f2) "concur_index4" btree (f2) WHERE f1 = 'a'::text "concur_index5" btree (f2) WHERE f1 = 'x'::text "std_index" btree (f2) @@ -2427,6 +2429,7 @@ Indexes: "concur_reindex_ind1" PRIMARY KEY, btree (c1) "concur_reindex_ind3" UNIQUE, btree (c1, abs(c1)) "concur_reindex_ind2" btree (c2) + "concur_reindex_ind3" UNIQUE, btree (abs(c1)) "concur_reindex_ind4" btree (c1, c1, c2) Referenced by: TABLE "concur_reindex_tab2" CONSTRAINT "concur_reindex_tab2_c1_fkey" FOREIGN KEY (c1) REFERENCES concur_reindex_tab(c1) @@ -2472,6 +2475,78 @@ ERROR: REINDEX CONCURRENTLY is not supported c1 | integer | | | DROP TABLE concur_reindex_tab4; +-- Check handling of indexes with expressions and predicates. The +-- definitions of the rebuilt indexes should match the original +-- definitions. +CREATE TABLE concur_exprs_tab (c1 int , c2 boolean); +INSERT INTO concur_exprs_tab (c1, c2) VALUES (1369652450, FALSE), + (414515746, TRUE), + (897778963, FALSE); +CREATE UNIQUE INDEX concur_exprs_index_expr + ON concur_exprs_tab ((c1::text COLLATE "C")); +CREATE UNIQUE INDEX concur_exprs_index_pred ON concur_exprs_tab (c1) + WHERE (c1::text > 500000000::text COLLATE "C"); +CREATE UNIQUE INDEX concur_exprs_index_pred_2 + ON concur_exprs_tab ((1 / c1)) + WHERE ('-H') >= (c2::TEXT) COLLATE "C"; +SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass); + pg_get_indexdef +--------------------------------------------------------------------------------------------------------------- + CREATE UNIQUE INDEX concur_exprs_index_expr ON public.concur_exprs_tab USING btree (((c1)::text) COLLATE "C") +(1 row) + +SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass); + pg_get_indexdef +---------------------------------------------------------------------------------------------------------------------------------------------- + CREATE UNIQUE INDEX concur_exprs_index_pred ON public.concur_exprs_tab USING btree (c1) WHERE ((c1)::text > ((500000000)::text COLLATE "C")) +(1 row) + +SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass); + pg_get_indexdef +-------------------------------------------------------------------------------------------------------------------------------------------------- + CREATE UNIQUE INDEX concur_exprs_index_pred_2 ON public.concur_exprs_tab USING btree (((1 / c1))) WHERE ('-H'::text >= ((c2)::text COLLATE "C")) +(1 row) + +REINDEX TABLE CONCURRENTLY concur_exprs_tab; +SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass); + pg_get_indexdef +--------------------------------------------------------------------------------------------------------------- + CREATE UNIQUE INDEX concur_exprs_index_expr ON public.concur_exprs_tab USING btree (((c1)::text) COLLATE "C") +(1 row) + +SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass); + pg_get_indexdef +---------------------------------------------------------------------------------------------------------------------------------------------- + CREATE UNIQUE INDEX concur_exprs_index_pred ON public.concur_exprs_tab USING btree (c1) WHERE ((c1)::text > ((500000000)::text COLLATE "C")) +(1 row) + +SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass); + pg_get_indexdef +-------------------------------------------------------------------------------------------------------------------------------------------------- + CREATE UNIQUE INDEX concur_exprs_index_pred_2 ON public.concur_exprs_tab USING btree (((1 / c1))) WHERE ('-H'::text >= ((c2)::text COLLATE "C")) +(1 row) + +-- ALTER TABLE recreates the indexes, which should keep their collations. +ALTER TABLE concur_exprs_tab ALTER c2 TYPE TEXT; +SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass); + pg_get_indexdef +--------------------------------------------------------------------------------------------------------------- + CREATE UNIQUE INDEX concur_exprs_index_expr ON public.concur_exprs_tab USING btree (((c1)::text) COLLATE "C") +(1 row) + +SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass); + pg_get_indexdef +---------------------------------------------------------------------------------------------------------------------------------------------- + CREATE UNIQUE INDEX concur_exprs_index_pred ON public.concur_exprs_tab USING btree (c1) WHERE ((c1)::text > ((500000000)::text COLLATE "C")) +(1 row) + +SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass); + pg_get_indexdef +------------------------------------------------------------------------------------------------------------------------------------------ + CREATE UNIQUE INDEX concur_exprs_index_pred_2 ON public.concur_exprs_tab USING btree (((1 / c1))) WHERE ('-H'::text >= (c2 COLLATE "C")) +(1 row) + +DROP TABLE concur_exprs_tab; -- -- REINDEX SCHEMA -- diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 454d2dfaf563..25ef468767df 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -501,6 +501,42 @@ Partition of: partitioned2 FOR VALUES FROM ('-1', 'aaaaa') TO (100, 'ccccc') Partition constraint: (((a + 1) IS NOT NULL) AND (substr(b, 1, 5) IS NOT NULL) AND (((a + 1) > '-1'::integer) OR (((a + 1) = '-1'::integer) AND (substr(b, 1, 5) >= 'aaaaa'::text))) AND (((a + 1) < 100) OR (((a + 1) = 100) AND (substr(b, 1, 5) < 'ccccc'::text)))) DROP TABLE partitioned, partitioned2; +-- check that dependencies of partition columns are handled correctly +create domain intdom1 as int; +create table partitioned ( + a intdom1, + b text +) partition by range (a); +alter table partitioned drop column a; -- fail +ERROR: cannot drop column "a" because it is part of the partition key of relation "partitioned" +drop domain intdom1; -- fail, requires cascade +ERROR: cannot drop type intdom1 because other objects depend on it +DETAIL: table partitioned depends on type intdom1 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +drop domain intdom1 cascade; +NOTICE: drop cascades to table partitioned +table partitioned; -- gone +ERROR: relation "partitioned" does not exist +LINE 1: table partitioned; + ^ +-- likewise for columns used in partition expressions +create domain intdom1 as int; +create table partitioned ( + a intdom1, + b text +) partition by range (plusone(a)); +alter table partitioned drop column a; -- fail +ERROR: cannot drop column "a" because it is part of the partition key of relation "partitioned" +drop domain intdom1; -- fail, requires cascade +ERROR: cannot drop type intdom1 because other objects depend on it +DETAIL: table partitioned depends on type intdom1 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +drop domain intdom1 cascade; +NOTICE: drop cascades to table partitioned +table partitioned; -- gone +ERROR: relation "partitioned" does not exist +LINE 1: table partitioned; + ^ -- -- Partitions -- diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 719455b0ebb7..4f4deaec2231 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -1,5 +1,5 @@ -- --- expression evaluated tests that don't fit into a more specific file +-- expression evaluation tests that don't fit into a more specific file -- -- -- Tests for SQLVAlueFunction @@ -18,12 +18,24 @@ SELECT now()::timetz::text = current_time::text; t (1 row) +SELECT now()::timetz(4)::text = current_time(4)::text; + ?column? +---------- + t +(1 row) + SELECT now()::time::text = localtime::text; ?column? ---------- t (1 row) +SELECT now()::time(3)::text = localtime(3)::text; + ?column? +---------- + t +(1 row) + -- current_timestamp / localtimestamp (always matches because of transactional behaviour) SELECT current_timestamp = NOW(); ?column? @@ -75,3 +87,74 @@ SELECT current_schema; (1 row) RESET search_path; +-- +-- Tests for BETWEEN +-- +explain (costs off) +select count(*) from date_tbl + where f1 between '1997-01-01' and '1998-01-01'; + QUERY PLAN +----------------------------------------------------------------------------- + Aggregate + -> Seq Scan on date_tbl + Filter: ((f1 >= '01-01-1997'::date) AND (f1 <= '01-01-1998'::date)) +(3 rows) + +select count(*) from date_tbl + where f1 between '1997-01-01' and '1998-01-01'; + count +------- + 3 +(1 row) + +explain (costs off) +select count(*) from date_tbl + where f1 not between '1997-01-01' and '1998-01-01'; + QUERY PLAN +-------------------------------------------------------------------------- + Aggregate + -> Seq Scan on date_tbl + Filter: ((f1 < '01-01-1997'::date) OR (f1 > '01-01-1998'::date)) +(3 rows) + +select count(*) from date_tbl + where f1 not between '1997-01-01' and '1998-01-01'; + count +------- + 12 +(1 row) + +explain (costs off) +select count(*) from date_tbl + where f1 between symmetric '1997-01-01' and '1998-01-01'; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------- + Aggregate + -> Seq Scan on date_tbl + Filter: (((f1 >= '01-01-1997'::date) AND (f1 <= '01-01-1998'::date)) OR ((f1 >= '01-01-1998'::date) AND (f1 <= '01-01-1997'::date))) +(3 rows) + +select count(*) from date_tbl + where f1 between symmetric '1997-01-01' and '1998-01-01'; + count +------- + 3 +(1 row) + +explain (costs off) +select count(*) from date_tbl + where f1 not between symmetric '1997-01-01' and '1998-01-01'; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------- + Aggregate + -> Seq Scan on date_tbl + Filter: (((f1 < '01-01-1997'::date) OR (f1 > '01-01-1998'::date)) AND ((f1 < '01-01-1998'::date) OR (f1 > '01-01-1997'::date))) +(3 rows) + +select count(*) from date_tbl + where f1 not between symmetric '1997-01-01' and '1998-01-01'; + count +------- + 12 +(1 row) + diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 45b67c6b9c7d..7100e41be83c 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -2385,3 +2385,18 @@ DROP SCHEMA fkpart7 CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to table fkpart7.pkpart drop cascades to table fkpart7.fk +-- ensure we check partitions are "not used" when dropping constraints +CREATE SCHEMA fkpart8 + CREATE TABLE tbl1(f1 int PRIMARY KEY) + CREATE TABLE tbl2(f1 int REFERENCES tbl1 DEFERRABLE INITIALLY DEFERRED) PARTITION BY RANGE(f1) + CREATE TABLE tbl2_p1 PARTITION OF tbl2 FOR VALUES FROM (minvalue) TO (maxvalue); +INSERT INTO fkpart8.tbl1 VALUES(1); +BEGIN; +INSERT INTO fkpart8.tbl2 VALUES(1); +ALTER TABLE fkpart8.tbl2 DROP CONSTRAINT tbl2_f1_fkey; +ERROR: cannot ALTER TABLE "tbl2_p1" because it has pending trigger events +COMMIT; +DROP SCHEMA fkpart8 CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table fkpart8.tbl1 +drop cascades to table fkpart8.tbl2 diff --git a/src/test/regress/expected/geometry.out b/src/test/regress/expected/geometry.out index 0397081f4825..2a657a158dea 100644 --- a/src/test/regress/expected/geometry.out +++ b/src/test/regress/expected/geometry.out @@ -429,384 +429,384 @@ SELECT p1.f1, p2.f1, p1.f1 / p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p2.f1 ~ AND p2.f1[0] = 0; ERROR: division by zero -- Distance to line -SELECT p.f1, l.s, p.f1 <-> l.s FROM POINT_TBL p, LINE_TBL l; - f1 | s | ?column? --------------------+---------------------------------------+-------------------- - (0,0) | {0,-1,5} | 5 - (0,0) | {1,0,5} | 5 - (0,0) | {0,3,0} | 0 - (0,0) | {1,-1,0} | 0 - (0,0) | {-0.4,-1,-6} | 5.57086014531 - (0,0) | {-0.000184615384615,-1,15.3846153846} | 15.3846151224 - (0,0) | {3,NaN,5} | NaN - (0,0) | {NaN,NaN,NaN} | NaN - (0,0) | {0,-1,3} | 3 - (0,0) | {-1,0,3} | 3 - (-10,0) | {0,-1,5} | 5 - (-10,0) | {1,0,5} | 5 - (-10,0) | {0,3,0} | 0 - (-10,0) | {1,-1,0} | 7.07106781187 - (-10,0) | {-0.4,-1,-6} | 1.85695338177 - (-10,0) | {-0.000184615384615,-1,15.3846153846} | 15.3864612763 - (-10,0) | {3,NaN,5} | NaN - (-10,0) | {NaN,NaN,NaN} | NaN - (-10,0) | {0,-1,3} | 3 - (-10,0) | {-1,0,3} | 13 - (-3,4) | {0,-1,5} | 1 - (-3,4) | {1,0,5} | 2 - (-3,4) | {0,3,0} | 4 - (-3,4) | {1,-1,0} | 4.94974746831 - (-3,4) | {-0.4,-1,-6} | 8.17059487979 - (-3,4) | {-0.000184615384615,-1,15.3846153846} | 11.3851690368 - (-3,4) | {3,NaN,5} | NaN - (-3,4) | {NaN,NaN,NaN} | NaN - (-3,4) | {0,-1,3} | 1 - (-3,4) | {-1,0,3} | 6 - (5.1,34.5) | {0,-1,5} | 29.5 - (5.1,34.5) | {1,0,5} | 10.1 - (5.1,34.5) | {0,3,0} | 34.5 - (5.1,34.5) | {1,-1,0} | 20.7889393669 - (5.1,34.5) | {-0.4,-1,-6} | 39.4973984303 - (5.1,34.5) | {-0.000184615384615,-1,15.3846153846} | 19.1163258281 - (5.1,34.5) | {3,NaN,5} | NaN - (5.1,34.5) | {NaN,NaN,NaN} | NaN - (5.1,34.5) | {0,-1,3} | 31.5 - (5.1,34.5) | {-1,0,3} | 2.1 - (-5,-12) | {0,-1,5} | 17 - (-5,-12) | {1,0,5} | 0 - (-5,-12) | {0,3,0} | 12 - (-5,-12) | {1,-1,0} | 4.94974746831 - (-5,-12) | {-0.4,-1,-6} | 7.42781352708 - (-5,-12) | {-0.000184615384615,-1,15.3846153846} | 27.3855379948 - (-5,-12) | {3,NaN,5} | NaN - (-5,-12) | {NaN,NaN,NaN} | NaN - (-5,-12) | {0,-1,3} | 15 - (-5,-12) | {-1,0,3} | 8 - (1e-300,-1e-300) | {0,-1,5} | 5 - (1e-300,-1e-300) | {1,0,5} | 5 - (1e-300,-1e-300) | {0,3,0} | 1e-300 - (1e-300,-1e-300) | {1,-1,0} | 1.41421356237e-300 - (1e-300,-1e-300) | {-0.4,-1,-6} | 5.57086014531 - (1e-300,-1e-300) | {-0.000184615384615,-1,15.3846153846} | 15.3846151224 - (1e-300,-1e-300) | {3,NaN,5} | NaN - (1e-300,-1e-300) | {NaN,NaN,NaN} | NaN - (1e-300,-1e-300) | {0,-1,3} | 3 - (1e-300,-1e-300) | {-1,0,3} | 3 - (1e+300,Infinity) | {0,-1,5} | Infinity - (1e+300,Infinity) | {1,0,5} | NaN - (1e+300,Infinity) | {0,3,0} | Infinity - (1e+300,Infinity) | {1,-1,0} | Infinity - (1e+300,Infinity) | {-0.4,-1,-6} | Infinity - (1e+300,Infinity) | {-0.000184615384615,-1,15.3846153846} | Infinity - (1e+300,Infinity) | {3,NaN,5} | NaN - (1e+300,Infinity) | {NaN,NaN,NaN} | NaN - (1e+300,Infinity) | {0,-1,3} | Infinity - (1e+300,Infinity) | {-1,0,3} | NaN - (NaN,NaN) | {0,-1,5} | NaN - (NaN,NaN) | {1,0,5} | NaN - (NaN,NaN) | {0,3,0} | NaN - (NaN,NaN) | {1,-1,0} | NaN - (NaN,NaN) | {-0.4,-1,-6} | NaN - (NaN,NaN) | {-0.000184615384615,-1,15.3846153846} | NaN - (NaN,NaN) | {3,NaN,5} | NaN - (NaN,NaN) | {NaN,NaN,NaN} | NaN - (NaN,NaN) | {0,-1,3} | NaN - (NaN,NaN) | {-1,0,3} | NaN - (10,10) | {0,-1,5} | 5 - (10,10) | {1,0,5} | 15 - (10,10) | {0,3,0} | 10 - (10,10) | {1,-1,0} | 0 - (10,10) | {-0.4,-1,-6} | 18.5695338177 - (10,10) | {-0.000184615384615,-1,15.3846153846} | 5.38276913903 - (10,10) | {3,NaN,5} | NaN - (10,10) | {NaN,NaN,NaN} | NaN - (10,10) | {0,-1,3} | 7 - (10,10) | {-1,0,3} | 7 +SELECT p.f1, l.s, p.f1 <-> l.s AS dist_pl, l.s <-> p.f1 AS dist_lp FROM POINT_TBL p, LINE_TBL l; + f1 | s | dist_pl | dist_lp +-------------------+---------------------------------------+--------------------+-------------------- + (0,0) | {0,-1,5} | 5 | 5 + (0,0) | {1,0,5} | 5 | 5 + (0,0) | {0,3,0} | 0 | 0 + (0,0) | {1,-1,0} | 0 | 0 + (0,0) | {-0.4,-1,-6} | 5.57086014531 | 5.57086014531 + (0,0) | {-0.000184615384615,-1,15.3846153846} | 15.3846151224 | 15.3846151224 + (0,0) | {3,NaN,5} | NaN | NaN + (0,0) | {NaN,NaN,NaN} | NaN | NaN + (0,0) | {0,-1,3} | 3 | 3 + (0,0) | {-1,0,3} | 3 | 3 + (-10,0) | {0,-1,5} | 5 | 5 + (-10,0) | {1,0,5} | 5 | 5 + (-10,0) | {0,3,0} | 0 | 0 + (-10,0) | {1,-1,0} | 7.07106781187 | 7.07106781187 + (-10,0) | {-0.4,-1,-6} | 1.85695338177 | 1.85695338177 + (-10,0) | {-0.000184615384615,-1,15.3846153846} | 15.3864612763 | 15.3864612763 + (-10,0) | {3,NaN,5} | NaN | NaN + (-10,0) | {NaN,NaN,NaN} | NaN | NaN + (-10,0) | {0,-1,3} | 3 | 3 + (-10,0) | {-1,0,3} | 13 | 13 + (-3,4) | {0,-1,5} | 1 | 1 + (-3,4) | {1,0,5} | 2 | 2 + (-3,4) | {0,3,0} | 4 | 4 + (-3,4) | {1,-1,0} | 4.94974746831 | 4.94974746831 + (-3,4) | {-0.4,-1,-6} | 8.17059487979 | 8.17059487979 + (-3,4) | {-0.000184615384615,-1,15.3846153846} | 11.3851690368 | 11.3851690368 + (-3,4) | {3,NaN,5} | NaN | NaN + (-3,4) | {NaN,NaN,NaN} | NaN | NaN + (-3,4) | {0,-1,3} | 1 | 1 + (-3,4) | {-1,0,3} | 6 | 6 + (5.1,34.5) | {0,-1,5} | 29.5 | 29.5 + (5.1,34.5) | {1,0,5} | 10.1 | 10.1 + (5.1,34.5) | {0,3,0} | 34.5 | 34.5 + (5.1,34.5) | {1,-1,0} | 20.7889393669 | 20.7889393669 + (5.1,34.5) | {-0.4,-1,-6} | 39.4973984303 | 39.4973984303 + (5.1,34.5) | {-0.000184615384615,-1,15.3846153846} | 19.1163258281 | 19.1163258281 + (5.1,34.5) | {3,NaN,5} | NaN | NaN + (5.1,34.5) | {NaN,NaN,NaN} | NaN | NaN + (5.1,34.5) | {0,-1,3} | 31.5 | 31.5 + (5.1,34.5) | {-1,0,3} | 2.1 | 2.1 + (-5,-12) | {0,-1,5} | 17 | 17 + (-5,-12) | {1,0,5} | 0 | 0 + (-5,-12) | {0,3,0} | 12 | 12 + (-5,-12) | {1,-1,0} | 4.94974746831 | 4.94974746831 + (-5,-12) | {-0.4,-1,-6} | 7.42781352708 | 7.42781352708 + (-5,-12) | {-0.000184615384615,-1,15.3846153846} | 27.3855379948 | 27.3855379948 + (-5,-12) | {3,NaN,5} | NaN | NaN + (-5,-12) | {NaN,NaN,NaN} | NaN | NaN + (-5,-12) | {0,-1,3} | 15 | 15 + (-5,-12) | {-1,0,3} | 8 | 8 + (1e-300,-1e-300) | {0,-1,5} | 5 | 5 + (1e-300,-1e-300) | {1,0,5} | 5 | 5 + (1e-300,-1e-300) | {0,3,0} | 1e-300 | 1e-300 + (1e-300,-1e-300) | {1,-1,0} | 1.41421356237e-300 | 1.41421356237e-300 + (1e-300,-1e-300) | {-0.4,-1,-6} | 5.57086014531 | 5.57086014531 + (1e-300,-1e-300) | {-0.000184615384615,-1,15.3846153846} | 15.3846151224 | 15.3846151224 + (1e-300,-1e-300) | {3,NaN,5} | NaN | NaN + (1e-300,-1e-300) | {NaN,NaN,NaN} | NaN | NaN + (1e-300,-1e-300) | {0,-1,3} | 3 | 3 + (1e-300,-1e-300) | {-1,0,3} | 3 | 3 + (1e+300,Infinity) | {0,-1,5} | Infinity | Infinity + (1e+300,Infinity) | {1,0,5} | NaN | NaN + (1e+300,Infinity) | {0,3,0} | Infinity | Infinity + (1e+300,Infinity) | {1,-1,0} | Infinity | Infinity + (1e+300,Infinity) | {-0.4,-1,-6} | Infinity | Infinity + (1e+300,Infinity) | {-0.000184615384615,-1,15.3846153846} | Infinity | Infinity + (1e+300,Infinity) | {3,NaN,5} | NaN | NaN + (1e+300,Infinity) | {NaN,NaN,NaN} | NaN | NaN + (1e+300,Infinity) | {0,-1,3} | Infinity | Infinity + (1e+300,Infinity) | {-1,0,3} | NaN | NaN + (NaN,NaN) | {0,-1,5} | NaN | NaN + (NaN,NaN) | {1,0,5} | NaN | NaN + (NaN,NaN) | {0,3,0} | NaN | NaN + (NaN,NaN) | {1,-1,0} | NaN | NaN + (NaN,NaN) | {-0.4,-1,-6} | NaN | NaN + (NaN,NaN) | {-0.000184615384615,-1,15.3846153846} | NaN | NaN + (NaN,NaN) | {3,NaN,5} | NaN | NaN + (NaN,NaN) | {NaN,NaN,NaN} | NaN | NaN + (NaN,NaN) | {0,-1,3} | NaN | NaN + (NaN,NaN) | {-1,0,3} | NaN | NaN + (10,10) | {0,-1,5} | 5 | 5 + (10,10) | {1,0,5} | 15 | 15 + (10,10) | {0,3,0} | 10 | 10 + (10,10) | {1,-1,0} | 0 | 0 + (10,10) | {-0.4,-1,-6} | 18.5695338177 | 18.5695338177 + (10,10) | {-0.000184615384615,-1,15.3846153846} | 5.38276913903 | 5.38276913903 + (10,10) | {3,NaN,5} | NaN | NaN + (10,10) | {NaN,NaN,NaN} | NaN | NaN + (10,10) | {0,-1,3} | 7 | 7 + (10,10) | {-1,0,3} | 7 | 7 (90 rows) -- Distance to line segment -SELECT p.f1, l.s, p.f1 <-> l.s FROM POINT_TBL p, LSEG_TBL l; - f1 | s | ?column? --------------------+-------------------------------+-------------------- - (0,0) | [(1,2),(3,4)] | 2.2360679775 - (0,0) | [(0,0),(6,6)] | 0 - (0,0) | [(10,-10),(-3,-4)] | 4.88901207039 - (0,0) | [(-1000000,200),(300000,-40)] | 15.3846151224 - (0,0) | [(11,22),(33,44)] | 24.5967477525 - (0,0) | [(-10,2),(-10,3)] | 10.1980390272 - (0,0) | [(0,-20),(30,-20)] | 20 - (0,0) | [(NaN,1),(NaN,90)] | NaN - (-10,0) | [(1,2),(3,4)] | 11.1803398875 - (-10,0) | [(0,0),(6,6)] | 10 - (-10,0) | [(10,-10),(-3,-4)] | 8.0622577483 - (-10,0) | [(-1000000,200),(300000,-40)] | 15.3864612763 - (-10,0) | [(11,22),(33,44)] | 30.4138126515 - (-10,0) | [(-10,2),(-10,3)] | 2 - (-10,0) | [(0,-20),(30,-20)] | 22.360679775 - (-10,0) | [(NaN,1),(NaN,90)] | NaN - (-3,4) | [(1,2),(3,4)] | 4.472135955 - (-3,4) | [(0,0),(6,6)] | 4.94974746831 - (-3,4) | [(10,-10),(-3,-4)] | 8 - (-3,4) | [(-1000000,200),(300000,-40)] | 11.3851690367 - (-3,4) | [(11,22),(33,44)] | 22.803508502 - (-3,4) | [(-10,2),(-10,3)] | 7.07106781187 - (-3,4) | [(0,-20),(30,-20)] | 24.1867732449 - (-3,4) | [(NaN,1),(NaN,90)] | NaN - (5.1,34.5) | [(1,2),(3,4)] | 30.5722096028 - (5.1,34.5) | [(0,0),(6,6)] | 28.5142069853 - (5.1,34.5) | [(10,-10),(-3,-4)] | 39.3428519556 - (5.1,34.5) | [(-1000000,200),(300000,-40)] | 19.1163258281 - (5.1,34.5) | [(11,22),(33,44)] | 13.0107647738 - (5.1,34.5) | [(-10,2),(-10,3)] | 34.932220084 - (5.1,34.5) | [(0,-20),(30,-20)] | 54.5 - (5.1,34.5) | [(NaN,1),(NaN,90)] | NaN - (-5,-12) | [(1,2),(3,4)] | 15.2315462117 - (-5,-12) | [(0,0),(6,6)] | 13 - (-5,-12) | [(10,-10),(-3,-4)] | 8.10179143093 - (-5,-12) | [(-1000000,200),(300000,-40)] | 27.3855379949 - (-5,-12) | [(11,22),(33,44)] | 37.5765884561 - (-5,-12) | [(-10,2),(-10,3)] | 14.8660687473 - (-5,-12) | [(0,-20),(30,-20)] | 9.43398113206 - (-5,-12) | [(NaN,1),(NaN,90)] | NaN - (1e-300,-1e-300) | [(1,2),(3,4)] | 2.2360679775 - (1e-300,-1e-300) | [(0,0),(6,6)] | 1.41421356237e-300 - (1e-300,-1e-300) | [(10,-10),(-3,-4)] | 4.88901207039 - (1e-300,-1e-300) | [(-1000000,200),(300000,-40)] | 15.3846151224 - (1e-300,-1e-300) | [(11,22),(33,44)] | 24.5967477525 - (1e-300,-1e-300) | [(-10,2),(-10,3)] | 10.1980390272 - (1e-300,-1e-300) | [(0,-20),(30,-20)] | 20 - (1e-300,-1e-300) | [(NaN,1),(NaN,90)] | NaN - (1e+300,Infinity) | [(1,2),(3,4)] | Infinity - (1e+300,Infinity) | [(0,0),(6,6)] | Infinity - (1e+300,Infinity) | [(10,-10),(-3,-4)] | Infinity - (1e+300,Infinity) | [(-1000000,200),(300000,-40)] | Infinity - (1e+300,Infinity) | [(11,22),(33,44)] | Infinity - (1e+300,Infinity) | [(-10,2),(-10,3)] | Infinity - (1e+300,Infinity) | [(0,-20),(30,-20)] | Infinity - (1e+300,Infinity) | [(NaN,1),(NaN,90)] | Infinity - (NaN,NaN) | [(1,2),(3,4)] | NaN - (NaN,NaN) | [(0,0),(6,6)] | NaN - (NaN,NaN) | [(10,-10),(-3,-4)] | NaN - (NaN,NaN) | [(-1000000,200),(300000,-40)] | NaN - (NaN,NaN) | [(11,22),(33,44)] | NaN - (NaN,NaN) | [(-10,2),(-10,3)] | NaN - (NaN,NaN) | [(0,-20),(30,-20)] | NaN - (NaN,NaN) | [(NaN,1),(NaN,90)] | NaN - (10,10) | [(1,2),(3,4)] | 9.21954445729 - (10,10) | [(0,0),(6,6)] | 5.65685424949 - (10,10) | [(10,-10),(-3,-4)] | 18.15918769 - (10,10) | [(-1000000,200),(300000,-40)] | 5.38276913904 - (10,10) | [(11,22),(33,44)] | 12.0415945788 - (10,10) | [(-10,2),(-10,3)] | 21.1896201004 - (10,10) | [(0,-20),(30,-20)] | 30 - (10,10) | [(NaN,1),(NaN,90)] | NaN +SELECT p.f1, l.s, p.f1 <-> l.s AS dist_ps, l.s <-> p.f1 AS dist_sp FROM POINT_TBL p, LSEG_TBL l; + f1 | s | dist_ps | dist_sp +-------------------+-------------------------------+--------------------+-------------------- + (0,0) | [(1,2),(3,4)] | 2.2360679775 | 2.2360679775 + (0,0) | [(0,0),(6,6)] | 0 | 0 + (0,0) | [(10,-10),(-3,-4)] | 4.88901207039 | 4.88901207039 + (0,0) | [(-1000000,200),(300000,-40)] | 15.3846151224 | 15.3846151224 + (0,0) | [(11,22),(33,44)] | 24.5967477525 | 24.5967477525 + (0,0) | [(-10,2),(-10,3)] | 10.1980390272 | 10.1980390272 + (0,0) | [(0,-20),(30,-20)] | 20 | 20 + (0,0) | [(NaN,1),(NaN,90)] | NaN | NaN + (-10,0) | [(1,2),(3,4)] | 11.1803398875 | 11.1803398875 + (-10,0) | [(0,0),(6,6)] | 10 | 10 + (-10,0) | [(10,-10),(-3,-4)] | 8.0622577483 | 8.0622577483 + (-10,0) | [(-1000000,200),(300000,-40)] | 15.3864612763 | 15.3864612763 + (-10,0) | [(11,22),(33,44)] | 30.4138126515 | 30.4138126515 + (-10,0) | [(-10,2),(-10,3)] | 2 | 2 + (-10,0) | [(0,-20),(30,-20)] | 22.360679775 | 22.360679775 + (-10,0) | [(NaN,1),(NaN,90)] | NaN | NaN + (-3,4) | [(1,2),(3,4)] | 4.472135955 | 4.472135955 + (-3,4) | [(0,0),(6,6)] | 4.94974746831 | 4.94974746831 + (-3,4) | [(10,-10),(-3,-4)] | 8 | 8 + (-3,4) | [(-1000000,200),(300000,-40)] | 11.3851690367 | 11.3851690367 + (-3,4) | [(11,22),(33,44)] | 22.803508502 | 22.803508502 + (-3,4) | [(-10,2),(-10,3)] | 7.07106781187 | 7.07106781187 + (-3,4) | [(0,-20),(30,-20)] | 24.1867732449 | 24.1867732449 + (-3,4) | [(NaN,1),(NaN,90)] | NaN | NaN + (5.1,34.5) | [(1,2),(3,4)] | 30.5722096028 | 30.5722096028 + (5.1,34.5) | [(0,0),(6,6)] | 28.5142069853 | 28.5142069853 + (5.1,34.5) | [(10,-10),(-3,-4)] | 39.3428519556 | 39.3428519556 + (5.1,34.5) | [(-1000000,200),(300000,-40)] | 19.1163258281 | 19.1163258281 + (5.1,34.5) | [(11,22),(33,44)] | 13.0107647738 | 13.0107647738 + (5.1,34.5) | [(-10,2),(-10,3)] | 34.932220084 | 34.932220084 + (5.1,34.5) | [(0,-20),(30,-20)] | 54.5 | 54.5 + (5.1,34.5) | [(NaN,1),(NaN,90)] | NaN | NaN + (-5,-12) | [(1,2),(3,4)] | 15.2315462117 | 15.2315462117 + (-5,-12) | [(0,0),(6,6)] | 13 | 13 + (-5,-12) | [(10,-10),(-3,-4)] | 8.10179143093 | 8.10179143093 + (-5,-12) | [(-1000000,200),(300000,-40)] | 27.3855379949 | 27.3855379949 + (-5,-12) | [(11,22),(33,44)] | 37.5765884561 | 37.5765884561 + (-5,-12) | [(-10,2),(-10,3)] | 14.8660687473 | 14.8660687473 + (-5,-12) | [(0,-20),(30,-20)] | 9.43398113206 | 9.43398113206 + (-5,-12) | [(NaN,1),(NaN,90)] | NaN | NaN + (1e-300,-1e-300) | [(1,2),(3,4)] | 2.2360679775 | 2.2360679775 + (1e-300,-1e-300) | [(0,0),(6,6)] | 1.41421356237e-300 | 1.41421356237e-300 + (1e-300,-1e-300) | [(10,-10),(-3,-4)] | 4.88901207039 | 4.88901207039 + (1e-300,-1e-300) | [(-1000000,200),(300000,-40)] | 15.3846151224 | 15.3846151224 + (1e-300,-1e-300) | [(11,22),(33,44)] | 24.5967477525 | 24.5967477525 + (1e-300,-1e-300) | [(-10,2),(-10,3)] | 10.1980390272 | 10.1980390272 + (1e-300,-1e-300) | [(0,-20),(30,-20)] | 20 | 20 + (1e-300,-1e-300) | [(NaN,1),(NaN,90)] | NaN | NaN + (1e+300,Infinity) | [(1,2),(3,4)] | Infinity | Infinity + (1e+300,Infinity) | [(0,0),(6,6)] | Infinity | Infinity + (1e+300,Infinity) | [(10,-10),(-3,-4)] | Infinity | Infinity + (1e+300,Infinity) | [(-1000000,200),(300000,-40)] | Infinity | Infinity + (1e+300,Infinity) | [(11,22),(33,44)] | Infinity | Infinity + (1e+300,Infinity) | [(-10,2),(-10,3)] | Infinity | Infinity + (1e+300,Infinity) | [(0,-20),(30,-20)] | Infinity | Infinity + (1e+300,Infinity) | [(NaN,1),(NaN,90)] | Infinity | Infinity + (NaN,NaN) | [(1,2),(3,4)] | NaN | NaN + (NaN,NaN) | [(0,0),(6,6)] | NaN | NaN + (NaN,NaN) | [(10,-10),(-3,-4)] | NaN | NaN + (NaN,NaN) | [(-1000000,200),(300000,-40)] | NaN | NaN + (NaN,NaN) | [(11,22),(33,44)] | NaN | NaN + (NaN,NaN) | [(-10,2),(-10,3)] | NaN | NaN + (NaN,NaN) | [(0,-20),(30,-20)] | NaN | NaN + (NaN,NaN) | [(NaN,1),(NaN,90)] | NaN | NaN + (10,10) | [(1,2),(3,4)] | 9.21954445729 | 9.21954445729 + (10,10) | [(0,0),(6,6)] | 5.65685424949 | 5.65685424949 + (10,10) | [(10,-10),(-3,-4)] | 18.15918769 | 18.15918769 + (10,10) | [(-1000000,200),(300000,-40)] | 5.38276913904 | 5.38276913904 + (10,10) | [(11,22),(33,44)] | 12.0415945788 | 12.0415945788 + (10,10) | [(-10,2),(-10,3)] | 21.1896201004 | 21.1896201004 + (10,10) | [(0,-20),(30,-20)] | 30 | 30 + (10,10) | [(NaN,1),(NaN,90)] | NaN | NaN (72 rows) -- Distance to box -SELECT p.f1, b.f1, p.f1 <-> b.f1 FROM POINT_TBL p, BOX_TBL b; - f1 | f1 | ?column? --------------------+---------------------+-------------------- - (0,0) | (2,2),(0,0) | 0 - (0,0) | (3,3),(1,1) | 1.41421356237 - (0,0) | (-2,2),(-8,-10) | 2 - (0,0) | (2.5,3.5),(2.5,2.5) | 3.53553390593 - (0,0) | (3,3),(3,3) | 4.24264068712 - (-10,0) | (2,2),(0,0) | 10 - (-10,0) | (3,3),(1,1) | 11.0453610172 - (-10,0) | (-2,2),(-8,-10) | 2 - (-10,0) | (2.5,3.5),(2.5,2.5) | 12.747548784 - (-10,0) | (3,3),(3,3) | 13.3416640641 - (-3,4) | (2,2),(0,0) | 3.60555127546 - (-3,4) | (3,3),(1,1) | 4.12310562562 - (-3,4) | (-2,2),(-8,-10) | 2 - (-3,4) | (2.5,3.5),(2.5,2.5) | 5.52268050859 - (-3,4) | (3,3),(3,3) | 6.0827625303 - (5.1,34.5) | (2,2),(0,0) | 32.6475113906 - (5.1,34.5) | (3,3),(1,1) | 31.5699223946 - (5.1,34.5) | (-2,2),(-8,-10) | 33.2664996656 - (5.1,34.5) | (2.5,3.5),(2.5,2.5) | 31.108841187 - (5.1,34.5) | (3,3),(3,3) | 31.5699223946 - (-5,-12) | (2,2),(0,0) | 13 - (-5,-12) | (3,3),(1,1) | 14.3178210633 - (-5,-12) | (-2,2),(-8,-10) | 2 - (-5,-12) | (2.5,3.5),(2.5,2.5) | 16.3248277173 - (-5,-12) | (3,3),(3,3) | 17 - (1e-300,-1e-300) | (2,2),(0,0) | 1.41421356237e-300 - (1e-300,-1e-300) | (3,3),(1,1) | 1.41421356237 - (1e-300,-1e-300) | (-2,2),(-8,-10) | 2 - (1e-300,-1e-300) | (2.5,3.5),(2.5,2.5) | 3.53553390593 - (1e-300,-1e-300) | (3,3),(3,3) | 4.24264068712 - (1e+300,Infinity) | (2,2),(0,0) | Infinity - (1e+300,Infinity) | (3,3),(1,1) | Infinity - (1e+300,Infinity) | (-2,2),(-8,-10) | Infinity - (1e+300,Infinity) | (2.5,3.5),(2.5,2.5) | Infinity - (1e+300,Infinity) | (3,3),(3,3) | Infinity - (NaN,NaN) | (2,2),(0,0) | NaN - (NaN,NaN) | (3,3),(1,1) | NaN - (NaN,NaN) | (-2,2),(-8,-10) | NaN - (NaN,NaN) | (2.5,3.5),(2.5,2.5) | NaN - (NaN,NaN) | (3,3),(3,3) | NaN - (10,10) | (2,2),(0,0) | 11.313708499 - (10,10) | (3,3),(1,1) | 9.89949493661 - (10,10) | (-2,2),(-8,-10) | 14.4222051019 - (10,10) | (2.5,3.5),(2.5,2.5) | 9.92471662064 - (10,10) | (3,3),(3,3) | 9.89949493661 +SELECT p.f1, b.f1, p.f1 <-> b.f1 AS dist_pb, b.f1 <-> p.f1 AS dist_bp FROM POINT_TBL p, BOX_TBL b; + f1 | f1 | dist_pb | dist_bp +-------------------+---------------------+--------------------+-------------------- + (0,0) | (2,2),(0,0) | 0 | 0 + (0,0) | (3,3),(1,1) | 1.41421356237 | 1.41421356237 + (0,0) | (-2,2),(-8,-10) | 2 | 2 + (0,0) | (2.5,3.5),(2.5,2.5) | 3.53553390593 | 3.53553390593 + (0,0) | (3,3),(3,3) | 4.24264068712 | 4.24264068712 + (-10,0) | (2,2),(0,0) | 10 | 10 + (-10,0) | (3,3),(1,1) | 11.0453610172 | 11.0453610172 + (-10,0) | (-2,2),(-8,-10) | 2 | 2 + (-10,0) | (2.5,3.5),(2.5,2.5) | 12.747548784 | 12.747548784 + (-10,0) | (3,3),(3,3) | 13.3416640641 | 13.3416640641 + (-3,4) | (2,2),(0,0) | 3.60555127546 | 3.60555127546 + (-3,4) | (3,3),(1,1) | 4.12310562562 | 4.12310562562 + (-3,4) | (-2,2),(-8,-10) | 2 | 2 + (-3,4) | (2.5,3.5),(2.5,2.5) | 5.52268050859 | 5.52268050859 + (-3,4) | (3,3),(3,3) | 6.0827625303 | 6.0827625303 + (5.1,34.5) | (2,2),(0,0) | 32.6475113906 | 32.6475113906 + (5.1,34.5) | (3,3),(1,1) | 31.5699223946 | 31.5699223946 + (5.1,34.5) | (-2,2),(-8,-10) | 33.2664996656 | 33.2664996656 + (5.1,34.5) | (2.5,3.5),(2.5,2.5) | 31.108841187 | 31.108841187 + (5.1,34.5) | (3,3),(3,3) | 31.5699223946 | 31.5699223946 + (-5,-12) | (2,2),(0,0) | 13 | 13 + (-5,-12) | (3,3),(1,1) | 14.3178210633 | 14.3178210633 + (-5,-12) | (-2,2),(-8,-10) | 2 | 2 + (-5,-12) | (2.5,3.5),(2.5,2.5) | 16.3248277173 | 16.3248277173 + (-5,-12) | (3,3),(3,3) | 17 | 17 + (1e-300,-1e-300) | (2,2),(0,0) | 1.41421356237e-300 | 1.41421356237e-300 + (1e-300,-1e-300) | (3,3),(1,1) | 1.41421356237 | 1.41421356237 + (1e-300,-1e-300) | (-2,2),(-8,-10) | 2 | 2 + (1e-300,-1e-300) | (2.5,3.5),(2.5,2.5) | 3.53553390593 | 3.53553390593 + (1e-300,-1e-300) | (3,3),(3,3) | 4.24264068712 | 4.24264068712 + (1e+300,Infinity) | (2,2),(0,0) | Infinity | Infinity + (1e+300,Infinity) | (3,3),(1,1) | Infinity | Infinity + (1e+300,Infinity) | (-2,2),(-8,-10) | Infinity | Infinity + (1e+300,Infinity) | (2.5,3.5),(2.5,2.5) | Infinity | Infinity + (1e+300,Infinity) | (3,3),(3,3) | Infinity | Infinity + (NaN,NaN) | (2,2),(0,0) | NaN | NaN + (NaN,NaN) | (3,3),(1,1) | NaN | NaN + (NaN,NaN) | (-2,2),(-8,-10) | NaN | NaN + (NaN,NaN) | (2.5,3.5),(2.5,2.5) | NaN | NaN + (NaN,NaN) | (3,3),(3,3) | NaN | NaN + (10,10) | (2,2),(0,0) | 11.313708499 | 11.313708499 + (10,10) | (3,3),(1,1) | 9.89949493661 | 9.89949493661 + (10,10) | (-2,2),(-8,-10) | 14.4222051019 | 14.4222051019 + (10,10) | (2.5,3.5),(2.5,2.5) | 9.92471662064 | 9.92471662064 + (10,10) | (3,3),(3,3) | 9.89949493661 | 9.89949493661 (45 rows) -- Distance to path -SELECT p.f1, p1.f1, p.f1 <-> p1.f1 FROM POINT_TBL p, PATH_TBL p1; - f1 | f1 | ?column? --------------------+---------------------------+-------------------- - (0,0) | [(1,2),(3,4)] | 2.2360679775 - (0,0) | ((1,2),(3,4)) | 2.2360679775 - (0,0) | [(0,0),(3,0),(4,5),(1,6)] | 0 - (0,0) | ((1,2),(3,4)) | 2.2360679775 - (0,0) | ((1,2),(3,4)) | 2.2360679775 - (0,0) | [(1,2),(3,4)] | 2.2360679775 - (0,0) | ((10,20)) | 22.360679775 - (0,0) | [(11,12),(13,14)] | 16.2788205961 - (0,0) | ((11,12),(13,14)) | 16.2788205961 - (-10,0) | [(1,2),(3,4)] | 11.1803398875 - (-10,0) | ((1,2),(3,4)) | 11.1803398875 - (-10,0) | [(0,0),(3,0),(4,5),(1,6)] | 10 - (-10,0) | ((1,2),(3,4)) | 11.1803398875 - (-10,0) | ((1,2),(3,4)) | 11.1803398875 - (-10,0) | [(1,2),(3,4)] | 11.1803398875 - (-10,0) | ((10,20)) | 28.2842712475 - (-10,0) | [(11,12),(13,14)] | 24.1867732449 - (-10,0) | ((11,12),(13,14)) | 24.1867732449 - (-3,4) | [(1,2),(3,4)] | 4.472135955 - (-3,4) | ((1,2),(3,4)) | 4.472135955 - (-3,4) | [(0,0),(3,0),(4,5),(1,6)] | 4.472135955 - (-3,4) | ((1,2),(3,4)) | 4.472135955 - (-3,4) | ((1,2),(3,4)) | 4.472135955 - (-3,4) | [(1,2),(3,4)] | 4.472135955 - (-3,4) | ((10,20)) | 20.6155281281 - (-3,4) | [(11,12),(13,14)] | 16.1245154966 - (-3,4) | ((11,12),(13,14)) | 16.1245154966 - (5.1,34.5) | [(1,2),(3,4)] | 30.5722096028 - (5.1,34.5) | ((1,2),(3,4)) | 30.5722096028 - (5.1,34.5) | [(0,0),(3,0),(4,5),(1,6)] | 28.793402022 - (5.1,34.5) | ((1,2),(3,4)) | 30.5722096028 - (5.1,34.5) | ((1,2),(3,4)) | 30.5722096028 - (5.1,34.5) | [(1,2),(3,4)] | 30.5722096028 - (5.1,34.5) | ((10,20)) | 15.3055545473 - (5.1,34.5) | [(11,12),(13,14)] | 21.9695243462 - (5.1,34.5) | ((11,12),(13,14)) | 21.9695243462 - (-5,-12) | [(1,2),(3,4)] | 15.2315462117 - (-5,-12) | ((1,2),(3,4)) | 15.2315462117 - (-5,-12) | [(0,0),(3,0),(4,5),(1,6)] | 13 - (-5,-12) | ((1,2),(3,4)) | 15.2315462117 - (-5,-12) | ((1,2),(3,4)) | 15.2315462117 - (-5,-12) | [(1,2),(3,4)] | 15.2315462117 - (-5,-12) | ((10,20)) | 35.3411940941 - (-5,-12) | [(11,12),(13,14)] | 28.8444102037 - (-5,-12) | ((11,12),(13,14)) | 28.8444102037 - (1e-300,-1e-300) | [(1,2),(3,4)] | 2.2360679775 - (1e-300,-1e-300) | ((1,2),(3,4)) | 2.2360679775 - (1e-300,-1e-300) | [(0,0),(3,0),(4,5),(1,6)] | 1.41421356237e-300 - (1e-300,-1e-300) | ((1,2),(3,4)) | 2.2360679775 - (1e-300,-1e-300) | ((1,2),(3,4)) | 2.2360679775 - (1e-300,-1e-300) | [(1,2),(3,4)] | 2.2360679775 - (1e-300,-1e-300) | ((10,20)) | 22.360679775 - (1e-300,-1e-300) | [(11,12),(13,14)] | 16.2788205961 - (1e-300,-1e-300) | ((11,12),(13,14)) | 16.2788205961 - (1e+300,Infinity) | [(1,2),(3,4)] | Infinity - (1e+300,Infinity) | ((1,2),(3,4)) | Infinity - (1e+300,Infinity) | [(0,0),(3,0),(4,5),(1,6)] | Infinity - (1e+300,Infinity) | ((1,2),(3,4)) | Infinity - (1e+300,Infinity) | ((1,2),(3,4)) | Infinity - (1e+300,Infinity) | [(1,2),(3,4)] | Infinity - (1e+300,Infinity) | ((10,20)) | Infinity - (1e+300,Infinity) | [(11,12),(13,14)] | Infinity - (1e+300,Infinity) | ((11,12),(13,14)) | Infinity - (NaN,NaN) | [(1,2),(3,4)] | NaN - (NaN,NaN) | ((1,2),(3,4)) | NaN - (NaN,NaN) | [(0,0),(3,0),(4,5),(1,6)] | NaN - (NaN,NaN) | ((1,2),(3,4)) | NaN - (NaN,NaN) | ((1,2),(3,4)) | NaN - (NaN,NaN) | [(1,2),(3,4)] | NaN - (NaN,NaN) | ((10,20)) | NaN - (NaN,NaN) | [(11,12),(13,14)] | NaN - (NaN,NaN) | ((11,12),(13,14)) | NaN - (10,10) | [(1,2),(3,4)] | 9.21954445729 - (10,10) | ((1,2),(3,4)) | 9.21954445729 - (10,10) | [(0,0),(3,0),(4,5),(1,6)] | 7.81024967591 - (10,10) | ((1,2),(3,4)) | 9.21954445729 - (10,10) | ((1,2),(3,4)) | 9.21954445729 - (10,10) | [(1,2),(3,4)] | 9.21954445729 - (10,10) | ((10,20)) | 10 - (10,10) | [(11,12),(13,14)] | 2.2360679775 - (10,10) | ((11,12),(13,14)) | 2.2360679775 +SELECT p.f1, p1.f1, p.f1 <-> p1.f1 AS dist_ppath, p1.f1 <-> p.f1 AS dist_pathp FROM POINT_TBL p, PATH_TBL p1; + f1 | f1 | dist_ppath | dist_pathp +-------------------+---------------------------+--------------------+-------------------- + (0,0) | [(1,2),(3,4)] | 2.2360679775 | 2.2360679775 + (0,0) | ((1,2),(3,4)) | 2.2360679775 | 2.2360679775 + (0,0) | [(0,0),(3,0),(4,5),(1,6)] | 0 | 0 + (0,0) | ((1,2),(3,4)) | 2.2360679775 | 2.2360679775 + (0,0) | ((1,2),(3,4)) | 2.2360679775 | 2.2360679775 + (0,0) | [(1,2),(3,4)] | 2.2360679775 | 2.2360679775 + (0,0) | ((10,20)) | 22.360679775 | 22.360679775 + (0,0) | [(11,12),(13,14)] | 16.2788205961 | 16.2788205961 + (0,0) | ((11,12),(13,14)) | 16.2788205961 | 16.2788205961 + (-10,0) | [(1,2),(3,4)] | 11.1803398875 | 11.1803398875 + (-10,0) | ((1,2),(3,4)) | 11.1803398875 | 11.1803398875 + (-10,0) | [(0,0),(3,0),(4,5),(1,6)] | 10 | 10 + (-10,0) | ((1,2),(3,4)) | 11.1803398875 | 11.1803398875 + (-10,0) | ((1,2),(3,4)) | 11.1803398875 | 11.1803398875 + (-10,0) | [(1,2),(3,4)] | 11.1803398875 | 11.1803398875 + (-10,0) | ((10,20)) | 28.2842712475 | 28.2842712475 + (-10,0) | [(11,12),(13,14)] | 24.1867732449 | 24.1867732449 + (-10,0) | ((11,12),(13,14)) | 24.1867732449 | 24.1867732449 + (-3,4) | [(1,2),(3,4)] | 4.472135955 | 4.472135955 + (-3,4) | ((1,2),(3,4)) | 4.472135955 | 4.472135955 + (-3,4) | [(0,0),(3,0),(4,5),(1,6)] | 4.472135955 | 4.472135955 + (-3,4) | ((1,2),(3,4)) | 4.472135955 | 4.472135955 + (-3,4) | ((1,2),(3,4)) | 4.472135955 | 4.472135955 + (-3,4) | [(1,2),(3,4)] | 4.472135955 | 4.472135955 + (-3,4) | ((10,20)) | 20.6155281281 | 20.6155281281 + (-3,4) | [(11,12),(13,14)] | 16.1245154966 | 16.1245154966 + (-3,4) | ((11,12),(13,14)) | 16.1245154966 | 16.1245154966 + (5.1,34.5) | [(1,2),(3,4)] | 30.5722096028 | 30.5722096028 + (5.1,34.5) | ((1,2),(3,4)) | 30.5722096028 | 30.5722096028 + (5.1,34.5) | [(0,0),(3,0),(4,5),(1,6)] | 28.793402022 | 28.793402022 + (5.1,34.5) | ((1,2),(3,4)) | 30.5722096028 | 30.5722096028 + (5.1,34.5) | ((1,2),(3,4)) | 30.5722096028 | 30.5722096028 + (5.1,34.5) | [(1,2),(3,4)] | 30.5722096028 | 30.5722096028 + (5.1,34.5) | ((10,20)) | 15.3055545473 | 15.3055545473 + (5.1,34.5) | [(11,12),(13,14)] | 21.9695243462 | 21.9695243462 + (5.1,34.5) | ((11,12),(13,14)) | 21.9695243462 | 21.9695243462 + (-5,-12) | [(1,2),(3,4)] | 15.2315462117 | 15.2315462117 + (-5,-12) | ((1,2),(3,4)) | 15.2315462117 | 15.2315462117 + (-5,-12) | [(0,0),(3,0),(4,5),(1,6)] | 13 | 13 + (-5,-12) | ((1,2),(3,4)) | 15.2315462117 | 15.2315462117 + (-5,-12) | ((1,2),(3,4)) | 15.2315462117 | 15.2315462117 + (-5,-12) | [(1,2),(3,4)] | 15.2315462117 | 15.2315462117 + (-5,-12) | ((10,20)) | 35.3411940941 | 35.3411940941 + (-5,-12) | [(11,12),(13,14)] | 28.8444102037 | 28.8444102037 + (-5,-12) | ((11,12),(13,14)) | 28.8444102037 | 28.8444102037 + (1e-300,-1e-300) | [(1,2),(3,4)] | 2.2360679775 | 2.2360679775 + (1e-300,-1e-300) | ((1,2),(3,4)) | 2.2360679775 | 2.2360679775 + (1e-300,-1e-300) | [(0,0),(3,0),(4,5),(1,6)] | 1.41421356237e-300 | 1.41421356237e-300 + (1e-300,-1e-300) | ((1,2),(3,4)) | 2.2360679775 | 2.2360679775 + (1e-300,-1e-300) | ((1,2),(3,4)) | 2.2360679775 | 2.2360679775 + (1e-300,-1e-300) | [(1,2),(3,4)] | 2.2360679775 | 2.2360679775 + (1e-300,-1e-300) | ((10,20)) | 22.360679775 | 22.360679775 + (1e-300,-1e-300) | [(11,12),(13,14)] | 16.2788205961 | 16.2788205961 + (1e-300,-1e-300) | ((11,12),(13,14)) | 16.2788205961 | 16.2788205961 + (1e+300,Infinity) | [(1,2),(3,4)] | Infinity | Infinity + (1e+300,Infinity) | ((1,2),(3,4)) | Infinity | Infinity + (1e+300,Infinity) | [(0,0),(3,0),(4,5),(1,6)] | Infinity | Infinity + (1e+300,Infinity) | ((1,2),(3,4)) | Infinity | Infinity + (1e+300,Infinity) | ((1,2),(3,4)) | Infinity | Infinity + (1e+300,Infinity) | [(1,2),(3,4)] | Infinity | Infinity + (1e+300,Infinity) | ((10,20)) | Infinity | Infinity + (1e+300,Infinity) | [(11,12),(13,14)] | Infinity | Infinity + (1e+300,Infinity) | ((11,12),(13,14)) | Infinity | Infinity + (NaN,NaN) | [(1,2),(3,4)] | NaN | NaN + (NaN,NaN) | ((1,2),(3,4)) | NaN | NaN + (NaN,NaN) | [(0,0),(3,0),(4,5),(1,6)] | NaN | NaN + (NaN,NaN) | ((1,2),(3,4)) | NaN | NaN + (NaN,NaN) | ((1,2),(3,4)) | NaN | NaN + (NaN,NaN) | [(1,2),(3,4)] | NaN | NaN + (NaN,NaN) | ((10,20)) | NaN | NaN + (NaN,NaN) | [(11,12),(13,14)] | NaN | NaN + (NaN,NaN) | ((11,12),(13,14)) | NaN | NaN + (10,10) | [(1,2),(3,4)] | 9.21954445729 | 9.21954445729 + (10,10) | ((1,2),(3,4)) | 9.21954445729 | 9.21954445729 + (10,10) | [(0,0),(3,0),(4,5),(1,6)] | 7.81024967591 | 7.81024967591 + (10,10) | ((1,2),(3,4)) | 9.21954445729 | 9.21954445729 + (10,10) | ((1,2),(3,4)) | 9.21954445729 | 9.21954445729 + (10,10) | [(1,2),(3,4)] | 9.21954445729 | 9.21954445729 + (10,10) | ((10,20)) | 10 | 10 + (10,10) | [(11,12),(13,14)] | 2.2360679775 | 2.2360679775 + (10,10) | ((11,12),(13,14)) | 2.2360679775 | 2.2360679775 (81 rows) -- Distance to polygon -SELECT p.f1, p1.f1, p.f1 <-> p1.f1 FROM POINT_TBL p, POLYGON_TBL p1; - f1 | f1 | ?column? --------------------+----------------------------+--------------- - (0,0) | ((2,0),(2,4),(0,0)) | 0 - (0,0) | ((3,1),(3,3),(1,0)) | 1 - (0,0) | ((1,2),(3,4),(5,6),(7,8)) | 2.2360679775 - (0,0) | ((7,8),(5,6),(3,4),(1,2)) | 2.2360679775 - (0,0) | ((1,2),(7,8),(5,6),(3,-4)) | 1.58113883008 - (0,0) | ((0,0)) | 0 - (0,0) | ((0,1),(0,1)) | 1 - (-10,0) | ((2,0),(2,4),(0,0)) | 10 - (-10,0) | ((3,1),(3,3),(1,0)) | 11 - (-10,0) | ((1,2),(3,4),(5,6),(7,8)) | 11.1803398875 - (-10,0) | ((7,8),(5,6),(3,4),(1,2)) | 11.1803398875 - (-10,0) | ((1,2),(7,8),(5,6),(3,-4)) | 11.1803398875 - (-10,0) | ((0,0)) | 10 - (-10,0) | ((0,1),(0,1)) | 10.0498756211 - (-3,4) | ((2,0),(2,4),(0,0)) | 4.472135955 - (-3,4) | ((3,1),(3,3),(1,0)) | 5.54700196225 - (-3,4) | ((1,2),(3,4),(5,6),(7,8)) | 4.472135955 - (-3,4) | ((7,8),(5,6),(3,4),(1,2)) | 4.472135955 - (-3,4) | ((1,2),(7,8),(5,6),(3,-4)) | 4.472135955 - (-3,4) | ((0,0)) | 5 - (-3,4) | ((0,1),(0,1)) | 4.24264068712 - (5.1,34.5) | ((2,0),(2,4),(0,0)) | 30.6571362002 - (5.1,34.5) | ((3,1),(3,3),(1,0)) | 31.5699223946 - (5.1,34.5) | ((1,2),(3,4),(5,6),(7,8)) | 26.5680258958 - (5.1,34.5) | ((7,8),(5,6),(3,4),(1,2)) | 26.5680258958 - (5.1,34.5) | ((1,2),(7,8),(5,6),(3,-4)) | 26.5680258958 - (5.1,34.5) | ((0,0)) | 34.8749193547 - (5.1,34.5) | ((0,1),(0,1)) | 33.8859853037 - (-5,-12) | ((2,0),(2,4),(0,0)) | 13 - (-5,-12) | ((3,1),(3,3),(1,0)) | 13.416407865 - (-5,-12) | ((1,2),(3,4),(5,6),(7,8)) | 15.2315462117 - (-5,-12) | ((7,8),(5,6),(3,4),(1,2)) | 15.2315462117 - (-5,-12) | ((1,2),(7,8),(5,6),(3,-4)) | 11.313708499 - (-5,-12) | ((0,0)) | 13 - (-5,-12) | ((0,1),(0,1)) | 13.9283882772 - (1e-300,-1e-300) | ((2,0),(2,4),(0,0)) | 0 - (1e-300,-1e-300) | ((3,1),(3,3),(1,0)) | 1 - (1e-300,-1e-300) | ((1,2),(3,4),(5,6),(7,8)) | 2.2360679775 - (1e-300,-1e-300) | ((7,8),(5,6),(3,4),(1,2)) | 2.2360679775 - (1e-300,-1e-300) | ((1,2),(7,8),(5,6),(3,-4)) | 1.58113883008 - (1e-300,-1e-300) | ((0,0)) | 0 - (1e-300,-1e-300) | ((0,1),(0,1)) | 1 - (1e+300,Infinity) | ((2,0),(2,4),(0,0)) | Infinity - (1e+300,Infinity) | ((3,1),(3,3),(1,0)) | Infinity - (1e+300,Infinity) | ((1,2),(3,4),(5,6),(7,8)) | Infinity - (1e+300,Infinity) | ((7,8),(5,6),(3,4),(1,2)) | Infinity - (1e+300,Infinity) | ((1,2),(7,8),(5,6),(3,-4)) | Infinity - (1e+300,Infinity) | ((0,0)) | Infinity - (1e+300,Infinity) | ((0,1),(0,1)) | Infinity - (NaN,NaN) | ((2,0),(2,4),(0,0)) | 0 - (NaN,NaN) | ((3,1),(3,3),(1,0)) | 0 - (NaN,NaN) | ((1,2),(3,4),(5,6),(7,8)) | 0 - (NaN,NaN) | ((7,8),(5,6),(3,4),(1,2)) | 0 - (NaN,NaN) | ((1,2),(7,8),(5,6),(3,-4)) | 0 - (NaN,NaN) | ((0,0)) | 0 - (NaN,NaN) | ((0,1),(0,1)) | 0 - (10,10) | ((2,0),(2,4),(0,0)) | 10 - (10,10) | ((3,1),(3,3),(1,0)) | 9.89949493661 - (10,10) | ((1,2),(3,4),(5,6),(7,8)) | 3.60555127546 - (10,10) | ((7,8),(5,6),(3,4),(1,2)) | 3.60555127546 - (10,10) | ((1,2),(7,8),(5,6),(3,-4)) | 3.60555127546 - (10,10) | ((0,0)) | 14.1421356237 - (10,10) | ((0,1),(0,1)) | 13.4536240471 +SELECT p.f1, p1.f1, p.f1 <-> p1.f1 AS dist_ppoly, p1.f1 <-> p.f1 AS dist_polyp FROM POINT_TBL p, POLYGON_TBL p1; + f1 | f1 | dist_ppoly | dist_polyp +-------------------+----------------------------+---------------+--------------- + (0,0) | ((2,0),(2,4),(0,0)) | 0 | 0 + (0,0) | ((3,1),(3,3),(1,0)) | 1 | 1 + (0,0) | ((1,2),(3,4),(5,6),(7,8)) | 2.2360679775 | 2.2360679775 + (0,0) | ((7,8),(5,6),(3,4),(1,2)) | 2.2360679775 | 2.2360679775 + (0,0) | ((1,2),(7,8),(5,6),(3,-4)) | 1.58113883008 | 1.58113883008 + (0,0) | ((0,0)) | 0 | 0 + (0,0) | ((0,1),(0,1)) | 1 | 1 + (-10,0) | ((2,0),(2,4),(0,0)) | 10 | 10 + (-10,0) | ((3,1),(3,3),(1,0)) | 11 | 11 + (-10,0) | ((1,2),(3,4),(5,6),(7,8)) | 11.1803398875 | 11.1803398875 + (-10,0) | ((7,8),(5,6),(3,4),(1,2)) | 11.1803398875 | 11.1803398875 + (-10,0) | ((1,2),(7,8),(5,6),(3,-4)) | 11.1803398875 | 11.1803398875 + (-10,0) | ((0,0)) | 10 | 10 + (-10,0) | ((0,1),(0,1)) | 10.0498756211 | 10.0498756211 + (-3,4) | ((2,0),(2,4),(0,0)) | 4.472135955 | 4.472135955 + (-3,4) | ((3,1),(3,3),(1,0)) | 5.54700196225 | 5.54700196225 + (-3,4) | ((1,2),(3,4),(5,6),(7,8)) | 4.472135955 | 4.472135955 + (-3,4) | ((7,8),(5,6),(3,4),(1,2)) | 4.472135955 | 4.472135955 + (-3,4) | ((1,2),(7,8),(5,6),(3,-4)) | 4.472135955 | 4.472135955 + (-3,4) | ((0,0)) | 5 | 5 + (-3,4) | ((0,1),(0,1)) | 4.24264068712 | 4.24264068712 + (5.1,34.5) | ((2,0),(2,4),(0,0)) | 30.6571362002 | 30.6571362002 + (5.1,34.5) | ((3,1),(3,3),(1,0)) | 31.5699223946 | 31.5699223946 + (5.1,34.5) | ((1,2),(3,4),(5,6),(7,8)) | 26.5680258958 | 26.5680258958 + (5.1,34.5) | ((7,8),(5,6),(3,4),(1,2)) | 26.5680258958 | 26.5680258958 + (5.1,34.5) | ((1,2),(7,8),(5,6),(3,-4)) | 26.5680258958 | 26.5680258958 + (5.1,34.5) | ((0,0)) | 34.8749193547 | 34.8749193547 + (5.1,34.5) | ((0,1),(0,1)) | 33.8859853037 | 33.8859853037 + (-5,-12) | ((2,0),(2,4),(0,0)) | 13 | 13 + (-5,-12) | ((3,1),(3,3),(1,0)) | 13.416407865 | 13.416407865 + (-5,-12) | ((1,2),(3,4),(5,6),(7,8)) | 15.2315462117 | 15.2315462117 + (-5,-12) | ((7,8),(5,6),(3,4),(1,2)) | 15.2315462117 | 15.2315462117 + (-5,-12) | ((1,2),(7,8),(5,6),(3,-4)) | 11.313708499 | 11.313708499 + (-5,-12) | ((0,0)) | 13 | 13 + (-5,-12) | ((0,1),(0,1)) | 13.9283882772 | 13.9283882772 + (1e-300,-1e-300) | ((2,0),(2,4),(0,0)) | 0 | 0 + (1e-300,-1e-300) | ((3,1),(3,3),(1,0)) | 1 | 1 + (1e-300,-1e-300) | ((1,2),(3,4),(5,6),(7,8)) | 2.2360679775 | 2.2360679775 + (1e-300,-1e-300) | ((7,8),(5,6),(3,4),(1,2)) | 2.2360679775 | 2.2360679775 + (1e-300,-1e-300) | ((1,2),(7,8),(5,6),(3,-4)) | 1.58113883008 | 1.58113883008 + (1e-300,-1e-300) | ((0,0)) | 0 | 0 + (1e-300,-1e-300) | ((0,1),(0,1)) | 1 | 1 + (1e+300,Infinity) | ((2,0),(2,4),(0,0)) | Infinity | Infinity + (1e+300,Infinity) | ((3,1),(3,3),(1,0)) | Infinity | Infinity + (1e+300,Infinity) | ((1,2),(3,4),(5,6),(7,8)) | Infinity | Infinity + (1e+300,Infinity) | ((7,8),(5,6),(3,4),(1,2)) | Infinity | Infinity + (1e+300,Infinity) | ((1,2),(7,8),(5,6),(3,-4)) | Infinity | Infinity + (1e+300,Infinity) | ((0,0)) | Infinity | Infinity + (1e+300,Infinity) | ((0,1),(0,1)) | Infinity | Infinity + (NaN,NaN) | ((2,0),(2,4),(0,0)) | 0 | 0 + (NaN,NaN) | ((3,1),(3,3),(1,0)) | 0 | 0 + (NaN,NaN) | ((1,2),(3,4),(5,6),(7,8)) | 0 | 0 + (NaN,NaN) | ((7,8),(5,6),(3,4),(1,2)) | 0 | 0 + (NaN,NaN) | ((1,2),(7,8),(5,6),(3,-4)) | 0 | 0 + (NaN,NaN) | ((0,0)) | 0 | 0 + (NaN,NaN) | ((0,1),(0,1)) | 0 | 0 + (10,10) | ((2,0),(2,4),(0,0)) | 10 | 10 + (10,10) | ((3,1),(3,3),(1,0)) | 9.89949493661 | 9.89949493661 + (10,10) | ((1,2),(3,4),(5,6),(7,8)) | 3.60555127546 | 3.60555127546 + (10,10) | ((7,8),(5,6),(3,4),(1,2)) | 3.60555127546 | 3.60555127546 + (10,10) | ((1,2),(7,8),(5,6),(3,-4)) | 3.60555127546 | 3.60555127546 + (10,10) | ((0,0)) | 14.1421356237 | 14.1421356237 + (10,10) | ((0,1),(0,1)) | 13.4536240471 | 13.4536240471 (63 rows) -- Closest point to line @@ -1253,6 +1253,8 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2; -- Distance to box SELECT l.s, b.f1, l.s <-> b.f1 FROM LINE_TBL l, BOX_TBL b; ERROR: function "dist_lb" not implemented +SELECT l.s, b.f1, b.f1 <-> l.s FROM LINE_TBL l, BOX_TBL b; +ERROR: function "dist_bl" not implemented -- Intersect with line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?# l2.s; s | s @@ -1849,89 +1851,89 @@ SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s ?-| l2.s; (2 rows) -- Distance to line -SELECT l.s, l1.s, l.s <-> l1.s FROM LSEG_TBL l, LINE_TBL l1; - s | s | ?column? --------------------------------+---------------------------------------+---------------- - [(1,2),(3,4)] | {0,-1,5} | 1 - [(0,0),(6,6)] | {0,-1,5} | 0 - [(10,-10),(-3,-4)] | {0,-1,5} | 9 - [(-1000000,200),(300000,-40)] | {0,-1,5} | 0 - [(11,22),(33,44)] | {0,-1,5} | 17 - [(-10,2),(-10,3)] | {0,-1,5} | 2 - [(0,-20),(30,-20)] | {0,-1,5} | 25 - [(NaN,1),(NaN,90)] | {0,-1,5} | NaN - [(1,2),(3,4)] | {1,0,5} | 6 - [(0,0),(6,6)] | {1,0,5} | 5 - [(10,-10),(-3,-4)] | {1,0,5} | 2 - [(-1000000,200),(300000,-40)] | {1,0,5} | 0 - [(11,22),(33,44)] | {1,0,5} | 16 - [(-10,2),(-10,3)] | {1,0,5} | 5 - [(0,-20),(30,-20)] | {1,0,5} | 5 - [(NaN,1),(NaN,90)] | {1,0,5} | NaN - [(1,2),(3,4)] | {0,3,0} | 2 - [(0,0),(6,6)] | {0,3,0} | 0 - [(10,-10),(-3,-4)] | {0,3,0} | 4 - [(-1000000,200),(300000,-40)] | {0,3,0} | 0 - [(11,22),(33,44)] | {0,3,0} | 22 - [(-10,2),(-10,3)] | {0,3,0} | 2 - [(0,-20),(30,-20)] | {0,3,0} | 20 - [(NaN,1),(NaN,90)] | {0,3,0} | NaN - [(1,2),(3,4)] | {1,-1,0} | 0.707106781187 - [(0,0),(6,6)] | {1,-1,0} | 0 - [(10,-10),(-3,-4)] | {1,-1,0} | 0.707106781187 - [(-1000000,200),(300000,-40)] | {1,-1,0} | 0 - [(11,22),(33,44)] | {1,-1,0} | 7.77817459305 - [(-10,2),(-10,3)] | {1,-1,0} | 8.48528137424 - [(0,-20),(30,-20)] | {1,-1,0} | 14.1421356237 - [(NaN,1),(NaN,90)] | {1,-1,0} | NaN - [(1,2),(3,4)] | {-0.4,-1,-6} | 7.79920420344 - [(0,0),(6,6)] | {-0.4,-1,-6} | 5.57086014531 - [(10,-10),(-3,-4)] | {-0.4,-1,-6} | 0 - [(-1000000,200),(300000,-40)] | {-0.4,-1,-6} | 0 - [(11,22),(33,44)] | {-0.4,-1,-6} | 30.0826447847 - [(-10,2),(-10,3)] | {-0.4,-1,-6} | 3.71390676354 - [(0,-20),(30,-20)] | {-0.4,-1,-6} | 1.85695338177 - [(NaN,1),(NaN,90)] | {-0.4,-1,-6} | NaN - [(1,2),(3,4)] | {-0.000184615384615,-1,15.3846153846} | 11.3840613445 - [(0,0),(6,6)] | {-0.000184615384615,-1,15.3846153846} | 9.3835075324 - [(10,-10),(-3,-4)] | {-0.000184615384615,-1,15.3846153846} | 19.3851689004 - [(-1000000,200),(300000,-40)] | {-0.000184615384615,-1,15.3846153846} | 0 - [(11,22),(33,44)] | {-0.000184615384615,-1,15.3846153846} | 6.61741527185 - [(-10,2),(-10,3)] | {-0.000184615384615,-1,15.3846153846} | 12.3864613274 - [(0,-20),(30,-20)] | {-0.000184615384615,-1,15.3846153846} | 35.3790763202 - [(NaN,1),(NaN,90)] | {-0.000184615384615,-1,15.3846153846} | NaN - [(1,2),(3,4)] | {3,NaN,5} | NaN - [(0,0),(6,6)] | {3,NaN,5} | NaN - [(10,-10),(-3,-4)] | {3,NaN,5} | NaN - [(-1000000,200),(300000,-40)] | {3,NaN,5} | NaN - [(11,22),(33,44)] | {3,NaN,5} | NaN - [(-10,2),(-10,3)] | {3,NaN,5} | NaN - [(0,-20),(30,-20)] | {3,NaN,5} | NaN - [(NaN,1),(NaN,90)] | {3,NaN,5} | NaN - [(1,2),(3,4)] | {NaN,NaN,NaN} | NaN - [(0,0),(6,6)] | {NaN,NaN,NaN} | NaN - [(10,-10),(-3,-4)] | {NaN,NaN,NaN} | NaN - [(-1000000,200),(300000,-40)] | {NaN,NaN,NaN} | NaN - [(11,22),(33,44)] | {NaN,NaN,NaN} | NaN - [(-10,2),(-10,3)] | {NaN,NaN,NaN} | NaN - [(0,-20),(30,-20)] | {NaN,NaN,NaN} | NaN - [(NaN,1),(NaN,90)] | {NaN,NaN,NaN} | NaN - [(1,2),(3,4)] | {0,-1,3} | 0 - [(0,0),(6,6)] | {0,-1,3} | 0 - [(10,-10),(-3,-4)] | {0,-1,3} | 7 - [(-1000000,200),(300000,-40)] | {0,-1,3} | 0 - [(11,22),(33,44)] | {0,-1,3} | 19 - [(-10,2),(-10,3)] | {0,-1,3} | 0 - [(0,-20),(30,-20)] | {0,-1,3} | 23 - [(NaN,1),(NaN,90)] | {0,-1,3} | NaN - [(1,2),(3,4)] | {-1,0,3} | 0 - [(0,0),(6,6)] | {-1,0,3} | 0 - [(10,-10),(-3,-4)] | {-1,0,3} | 0 - [(-1000000,200),(300000,-40)] | {-1,0,3} | 0 - [(11,22),(33,44)] | {-1,0,3} | 8 - [(-10,2),(-10,3)] | {-1,0,3} | 13 - [(0,-20),(30,-20)] | {-1,0,3} | 0 - [(NaN,1),(NaN,90)] | {-1,0,3} | NaN +SELECT l.s, l1.s, l.s <-> l1.s AS dist_sl, l1.s <-> l.s AS dist_ls FROM LSEG_TBL l, LINE_TBL l1; + s | s | dist_sl | dist_ls +-------------------------------+---------------------------------------+----------------+---------------- + [(1,2),(3,4)] | {0,-1,5} | 1 | 1 + [(0,0),(6,6)] | {0,-1,5} | 0 | 0 + [(10,-10),(-3,-4)] | {0,-1,5} | 9 | 9 + [(-1000000,200),(300000,-40)] | {0,-1,5} | 0 | 0 + [(11,22),(33,44)] | {0,-1,5} | 17 | 17 + [(-10,2),(-10,3)] | {0,-1,5} | 2 | 2 + [(0,-20),(30,-20)] | {0,-1,5} | 25 | 25 + [(NaN,1),(NaN,90)] | {0,-1,5} | NaN | NaN + [(1,2),(3,4)] | {1,0,5} | 6 | 6 + [(0,0),(6,6)] | {1,0,5} | 5 | 5 + [(10,-10),(-3,-4)] | {1,0,5} | 2 | 2 + [(-1000000,200),(300000,-40)] | {1,0,5} | 0 | 0 + [(11,22),(33,44)] | {1,0,5} | 16 | 16 + [(-10,2),(-10,3)] | {1,0,5} | 5 | 5 + [(0,-20),(30,-20)] | {1,0,5} | 5 | 5 + [(NaN,1),(NaN,90)] | {1,0,5} | NaN | NaN + [(1,2),(3,4)] | {0,3,0} | 2 | 2 + [(0,0),(6,6)] | {0,3,0} | 0 | 0 + [(10,-10),(-3,-4)] | {0,3,0} | 4 | 4 + [(-1000000,200),(300000,-40)] | {0,3,0} | 0 | 0 + [(11,22),(33,44)] | {0,3,0} | 22 | 22 + [(-10,2),(-10,3)] | {0,3,0} | 2 | 2 + [(0,-20),(30,-20)] | {0,3,0} | 20 | 20 + [(NaN,1),(NaN,90)] | {0,3,0} | NaN | NaN + [(1,2),(3,4)] | {1,-1,0} | 0.707106781187 | 0.707106781187 + [(0,0),(6,6)] | {1,-1,0} | 0 | 0 + [(10,-10),(-3,-4)] | {1,-1,0} | 0.707106781187 | 0.707106781187 + [(-1000000,200),(300000,-40)] | {1,-1,0} | 0 | 0 + [(11,22),(33,44)] | {1,-1,0} | 7.77817459305 | 7.77817459305 + [(-10,2),(-10,3)] | {1,-1,0} | 8.48528137424 | 8.48528137424 + [(0,-20),(30,-20)] | {1,-1,0} | 14.1421356237 | 14.1421356237 + [(NaN,1),(NaN,90)] | {1,-1,0} | NaN | NaN + [(1,2),(3,4)] | {-0.4,-1,-6} | 7.79920420344 | 7.79920420344 + [(0,0),(6,6)] | {-0.4,-1,-6} | 5.57086014531 | 5.57086014531 + [(10,-10),(-3,-4)] | {-0.4,-1,-6} | 0 | 0 + [(-1000000,200),(300000,-40)] | {-0.4,-1,-6} | 0 | 0 + [(11,22),(33,44)] | {-0.4,-1,-6} | 30.0826447847 | 30.0826447847 + [(-10,2),(-10,3)] | {-0.4,-1,-6} | 3.71390676354 | 3.71390676354 + [(0,-20),(30,-20)] | {-0.4,-1,-6} | 1.85695338177 | 1.85695338177 + [(NaN,1),(NaN,90)] | {-0.4,-1,-6} | NaN | NaN + [(1,2),(3,4)] | {-0.000184615384615,-1,15.3846153846} | 11.3840613445 | 11.3840613445 + [(0,0),(6,6)] | {-0.000184615384615,-1,15.3846153846} | 9.3835075324 | 9.3835075324 + [(10,-10),(-3,-4)] | {-0.000184615384615,-1,15.3846153846} | 19.3851689004 | 19.3851689004 + [(-1000000,200),(300000,-40)] | {-0.000184615384615,-1,15.3846153846} | 0 | 0 + [(11,22),(33,44)] | {-0.000184615384615,-1,15.3846153846} | 6.61741527185 | 6.61741527185 + [(-10,2),(-10,3)] | {-0.000184615384615,-1,15.3846153846} | 12.3864613274 | 12.3864613274 + [(0,-20),(30,-20)] | {-0.000184615384615,-1,15.3846153846} | 35.3790763202 | 35.3790763202 + [(NaN,1),(NaN,90)] | {-0.000184615384615,-1,15.3846153846} | NaN | NaN + [(1,2),(3,4)] | {3,NaN,5} | NaN | NaN + [(0,0),(6,6)] | {3,NaN,5} | NaN | NaN + [(10,-10),(-3,-4)] | {3,NaN,5} | NaN | NaN + [(-1000000,200),(300000,-40)] | {3,NaN,5} | NaN | NaN + [(11,22),(33,44)] | {3,NaN,5} | NaN | NaN + [(-10,2),(-10,3)] | {3,NaN,5} | NaN | NaN + [(0,-20),(30,-20)] | {3,NaN,5} | NaN | NaN + [(NaN,1),(NaN,90)] | {3,NaN,5} | NaN | NaN + [(1,2),(3,4)] | {NaN,NaN,NaN} | NaN | NaN + [(0,0),(6,6)] | {NaN,NaN,NaN} | NaN | NaN + [(10,-10),(-3,-4)] | {NaN,NaN,NaN} | NaN | NaN + [(-1000000,200),(300000,-40)] | {NaN,NaN,NaN} | NaN | NaN + [(11,22),(33,44)] | {NaN,NaN,NaN} | NaN | NaN + [(-10,2),(-10,3)] | {NaN,NaN,NaN} | NaN | NaN + [(0,-20),(30,-20)] | {NaN,NaN,NaN} | NaN | NaN + [(NaN,1),(NaN,90)] | {NaN,NaN,NaN} | NaN | NaN + [(1,2),(3,4)] | {0,-1,3} | 0 | 0 + [(0,0),(6,6)] | {0,-1,3} | 0 | 0 + [(10,-10),(-3,-4)] | {0,-1,3} | 7 | 7 + [(-1000000,200),(300000,-40)] | {0,-1,3} | 0 | 0 + [(11,22),(33,44)] | {0,-1,3} | 19 | 19 + [(-10,2),(-10,3)] | {0,-1,3} | 0 | 0 + [(0,-20),(30,-20)] | {0,-1,3} | 23 | 23 + [(NaN,1),(NaN,90)] | {0,-1,3} | NaN | NaN + [(1,2),(3,4)] | {-1,0,3} | 0 | 0 + [(0,0),(6,6)] | {-1,0,3} | 0 | 0 + [(10,-10),(-3,-4)] | {-1,0,3} | 0 | 0 + [(-1000000,200),(300000,-40)] | {-1,0,3} | 0 | 0 + [(11,22),(33,44)] | {-1,0,3} | 8 | 8 + [(-10,2),(-10,3)] | {-1,0,3} | 13 | 13 + [(0,-20),(30,-20)] | {-1,0,3} | 0 | 0 + [(NaN,1),(NaN,90)] | {-1,0,3} | NaN | NaN (80 rows) -- Distance to line segment @@ -2005,49 +2007,49 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LSEG_TBL l1, LSEG_TBL l2; (64 rows) -- Distance to box -SELECT l.s, b.f1, l.s <-> b.f1 FROM LSEG_TBL l, BOX_TBL b; - s | f1 | ?column? --------------------------------+---------------------+---------------- - [(1,2),(3,4)] | (2,2),(0,0) | 0 - [(1,2),(3,4)] | (3,3),(1,1) | 0 - [(1,2),(3,4)] | (-2,2),(-8,-10) | 3 - [(1,2),(3,4)] | (2.5,3.5),(2.5,2.5) | 0 - [(1,2),(3,4)] | (3,3),(3,3) | 0.707106781187 - [(0,0),(6,6)] | (2,2),(0,0) | 0 - [(0,0),(6,6)] | (3,3),(1,1) | 0 - [(0,0),(6,6)] | (-2,2),(-8,-10) | 2 - [(0,0),(6,6)] | (2.5,3.5),(2.5,2.5) | 0 - [(0,0),(6,6)] | (3,3),(3,3) | 0 - [(10,-10),(-3,-4)] | (2,2),(0,0) | 4.88901207039 - [(10,-10),(-3,-4)] | (3,3),(1,1) | 6.21602963235 - [(10,-10),(-3,-4)] | (-2,2),(-8,-10) | 0 - [(10,-10),(-3,-4)] | (2.5,3.5),(2.5,2.5) | 8.20655597529 - [(10,-10),(-3,-4)] | (3,3),(3,3) | 8.87006475627 - [(-1000000,200),(300000,-40)] | (2,2),(0,0) | 13.3842459258 - [(-1000000,200),(300000,-40)] | (3,3),(1,1) | 12.3840613274 - [(-1000000,200),(300000,-40)] | (-2,2),(-8,-10) | 13.3849843873 - [(-1000000,200),(300000,-40)] | (2.5,3.5),(2.5,2.5) | 11.8841536436 - [(-1000000,200),(300000,-40)] | (3,3),(3,3) | 12.3840613274 - [(11,22),(33,44)] | (2,2),(0,0) | 21.9317121995 - [(11,22),(33,44)] | (3,3),(1,1) | 20.6155281281 - [(11,22),(33,44)] | (-2,2),(-8,-10) | 23.8537208838 - [(11,22),(33,44)] | (2.5,3.5),(2.5,2.5) | 20.3592730715 - [(11,22),(33,44)] | (3,3),(3,3) | 20.6155281281 - [(-10,2),(-10,3)] | (2,2),(0,0) | 10 - [(-10,2),(-10,3)] | (3,3),(1,1) | 11 - [(-10,2),(-10,3)] | (-2,2),(-8,-10) | 2 - [(-10,2),(-10,3)] | (2.5,3.5),(2.5,2.5) | 12.5 - [(-10,2),(-10,3)] | (3,3),(3,3) | 13 - [(0,-20),(30,-20)] | (2,2),(0,0) | 20 - [(0,-20),(30,-20)] | (3,3),(1,1) | 21 - [(0,-20),(30,-20)] | (-2,2),(-8,-10) | 10.1980390272 - [(0,-20),(30,-20)] | (2.5,3.5),(2.5,2.5) | 22.5 - [(0,-20),(30,-20)] | (3,3),(3,3) | 23 - [(NaN,1),(NaN,90)] | (2,2),(0,0) | NaN - [(NaN,1),(NaN,90)] | (3,3),(1,1) | NaN - [(NaN,1),(NaN,90)] | (-2,2),(-8,-10) | NaN - [(NaN,1),(NaN,90)] | (2.5,3.5),(2.5,2.5) | NaN - [(NaN,1),(NaN,90)] | (3,3),(3,3) | NaN +SELECT l.s, b.f1, l.s <-> b.f1 AS dist_sb, b.f1 <-> l.s AS dist_bs FROM LSEG_TBL l, BOX_TBL b; + s | f1 | dist_sb | dist_bs +-------------------------------+---------------------+----------------+---------------- + [(1,2),(3,4)] | (2,2),(0,0) | 0 | 0 + [(1,2),(3,4)] | (3,3),(1,1) | 0 | 0 + [(1,2),(3,4)] | (-2,2),(-8,-10) | 3 | 3 + [(1,2),(3,4)] | (2.5,3.5),(2.5,2.5) | 0 | 0 + [(1,2),(3,4)] | (3,3),(3,3) | 0.707106781187 | 0.707106781187 + [(0,0),(6,6)] | (2,2),(0,0) | 0 | 0 + [(0,0),(6,6)] | (3,3),(1,1) | 0 | 0 + [(0,0),(6,6)] | (-2,2),(-8,-10) | 2 | 2 + [(0,0),(6,6)] | (2.5,3.5),(2.5,2.5) | 0 | 0 + [(0,0),(6,6)] | (3,3),(3,3) | 0 | 0 + [(10,-10),(-3,-4)] | (2,2),(0,0) | 4.88901207039 | 4.88901207039 + [(10,-10),(-3,-4)] | (3,3),(1,1) | 6.21602963235 | 6.21602963235 + [(10,-10),(-3,-4)] | (-2,2),(-8,-10) | 0 | 0 + [(10,-10),(-3,-4)] | (2.5,3.5),(2.5,2.5) | 8.20655597529 | 8.20655597529 + [(10,-10),(-3,-4)] | (3,3),(3,3) | 8.87006475627 | 8.87006475627 + [(-1000000,200),(300000,-40)] | (2,2),(0,0) | 13.3842459258 | 13.3842459258 + [(-1000000,200),(300000,-40)] | (3,3),(1,1) | 12.3840613274 | 12.3840613274 + [(-1000000,200),(300000,-40)] | (-2,2),(-8,-10) | 13.3849843873 | 13.3849843873 + [(-1000000,200),(300000,-40)] | (2.5,3.5),(2.5,2.5) | 11.8841536436 | 11.8841536436 + [(-1000000,200),(300000,-40)] | (3,3),(3,3) | 12.3840613274 | 12.3840613274 + [(11,22),(33,44)] | (2,2),(0,0) | 21.9317121995 | 21.9317121995 + [(11,22),(33,44)] | (3,3),(1,1) | 20.6155281281 | 20.6155281281 + [(11,22),(33,44)] | (-2,2),(-8,-10) | 23.8537208838 | 23.8537208838 + [(11,22),(33,44)] | (2.5,3.5),(2.5,2.5) | 20.3592730715 | 20.3592730715 + [(11,22),(33,44)] | (3,3),(3,3) | 20.6155281281 | 20.6155281281 + [(-10,2),(-10,3)] | (2,2),(0,0) | 10 | 10 + [(-10,2),(-10,3)] | (3,3),(1,1) | 11 | 11 + [(-10,2),(-10,3)] | (-2,2),(-8,-10) | 2 | 2 + [(-10,2),(-10,3)] | (2.5,3.5),(2.5,2.5) | 12.5 | 12.5 + [(-10,2),(-10,3)] | (3,3),(3,3) | 13 | 13 + [(0,-20),(30,-20)] | (2,2),(0,0) | 20 | 20 + [(0,-20),(30,-20)] | (3,3),(1,1) | 21 | 21 + [(0,-20),(30,-20)] | (-2,2),(-8,-10) | 10.1980390272 | 10.1980390272 + [(0,-20),(30,-20)] | (2.5,3.5),(2.5,2.5) | 22.5 | 22.5 + [(0,-20),(30,-20)] | (3,3),(3,3) | 23 | 23 + [(NaN,1),(NaN,90)] | (2,2),(0,0) | NaN | NaN + [(NaN,1),(NaN,90)] | (3,3),(1,1) | NaN | NaN + [(NaN,1),(NaN,90)] | (-2,2),(-8,-10) | NaN | NaN + [(NaN,1),(NaN,90)] | (2.5,3.5),(2.5,2.5) | NaN | NaN + [(NaN,1),(NaN,90)] | (3,3),(3,3) | NaN | NaN (40 rows) -- Intersect with line segment diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out index 6151ed327c75..4c0e05090a9b 100644 --- a/src/test/regress/expected/gist.out +++ b/src/test/regress/expected/gist.out @@ -212,6 +212,82 @@ select b from gist_tbl where b <@ box(point(5,5), point(6,6)); (6,6),(6,6) (21 rows) +-- Also test an index-only knn-search +explain (costs off) +select b from gist_tbl where b <@ box(point(5,5), point(6,6)) +order by b <-> point(5.2, 5.91); + QUERY PLAN +------------------------------------------------------ + Index Only Scan using gist_tbl_box_index on gist_tbl + Index Cond: (b <@ '(6,6),(5,5)'::box) + Order By: (b <-> '(5.2,5.91)'::point) +(3 rows) + +select b from gist_tbl where b <@ box(point(5,5), point(6,6)) +order by b <-> point(5.2, 5.91); + b +------------------------- + (5.55,5.55),(5.55,5.55) + (5.6,5.6),(5.6,5.6) + (5.5,5.5),(5.5,5.5) + (5.65,5.65),(5.65,5.65) + (5.45,5.45),(5.45,5.45) + (5.7,5.7),(5.7,5.7) + (5.4,5.4),(5.4,5.4) + (5.75,5.75),(5.75,5.75) + (5.35,5.35),(5.35,5.35) + (5.8,5.8),(5.8,5.8) + (5.3,5.3),(5.3,5.3) + (5.85,5.85),(5.85,5.85) + (5.25,5.25),(5.25,5.25) + (5.9,5.9),(5.9,5.9) + (5.2,5.2),(5.2,5.2) + (5.95,5.95),(5.95,5.95) + (5.15,5.15),(5.15,5.15) + (6,6),(6,6) + (5.1,5.1),(5.1,5.1) + (5.05,5.05),(5.05,5.05) + (5,5),(5,5) +(21 rows) + +-- Check commuted case as well +explain (costs off) +select b from gist_tbl where b <@ box(point(5,5), point(6,6)) +order by point(5.2, 5.91) <-> b; + QUERY PLAN +------------------------------------------------------ + Index Only Scan using gist_tbl_box_index on gist_tbl + Index Cond: (b <@ '(6,6),(5,5)'::box) + Order By: (b <-> '(5.2,5.91)'::point) +(3 rows) + +select b from gist_tbl where b <@ box(point(5,5), point(6,6)) +order by point(5.2, 5.91) <-> b; + b +------------------------- + (5.55,5.55),(5.55,5.55) + (5.6,5.6),(5.6,5.6) + (5.5,5.5),(5.5,5.5) + (5.65,5.65),(5.65,5.65) + (5.45,5.45),(5.45,5.45) + (5.7,5.7),(5.7,5.7) + (5.4,5.4),(5.4,5.4) + (5.75,5.75),(5.75,5.75) + (5.35,5.35),(5.35,5.35) + (5.8,5.8),(5.8,5.8) + (5.3,5.3),(5.3,5.3) + (5.85,5.85),(5.85,5.85) + (5.25,5.25),(5.25,5.25) + (5.9,5.9),(5.9,5.9) + (5.2,5.2),(5.2,5.2) + (5.95,5.95),(5.95,5.95) + (5.15,5.15),(5.15,5.15) + (6,6),(6,6) + (5.1,5.1),(5.1,5.1) + (5.05,5.05),(5.05,5.05) + (5,5),(5,5) +(21 rows) + drop index gist_tbl_box_index; -- Test that an index-only scan is not chosen, when the query involves the -- circle column (the circle opclass does not support index-only scans). diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index e082cd208951..6b71f251929a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -1547,6 +1547,61 @@ explain (costs off) -> Function Scan on gstest_data (10 rows) +-- Verify that we correctly handle the child node returning a +-- non-minimal slot, which happens if the input is pre-sorted, +-- e.g. due to an index scan. +BEGIN; +SET LOCAL enable_hashagg = false; +EXPLAIN (COSTS OFF) SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; + QUERY PLAN +--------------------------------------- + Sort + Sort Key: a, b + -> GroupAggregate + Group Key: a + Group Key: () + Sort Key: b + Group Key: b + -> Sort + Sort Key: a + -> Seq Scan on gstest3 +(10 rows) + +SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; + a | b | count | max | max +---+---+-------+-----+----- + 1 | | 1 | 1 | 1 + 2 | | 1 | 2 | 2 + | 1 | 1 | 1 | 1 + | 2 | 1 | 2 | 2 + | | 2 | 2 | 2 +(5 rows) + +SET LOCAL enable_seqscan = false; +EXPLAIN (COSTS OFF) SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; + QUERY PLAN +------------------------------------------------------ + Sort + Sort Key: a, b + -> GroupAggregate + Group Key: a + Group Key: () + Sort Key: b + Group Key: b + -> Index Scan using gstest3_pkey on gstest3 +(8 rows) + +SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; + a | b | count | max | max +---+---+-------+-----+----- + 1 | | 1 | 1 | 1 + 2 | | 1 | 2 | 2 + | 1 | 1 | 1 | 1 + | 2 | 1 | 2 | 2 + | | 2 | 2 | 2 +(5 rows) + +COMMIT; -- More rescan tests -- start_ignore -- GPDB_95_MERGE_FIXME: the lateral query with grouping sets do not make right plans diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out index efc334ee277f..7d09c4bcdc35 100644 --- a/src/test/regress/expected/identity.out +++ b/src/test/regress/expected/identity.out @@ -399,3 +399,8 @@ CREATE TABLE itest_child PARTITION OF itest_parent ( ) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error ERROR: identity columns are not supported on partitions DROP TABLE itest_parent; +-- test that sequence of half-dropped serial column is properly ignored +CREATE TABLE itest14 (id serial); +ALTER TABLE itest14 ALTER id DROP DEFAULT; +ALTER TABLE itest14 ALTER id ADD GENERATED BY DEFAULT AS IDENTITY; +INSERT INTO itest14 (id) VALUES (DEFAULT); diff --git a/src/test/regress/expected/join_hash.out b/src/test/regress/expected/join_hash.out index 532c8f577ca1..a60d267054aa 100644 --- a/src/test/regress/expected/join_hash.out +++ b/src/test/regress/expected/join_hash.out @@ -9,6 +9,7 @@ begin; set allow_system_table_mods=on; set local min_parallel_table_scan_size = 0; set local parallel_setup_cost = 0; +set local enable_hashjoin = on; -- Extract bucket and batch counts from an explain analyze plan. In -- general we can't make assertions about how many batches (or -- buckets) will be required because it can vary, but we can in some diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out index ab86595fc021..fc71a72fed31 100644 --- a/src/test/regress/expected/money.out +++ b/src/test/regress/expected/money.out @@ -1,6 +1,8 @@ -- -- MONEY -- +-- Note that we assume lc_monetary has been set to C. +-- CREATE TABLE money_data (m money); INSERT INTO money_data VALUES ('123'); SELECT * FROM money_data; @@ -476,7 +478,7 @@ SELECT (-12345678901234567)::numeric::money; -$12,345,678,901,234,567.00 (1 row) --- Cast from money +-- Cast from money to numeric SELECT '12345678901234567'::money::numeric; numeric ---------------------- @@ -489,3 +491,15 @@ SELECT '-12345678901234567'::money::numeric; -12345678901234567.00 (1 row) +SELECT '92233720368547758.07'::money::numeric; + numeric +---------------------- + 92233720368547758.07 +(1 row) + +SELECT '-92233720368547758.08'::money::numeric; + numeric +----------------------- + -92233720368547758.08 +(1 row) + diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out index de75b9a7dd35..d6d147015631 100644 --- a/src/test/regress/expected/object_address.out +++ b/src/test/regress/expected/object_address.out @@ -42,7 +42,10 @@ ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM CREATE TRANSFORM FOR int LANGUAGE SQL ( FROM SQL WITH FUNCTION prsd_lextype(internal), TO SQL WITH FUNCTION int4recv(internal)); +-- suppress warning that depends on wal_level +SET client_min_messages = 'ERROR'; CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; +RESET client_min_messages; CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable; @@ -394,7 +397,7 @@ WITH objects (type, name, args) AS (VALUES ('collation', '{default}', '{}'), ('table constraint', '{addr_nsp, gentable, a_chk}', '{}'), ('domain constraint', '{addr_nsp.gendomain}', '{domconstr}'), - ('conversion', '{pg_catalog, ascii_to_mic}', '{}'), + ('conversion', '{pg_catalog, koi8_r_to_mic}', '{}'), ('default value', '{addr_nsp, gentable, b}', '{}'), ('language', '{plpgsql}', '{}'), -- large object @@ -468,7 +471,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*, cast | | | (bigint AS integer) | t table constraint | addr_nsp | | a_chk on addr_nsp.gentable | t domain constraint | addr_nsp | | domconstr on addr_nsp.gendomain | t - conversion | pg_catalog | ascii_to_mic | pg_catalog.ascii_to_mic | t + conversion | pg_catalog | koi8_r_to_mic | pg_catalog.koi8_r_to_mic | t language | | plpgsql | plpgsql | t schema | | addr_nsp | addr_nsp | t operator class | pg_catalog | int4_ops | pg_catalog.int4_ops USING btree | t diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index ec05f65ae5ad..8a7ba4a26f2b 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -755,6 +755,7 @@ sha224(bytea) sha256(bytea) sha384(bytea) sha512(bytea) +gen_random_uuid() starts_with(text,text) macaddr8_eq(macaddr8,macaddr8) macaddr8_lt(macaddr8,macaddr8) diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 414ce2da578a..177fdf6e7824 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -1279,6 +1279,19 @@ explain (costs off) select * from boolpart where a is not unknown; Optimizer: Postgres query optimizer (9 rows) +create table boolrangep (a bool, b bool, c int) partition by range (a,b,c); +create table boolrangep_tf partition of boolrangep for values from ('true', 'false', 0) to ('true', 'false', 100); +create table boolrangep_ft partition of boolrangep for values from ('false', 'true', 0) to ('false', 'true', 100); +create table boolrangep_ff1 partition of boolrangep for values from ('false', 'false', 0) to ('false', 'false', 50); +create table boolrangep_ff2 partition of boolrangep for values from ('false', 'false', 50) to ('false', 'false', 100); +-- try a more complex case that's been known to trip up pruning in the past +explain (costs off) select * from boolrangep where not a and not b and c = 25; + QUERY PLAN +---------------------------------------------- + Seq Scan on boolrangep_ff1 + Filter: ((NOT a) AND (NOT b) AND (c = 25)) +(2 rows) + -- test scalar-to-array operators create table coercepart (a varchar) partition by list (a); create table coercepart_ab partition of coercepart for values in ('ab'); @@ -1660,7 +1673,7 @@ explain (costs off) select * from rparted_by_int2 where a > 100000000000000; Optimizer: Postgres query optimizer (4 rows) -drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; +drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; -- -- Test Partition pruning for HASH partitioning -- diff --git a/src/test/regress/expected/pg_lsn.out b/src/test/regress/expected/pg_lsn.out index ebcf82edb10e..1ec0afbd55e7 100644 --- a/src/test/regress/expected/pg_lsn.out +++ b/src/test/regress/expected/pg_lsn.out @@ -26,6 +26,13 @@ INSERT INTO PG_LSN_TBL VALUES ('/ABCD'); ERROR: invalid input syntax for type pg_lsn: "/ABCD" LINE 1: INSERT INTO PG_LSN_TBL VALUES ('/ABCD'); ^ +-- Min/Max aggregation +SELECT MIN(f1), MAX(f1) FROM PG_LSN_TBL; + min | max +-----+------------------- + 0/0 | FFFFFFFF/FFFFFFFF +(1 row) + DROP TABLE PG_LSN_TBL; -- Operators SELECT '0/16AE7F8' = '0/16AE7F8'::pg_lsn; diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 5f37c830fd2a..7a27f546f234 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4272,6 +4272,25 @@ drop table psql_serial_tab; \pset format aligned \pset expanded off \pset border 1 +-- \echo and allied features +\echo this is a test +this is a test +\echo -n without newline +without newline\echo with -n newline +with -n newline +\echo '-n' with newline +-n with newline +\set foo bar +\echo foo = :foo +foo = bar +\qecho this is a test +this is a test +\qecho foo = :foo +foo = bar +\warn this is a test +this is a test +\warn foo = :foo +foo = bar -- tests for \if ... \endif \if true select 'okay'; @@ -4824,3 +4843,15 @@ drop schema testpart; set search_path to default; set role to default; drop role regress_partitioning_role; +-- \d on toast table (use pg_statistic's toast table, which has a known name) +\d pg_toast.pg_toast_2619 +TOAST table "pg_toast.pg_toast_2619" + Column | Type +------------+--------- + chunk_id | oid + chunk_seq | integer + chunk_data | bytea +Owning table: "pg_catalog.pg_statistic" +Indexes: + "pg_toast_2619_index" PRIMARY KEY, btree (chunk_id, chunk_seq) + diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 0e5e8f2b9293..feb51e4add7f 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -5,7 +5,10 @@ CREATE ROLE regress_publication_user LOGIN SUPERUSER; CREATE ROLE regress_publication_user2; CREATE ROLE regress_publication_user_dummy LOGIN NOSUPERUSER; SET SESSION AUTHORIZATION 'regress_publication_user'; +-- suppress warning that depends on wal_level +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_default; +RESET client_min_messages; COMMENT ON PUBLICATION testpub_default IS 'test publication'; SELECT obj_description(p.oid, 'pg_publication') FROM pg_publication p; obj_description @@ -13,7 +16,9 @@ SELECT obj_description(p.oid, 'pg_publication') FROM pg_publication p; test publication (1 row) +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpib_ins_trunct WITH (publish = insert); +RESET client_min_messages; ALTER PUBLICATION testpub_default SET (publish = update); -- error cases CREATE PUBLICATION testpub_xxx WITH (foo); @@ -43,7 +48,9 @@ CREATE TABLE testpub_tbl1 (id serial primary key, data text); CREATE TABLE pub_test.testpub_nopk (foo int, bar int); CREATE VIEW testpub_view AS SELECT 1; CREATE TABLE testpub_parted (a int) PARTITION BY LIST (a); +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_foralltables FOR ALL TABLES WITH (publish = 'insert'); +RESET client_min_messages; ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update'); CREATE TABLE testpub_tbl2 (id serial primary key, data text); -- fail - can't add to for all tables publication @@ -86,8 +93,10 @@ DROP TABLE testpub_tbl2; DROP PUBLICATION testpub_foralltables; CREATE TABLE testpub_tbl3 (a int); CREATE TABLE testpub_tbl3a (b text) INHERITS (testpub_tbl3); +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3; CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3; +RESET client_min_messages; \dRp+ testpub3 Publication testpub3 Owner | All tables | Inserts | Updates | Deletes | Truncates @@ -111,7 +120,9 @@ DROP PUBLICATION testpub3, testpub4; CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_view; ERROR: "testpub_view" is not a table DETAIL: Only tables can be added to publications. +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1, pub_test.testpub_nopk; +RESET client_min_messages; -- fail - already added ALTER PUBLICATION testpub_fortbl ADD TABLE testpub_tbl1; ERROR: relation "testpub_tbl1" is already member of publication "testpub_fortbl" @@ -196,7 +207,9 @@ ERROR: permission denied for database regression SET ROLE regress_publication_user; GRANT CREATE ON DATABASE regression TO regress_publication_user2; SET ROLE regress_publication_user2; +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub2; -- ok +RESET client_min_messages; ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1; -- fail ERROR: must be owner of table testpub_tbl1 SET ROLE regress_publication_user; diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out index 765274cffad3..ef5fdf95fca0 100644 --- a/src/test/regress/expected/rangetypes.out +++ b/src/test/regress/expected/rangetypes.out @@ -658,6 +658,30 @@ select daterange('2000-01-10'::date, '2000-01-11'::date, '(]'); [01-11-2000,01-12-2000) (1 row) +select daterange('-infinity'::date, '2000-01-01'::date, '()'); + daterange +------------------------ + (-infinity,01-01-2000) +(1 row) + +select daterange('-infinity'::date, '2000-01-01'::date, '[)'); + daterange +------------------------ + [-infinity,01-01-2000) +(1 row) + +select daterange('2000-01-01'::date, 'infinity'::date, '[)'); + daterange +----------------------- + [01-01-2000,infinity) +(1 row) + +select daterange('2000-01-01'::date, 'infinity'::date, '[]'); + daterange +----------------------- + [01-01-2000,infinity] +(1 row) + -- test GiST index that's been built incrementally create table test_range_gist(ir int4range); create index test_range_gist_idx on test_range_gist using gist (ir); diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index e533222037ae..84db30a7b6f3 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -92,6 +92,8 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; Indexes: "test_replica_identity_pkey" PRIMARY KEY, btree (keya) REPLICA IDENTITY "test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3)) + "test_replica_identity_hash" hash (nonkey) + "test_replica_identity_keyab" btree (keya, keyb) "test_replica_identity_keyab_key" UNIQUE, btree (keya, keyb) "test_replica_identity_nonkey" UNIQUE, btree (keya, nonkey) "test_replica_identity_partial" UNIQUE, btree (keya, keyb) WHERE keyb <> '3'::text @@ -123,6 +125,8 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; Indexes: "test_replica_identity_pkey" PRIMARY KEY, btree (keya) "test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3)) + "test_replica_identity_hash" hash (nonkey) + "test_replica_identity_keyab" btree (keya, keyb) "test_replica_identity_keyab_key" UNIQUE, btree (keya, keyb) REPLICA IDENTITY "test_replica_identity_nonkey" UNIQUE, btree (keya, nonkey) "test_replica_identity_partial" UNIQUE, btree (keya, keyb) WHERE keyb <> '3'::text @@ -172,13 +176,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; Indexes: "test_replica_identity_pkey" PRIMARY KEY, btree (keya) "test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3)) + "test_replica_identity_hash" hash (nonkey) + "test_replica_identity_keyab" btree (keya, keyb) "test_replica_identity_keyab_key" UNIQUE, btree (keya, keyb) "test_replica_identity_nonkey" UNIQUE, btree (keya, nonkey) "test_replica_identity_partial" UNIQUE, btree (keya, keyb) WHERE keyb <> '3'::text "test_replica_identity_unique_defer" UNIQUE CONSTRAINT, btree (keya, keyb) DEFERRABLE "test_replica_identity_unique_nondefer" UNIQUE CONSTRAINT, btree (keya, keyb) - "test_replica_identity_hash" hash (nonkey) - "test_replica_identity_keyab" btree (keya, keyb) Replica Identity: FULL Distributed by: (keya) diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index a9d65c2ac9cd..0201f5aad0c4 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -169,6 +169,8 @@ pg_user_mapping|t point_tbl|t polygon_tbl|t quad_box_tbl|t +quad_box_tbl_ord_seq1|f +quad_box_tbl_ord_seq2|f quad_point_tbl|t quad_poly_tbl|t radix_text_tbl|t diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out index d3ad4e91af25..ac281df4ed80 100644 --- a/src/test/regress/expected/stats_ext.out +++ b/src/test/regress/expected/stats_ext.out @@ -128,13 +128,13 @@ VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2; -- Ensure non-inherited stats are not applied to inherited query -- Without stats object, it looks like this SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2'); - estimated | actual + estimated | actual -----------+-------- 400 | 150 (1 row) SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0'); - estimated | actual + estimated | actual -----------+-------- 9 | 40 (1 row) @@ -144,7 +144,7 @@ VACUUM ANALYZE stxdinh, stxdinh1, stxdinh2; -- Since the stats object does not include inherited stats, it should not -- affect the estimates SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2'); - estimated | actual + estimated | actual -----------+-------- 400 | 150 (1 row) @@ -152,7 +152,7 @@ SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* GROUP BY 1, 2'); -- Dependencies are applied at individual relations (within append), so -- this estimate changes a bit because we improve estimates for the parent SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinh* WHERE a = 0 AND b = 0'); - estimated | actual + estimated | actual -----------+-------- 26 | 40 (1 row) @@ -165,13 +165,13 @@ INSERT INTO stxdinp SELECT 1, a/100, a/100 FROM generate_series(1, 999) a; CREATE STATISTICS stxdinp ON a, b FROM stxdinp; VACUUM ANALYZE stxdinp; -- partitions are processed recursively SELECT 1 FROM pg_statistic_ext WHERE stxrelid = 'stxdinp'::regclass; - ?column? + ?column? ---------- 1 (1 row) SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinp GROUP BY 1, 2'); - estimated | actual + estimated | actual -----------+-------- 10 | 10 (1 row) @@ -691,9 +691,35 @@ SELECT m.* pg_mcv_list_items(d.stxdmcv) m WHERE s.stxname = 'mcv_lists_stats' AND d.stxoid = s.oid; - index | values | nulls | frequency | base_frequency --------+-----------+---------+-----------+---------------- - 0 | {1, 2, 3} | {f,f,f} | 1 | 1 + index | values | nulls | frequency | base_frequency +-------+---------+---------+-----------+---------------- + 0 | {1,2,3} | {f,f,f} | 1 | 1 +(1 row) + +-- 2 distinct combinations with NULL values, all in the MCV list +TRUNCATE mcv_lists; +DROP STATISTICS mcv_lists_stats; +INSERT INTO mcv_lists (a, b, c, d) + SELECT + (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 0 END), + (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 'x' END), + (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 0 END), + (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 'x' END) + FROM generate_series(1,5000) s(i); +ANALYZE mcv_lists; +SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x'''); + estimated | actual +-----------+-------- + 3750 | 2500 +(1 row) + +-- create statistics +CREATE STATISTICS mcv_lists_stats (mcv) ON b, d FROM mcv_lists; +ANALYZE mcv_lists; +SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x'''); + estimated | actual +-----------+-------- + 2500 | 2500 (1 row) -- mcv with arrays diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 40e76ed7f966..0e9316a00ab1 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1966,6 +1966,30 @@ NOTICE: trigger zzz on parted_trig_1_1 AFTER INSERT for ROW NOTICE: trigger bbb on parted_trig_2 AFTER INSERT for ROW NOTICE: trigger zzz on parted_trig_2 AFTER INSERT for ROW drop table parted_trig; +-- Verify propagation of trigger arguments to partitions +create table parted_trig (a int) partition by list (a); +create table parted_trig1 partition of parted_trig for values in (1); +create or replace function trigger_notice() returns trigger as $$ + declare + arg1 text = TG_ARGV[0]; + arg2 integer = TG_ARGV[1]; + begin + raise notice 'trigger % on % % % for % args % %', + TG_NAME, TG_TABLE_NAME, TG_WHEN, TG_OP, TG_LEVEL, arg1, arg2; + return null; + end; + $$ language plpgsql; +create trigger aaa after insert on parted_trig + for each row execute procedure trigger_notice('quirky', 1); +-- Verify propagation of trigger arguments to partitions attached after creating trigger +create table parted_trig2 partition of parted_trig for values in (2); +create table parted_trig3 (like parted_trig); +alter table parted_trig attach partition parted_trig3 for values in (3); +insert into parted_trig values (1), (2), (3); +NOTICE: trigger aaa on parted_trig1 AFTER INSERT for ROW args quirky 1 +NOTICE: trigger aaa on parted_trig2 AFTER INSERT for ROW args quirky 1 +NOTICE: trigger aaa on parted_trig3 AFTER INSERT for ROW args quirky 1 +drop table parted_trig; -- test irregular partitions (i.e., different column definitions), -- including that the WHEN clause works create function bark(text) returns bool language plpgsql immutable diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index d59e1913205f..2eb8c588406b 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -224,6 +224,21 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) RETURNING *; ERROR: modification of distribution columns in OnConflictUpdate is not supported +-- ON CONFLICT using system attributes in RETURNING, testing both the +-- inserting and updating paths. See bug report at: +-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au +CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$; +INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a) + DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) + RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct; +ERROR: modification of distribution columns in OnConflictUpdate is not supported +-- currently xmax is set after a conflict - that's probably not good, +-- but it seems worthwhile to have to be explicit if that changes. +INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a) + DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) + RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct; +ERROR: modification of distribution columns in OnConflictUpdate is not supported +DROP FUNCTION xid_current(); DROP TABLE update_test; DROP TABLE upsert_test; --------------------------- diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out index db66dc723efc..090103df48ab 100644 --- a/src/test/regress/expected/uuid.out +++ b/src/test/regress/expected/uuid.out @@ -145,5 +145,15 @@ SELECT COUNT(*) FROM guid1 g1 LEFT JOIN guid2 g2 ON g1.guid_field = g2.guid_fiel 1 (1 row) +-- generation test +TRUNCATE guid1; +INSERT INTO guid1 (guid_field) VALUES (gen_random_uuid()); +INSERT INTO guid1 (guid_field) VALUES (gen_random_uuid()); +SELECT count(DISTINCT guid_field) FROM guid1; + count +------- + 2 +(1 row) + -- clean up DROP TABLE guid1, guid2 CASCADE; diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source index 80f61672c0f4..7e225e6d860d 100644 --- a/src/test/regress/input/tablespace.source +++ b/src/test/regress/input/tablespace.source @@ -48,6 +48,10 @@ CREATE INDEX foo_idx on testschema.foo(i) TABLESPACE regress_tblspace; SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c where c.reltablespace = t.oid AND c.relname = 'foo_idx'; +-- check \d output +\d testschema.foo +\d testschema.foo_idx + -- -- partitioned table -- @@ -93,7 +97,12 @@ CREATE INDEX part_a_idx ON testschema.part (a) TABLESPACE regress_tblspace; CREATE TABLE testschema.part2 PARTITION OF testschema.part FOR VALUES IN (2); SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c where c.reltablespace = t.oid AND c.relname LIKE 'part%_idx'; +\d testschema.part +\d+ testschema.part +\d testschema.part1 +\d+ testschema.part1 \d testschema.part_a_idx +\d+ testschema.part_a_idx -- partitioned rels cannot specify the default tablespace. These fail: CREATE TABLE testschema.dflt (a int PRIMARY KEY) PARTITION BY LIST (a) TABLESPACE pg_default; diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source index 22a8b91a6bb5..7e3f995c11dc 100644 --- a/src/test/regress/output/tablespace.source +++ b/src/test/regress/output/tablespace.source @@ -65,6 +65,24 @@ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c foo_idx | regress_tblspace (1 row) +-- check \d output +\d testschema.foo + Table "testschema.foo" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + i | integer | | | +Indexes: + "foo_idx" btree (i), tablespace "regress_tblspace" +Tablespace: "regress_tblspace" + +\d testschema.foo_idx + Index "testschema.foo_idx" + Column | Type | Key? | Definition +--------+---------+------+------------ + i | integer | yes | i +btree, for table "testschema.foo" +Tablespace: "regress_tblspace" + -- -- partitioned table -- @@ -130,12 +148,63 @@ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c part_a_idx | regress_tblspace (3 rows) +\d testschema.part + Partitioned table "testschema.part" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | +Partition key: LIST (a) +Indexes: + "part_a_idx" btree (a), tablespace "regress_tblspace" +Number of partitions: 2 (Use \d+ to list them.) + +\d+ testschema.part + Partitioned table "testschema.part" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+---------+--------------+------------- + a | integer | | | | plain | | +Partition key: LIST (a) +Indexes: + "part_a_idx" btree (a), tablespace "regress_tblspace" +Partitions: testschema.part1 FOR VALUES IN (1), + testschema.part2 FOR VALUES IN (2) + +\d testschema.part1 + Table "testschema.part1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | +Partition of: testschema.part FOR VALUES IN (1) +Indexes: + "part1_a_idx" btree (a), tablespace "regress_tblspace" + +\d+ testschema.part1 + Table "testschema.part1" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+---------+--------------+------------- + a | integer | | | | plain | | +Partition of: testschema.part FOR VALUES IN (1) +Partition constraint: ((a IS NOT NULL) AND (a = 1)) +Indexes: + "part1_a_idx" btree (a), tablespace "regress_tblspace" + \d testschema.part_a_idx Partitioned index "testschema.part_a_idx" Column | Type | Key? | Definition --------+---------+------+------------ a | integer | yes | a btree, for table "testschema.part" +Number of partitions: 2 (Use \d+ to list them.) +Tablespace: "regress_tblspace" + +\d+ testschema.part_a_idx + Partitioned index "testschema.part_a_idx" + Column | Type | Key? | Definition | Storage | Stats target +--------+---------+------+------------+---------+-------------- + a | integer | yes | a | plain | +btree, for table "testschema.part" +Partitions: testschema.part1_a_idx, + testschema.part2_a_idx Tablespace: "regress_tblspace" -- partitioned rels cannot specify the default tablespace. These fail: @@ -360,6 +429,7 @@ Partitioned index "testschema.test_index1" --------+--------+------+------------ val | bigint | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index2 Partitioned index "testschema.test_index2" @@ -367,6 +437,7 @@ Partitioned index "testschema.test_index2" --------+--------+------+------------ val | bigint | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" \d testschema.test_index3 @@ -375,6 +446,7 @@ Partitioned index "testschema.test_index3" --------+--------+------+------------ id | bigint | yes | id primary key, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index4 Partitioned index "testschema.test_index4" @@ -382,6 +454,7 @@ Partitioned index "testschema.test_index4" --------+--------+------+------------ id | bigint | yes | id unique, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" -- use a custom tablespace for default_tablespace @@ -394,6 +467,7 @@ Partitioned index "testschema.test_index1" --------+--------+------+------------ val | bigint | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index2 Partitioned index "testschema.test_index2" @@ -401,6 +475,7 @@ Partitioned index "testschema.test_index2" --------+--------+------+------------ val | bigint | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" \d testschema.test_index3 @@ -409,6 +484,7 @@ Partitioned index "testschema.test_index3" --------+--------+------+------------ id | bigint | yes | id primary key, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index4 Partitioned index "testschema.test_index4" @@ -416,6 +492,7 @@ Partitioned index "testschema.test_index4" --------+--------+------+------------ id | bigint | yes | id unique, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" SELECT * FROM testschema.test_default_tab_p; @@ -432,6 +509,7 @@ Partitioned index "testschema.test_index1" --------+---------+------+------------ val | integer | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index2 Partitioned index "testschema.test_index2" @@ -439,6 +517,7 @@ Partitioned index "testschema.test_index2" --------+---------+------+------------ val | integer | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" \d testschema.test_index3 @@ -447,6 +526,7 @@ Partitioned index "testschema.test_index3" --------+--------+------+------------ id | bigint | yes | id primary key, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index4 Partitioned index "testschema.test_index4" @@ -454,6 +534,7 @@ Partitioned index "testschema.test_index4" --------+--------+------+------------ id | bigint | yes | id unique, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" SELECT * FROM testschema.test_default_tab_p; @@ -472,6 +553,7 @@ Partitioned index "testschema.test_index1" --------+---------+------+------------ val | integer | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index2 Partitioned index "testschema.test_index2" @@ -479,6 +561,7 @@ Partitioned index "testschema.test_index2" --------+---------+------+------------ val | integer | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" \d testschema.test_index3 @@ -487,6 +570,7 @@ Partitioned index "testschema.test_index3" --------+--------+------+------------ id | bigint | yes | id primary key, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index4 Partitioned index "testschema.test_index4" @@ -494,6 +578,7 @@ Partitioned index "testschema.test_index4" --------+--------+------+------------ id | bigint | yes | id unique, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" -- tablespace should not change even if there is an index rewrite @@ -504,6 +589,7 @@ Partitioned index "testschema.test_index1" --------+--------+------+------------ val | bigint | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index2 Partitioned index "testschema.test_index2" @@ -511,6 +597,7 @@ Partitioned index "testschema.test_index2" --------+--------+------+------------ val | bigint | yes | val btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" \d testschema.test_index3 @@ -519,6 +606,7 @@ Partitioned index "testschema.test_index3" --------+--------+------+------------ id | bigint | yes | id primary key, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) \d testschema.test_index4 Partitioned index "testschema.test_index4" @@ -526,6 +614,7 @@ Partitioned index "testschema.test_index4" --------+--------+------+------------ id | bigint | yes | id unique, btree, for table "testschema.test_default_tab_p" +Number of partitions: 1 (Use \d+ to list them.) Tablespace: "regress_tblspace" DROP TABLE testschema.test_default_tab_p; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index bfb55f2ecdf0..f9dd83b8d65c 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -105,11 +105,12 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Another group of parallel tests # ---------- -test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tidscan +test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tidscan collate.icu.utf8 # rules cannot run concurrently with any test that creates # a view or rule in the public schema -test: psql psql_crosstab amutils stats_ext +# collate.*.utf8 tests cannot be run in parallel with each other +test: psql psql_crosstab amutils stats_ext collate.linux.utf8 # # '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 diff --git a/src/test/regress/regressplans.sh b/src/test/regress/regressplans.sh index 678ab0a3f227..31e7876daab5 100755 --- a/src/test/regress/regressplans.sh +++ b/src/test/regress/regressplans.sh @@ -72,7 +72,7 @@ mv -f regression.out planregress/out.in mv -f regression.diffs planregress/diffs.in PGOPTIONS="$PGOPTIONS -fi -fn -fh" $MAKE runtest mv -f regression.out planregress/out.inh -mv -f regression.diffsregression.planregress/inh +mv -f regression.diffs planregress/diffs.inh PGOPTIONS="$PGOPTIONS -fi -fn -fm " $MAKE runtest mv -f regression.out planregress/out.inm mv -f regression.diffs planregress/diffs.inm diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index afb77ece917a..0a947f6b0c80 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -137,11 +137,13 @@ test: misc_functions test: sysviews test: tsrf test: tidscan +test: collate.icu.utf8 test: rules test: psql test: psql_crosstab test: amutils test: stats_ext +test: collate.linux.utf8 test: select_parallel test: write_parallel test: publication diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index fa7de8d0febf..3971b276b050 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -421,9 +421,31 @@ group by t1.a,t1.b,t1.c,t1.d,t2.x,t2.z; -- Cannot optimize when PK is deferrable explain (costs off) select * from t3 group by a,b,c; -drop table t1; +create temp table t1c () inherits (t1); + +-- Ensure we don't remove any columns when t1 has a child table +explain (costs off) select * from t1 group by a,b,c,d; + +-- Okay to remove columns if we're only querying the parent. +explain (costs off) select * from only t1 group by a,b,c,d; + +create temp table p_t1 ( + a int, + b int, + c int, + d int, + primary key(a,b) +) partition by list(a); +create temp table p_t1_1 partition of p_t1 for values in(1); +create temp table p_t1_2 partition of p_t1 for values in(2); + +-- Ensure we can remove non-PK columns for partitioned tables. +explain (costs off) select * from p_t1 group by a,b,c,d; + +drop table t1 cascade; drop table t2; drop table t3; +drop table p_t1; -- -- Test combinations of DISTINCT and/or ORDER BY diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index be0dc9dab12d..e718611633ca 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -1770,7 +1770,7 @@ create operator alter1.=(procedure = alter1.same, leftarg = alter1.ctype, right create operator class alter1.ctype_hash_ops default for type alter1.ctype using hash as operator 1 alter1.=(alter1.ctype, alter1.ctype); -create conversion alter1.ascii_to_utf8 for 'sql_ascii' to 'utf8' from ascii_to_utf8; +create conversion alter1.latin1_to_utf8 for 'latin1' to 'utf8' from iso8859_1_to_utf8; create text search parser alter1.prs(start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); create text search configuration alter1.cfg(parser = alter1.prs); @@ -1791,7 +1791,7 @@ alter operator alter1.=(alter1.ctype, alter1.ctype) set schema alter2; alter function alter1.same(alter1.ctype, alter1.ctype) set schema alter2; alter type alter1.ctype set schema alter1; -- no-op, same schema alter type alter1.ctype set schema alter2; -alter conversion alter1.ascii_to_utf8 set schema alter2; +alter conversion alter1.latin1_to_utf8 set schema alter2; alter text search parser alter1.prs set schema alter2; alter text search configuration alter1.cfg set schema alter2; alter text search template alter1.tmpl set schema alter2; diff --git a/src/test/regress/sql/box.sql b/src/test/regress/sql/box.sql index d7ee2ed4b8c9..6f679c7cf39b 100644 --- a/src/test/regress/sql/box.sql +++ b/src/test/regress/sql/box.sql @@ -193,29 +193,42 @@ DROP INDEX box_spgist; -- -- Test the SP-GiST index on the larger volume of data -- -CREATE TABLE quad_box_tbl (b box); +CREATE TABLE quad_box_tbl (id int, b box); INSERT INTO quad_box_tbl - SELECT box(point(x * 10, y * 10), point(x * 10 + 5, y * 10 + 5)) - FROM generate_series(1, 100) x, - generate_series(1, 100) y; + SELECT (x - 1) * 100 + y, box(point(x * 10, y * 10), point(x * 10 + 5, y * 10 + 5)) + FROM generate_series(1, 100) x, + generate_series(1, 100) y; -- insert repeating data to test allTheSame INSERT INTO quad_box_tbl - SELECT '((200, 300),(210, 310))' - FROM generate_series(1, 1000); + SELECT i, '((200, 300),(210, 310))' + FROM generate_series(10001, 11000) AS i; INSERT INTO quad_box_tbl - VALUES - (NULL), - (NULL), - ('((-infinity,-infinity),(infinity,infinity))'), - ('((-infinity,100),(-infinity,500))'), - ('((-infinity,-infinity),(700,infinity))'); +VALUES + (11001, NULL), + (11002, NULL), + (11003, '((-infinity,-infinity),(infinity,infinity))'), + (11004, '((-infinity,100),(-infinity,500))'), + (11005, '((-infinity,-infinity),(700,infinity))'); CREATE INDEX quad_box_tbl_idx ON quad_box_tbl USING spgist(b); ANALYZE quad_box_tbl; +-- get reference results for ORDER BY distance from seq scan +SET enable_seqscan = ON; +SET enable_indexscan = OFF; +SET enable_bitmapscan = OFF; + +CREATE TABLE quad_box_tbl_ord_seq1 AS +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl; + +CREATE TABLE quad_box_tbl_ord_seq2 AS +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl WHERE b <@ box '((200,300),(500,600))'; + SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = ON; @@ -234,6 +247,39 @@ SELECT count(*) FROM quad_box_tbl WHERE b @> box '((201,301),(202,303))'; SELECT count(*) FROM quad_box_tbl WHERE b <@ box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b ~= box '((200,300),(205,305))'; +-- test ORDER BY distance +SET enable_indexscan = ON; +SET enable_bitmapscan = OFF; + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl; + +CREATE TEMP TABLE quad_box_tbl_ord_idx1 AS +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl; + +SELECT * +FROM quad_box_tbl_ord_seq1 seq FULL JOIN quad_box_tbl_ord_idx1 idx + ON seq.n = idx.n AND seq.id = idx.id AND + (seq.dist = idx.dist OR seq.dist IS NULL AND idx.dist IS NULL) +WHERE seq.id IS NULL OR idx.id IS NULL; + + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl WHERE b <@ box '((200,300),(500,600))'; + +CREATE TEMP TABLE quad_box_tbl_ord_idx2 AS +SELECT rank() OVER (ORDER BY b <-> point '123,456') n, b <-> point '123,456' dist, id +FROM quad_box_tbl WHERE b <@ box '((200,300),(500,600))'; + +SELECT * +FROM quad_box_tbl_ord_seq2 seq FULL JOIN quad_box_tbl_ord_idx2 idx + ON seq.n = idx.n AND seq.id = idx.id AND + (seq.dist = idx.dist OR seq.dist IS NULL AND idx.dist IS NULL) +WHERE seq.id IS NULL OR idx.id IS NULL; + RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql index 2be7759c92d4..46999fb9266f 100644 --- a/src/test/regress/sql/collate.icu.utf8.sql +++ b/src/test/regress/sql/collate.icu.utf8.sql @@ -2,6 +2,14 @@ * This test is for ICU collations. */ +/* skip test if not UTF8 server encoding or no ICU collations installed */ +SELECT getdatabaseencoding() <> 'UTF8' OR + (SELECT count(*) FROM pg_collation WHERE collprovider = 'i') = 0 + AS skip_test \gset +\if :skip_test +\quit +\endif + SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql index c009fd2372d9..eac2f900142b 100644 --- a/src/test/regress/sql/collate.linux.utf8.sql +++ b/src/test/regress/sql/collate.linux.utf8.sql @@ -4,6 +4,14 @@ * because other encodings don't support all the characters used. */ +SELECT getdatabaseencoding() <> 'UTF8' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE', 'tr_TR') AND collencoding = pg_char_to_encoding('UTF8')) <> 4 OR + version() !~ 'linux-gnu' + AS skip_test \gset +\if :skip_test +\quit +\endif + SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index 3ca25b1a943b..2876eade027a 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -981,6 +981,34 @@ REINDEX INDEX CONCURRENTLY concur_reindex_ind5; \d concur_reindex_tab4 DROP TABLE concur_reindex_tab4; +-- Check handling of indexes with expressions and predicates. The +-- definitions of the rebuilt indexes should match the original +-- definitions. +CREATE TABLE concur_exprs_tab (c1 int , c2 boolean); +INSERT INTO concur_exprs_tab (c1, c2) VALUES (1369652450, FALSE), + (414515746, TRUE), + (897778963, FALSE); +CREATE UNIQUE INDEX concur_exprs_index_expr + ON concur_exprs_tab ((c1::text COLLATE "C")); +CREATE UNIQUE INDEX concur_exprs_index_pred ON concur_exprs_tab (c1) + WHERE (c1::text > 500000000::text COLLATE "C"); +CREATE UNIQUE INDEX concur_exprs_index_pred_2 + ON concur_exprs_tab ((1 / c1)) + WHERE ('-H') >= (c2::TEXT) COLLATE "C"; +SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass); +SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass); +SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass); +REINDEX TABLE CONCURRENTLY concur_exprs_tab; +SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass); +SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass); +SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass); +-- ALTER TABLE recreates the indexes, which should keep their collations. +ALTER TABLE concur_exprs_tab ALTER c2 TYPE TEXT; +SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass); +SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass); +SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass); +DROP TABLE concur_exprs_tab; + -- -- REINDEX SCHEMA -- diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 069c51857f49..aab7f4ecb116 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -449,6 +449,39 @@ CREATE TABLE part2_1 PARTITION OF partitioned2 FOR VALUES FROM (-1, 'aaaaa') TO DROP TABLE partitioned, partitioned2; +-- check that dependencies of partition columns are handled correctly +create domain intdom1 as int; + +create table partitioned ( + a intdom1, + b text +) partition by range (a); + +alter table partitioned drop column a; -- fail + +drop domain intdom1; -- fail, requires cascade + +drop domain intdom1 cascade; + +table partitioned; -- gone + +-- likewise for columns used in partition expressions +create domain intdom1 as int; + +create table partitioned ( + a intdom1, + b text +) partition by range (plusone(a)); + +alter table partitioned drop column a; -- fail + +drop domain intdom1; -- fail, requires cascade + +drop domain intdom1 cascade; + +table partitioned; -- gone + + -- -- Partitions -- diff --git a/src/test/regress/sql/expressions.sql b/src/test/regress/sql/expressions.sql index 3427fdfdd728..1ca8bb151c80 100644 --- a/src/test/regress/sql/expressions.sql +++ b/src/test/regress/sql/expressions.sql @@ -1,5 +1,5 @@ -- --- expression evaluated tests that don't fit into a more specific file +-- expression evaluation tests that don't fit into a more specific file -- -- @@ -13,7 +13,9 @@ SELECT date(now())::text = current_date::text; -- current_time / localtime SELECT now()::timetz::text = current_time::text; +SELECT now()::timetz(4)::text = current_time(4)::text; SELECT now()::time::text = localtime::text; +SELECT now()::time(3)::text = localtime(3)::text; -- current_timestamp / localtimestamp (always matches because of transactional behaviour) SELECT current_timestamp = NOW(); @@ -34,3 +36,32 @@ SELECT current_schema; SET search_path = 'pg_catalog'; SELECT current_schema; RESET search_path; + + +-- +-- Tests for BETWEEN +-- + +explain (costs off) +select count(*) from date_tbl + where f1 between '1997-01-01' and '1998-01-01'; +select count(*) from date_tbl + where f1 between '1997-01-01' and '1998-01-01'; + +explain (costs off) +select count(*) from date_tbl + where f1 not between '1997-01-01' and '1998-01-01'; +select count(*) from date_tbl + where f1 not between '1997-01-01' and '1998-01-01'; + +explain (costs off) +select count(*) from date_tbl + where f1 between symmetric '1997-01-01' and '1998-01-01'; +select count(*) from date_tbl + where f1 between symmetric '1997-01-01' and '1998-01-01'; + +explain (costs off) +select count(*) from date_tbl + where f1 not between symmetric '1997-01-01' and '1998-01-01'; +select count(*) from date_tbl + where f1 not between symmetric '1997-01-01' and '1998-01-01'; diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 6c42e158bf94..f97bb996b47d 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -1667,3 +1667,15 @@ ALTER TABLE fkpart7.pkpart1 ADD PRIMARY KEY (a); ALTER TABLE fkpart7.pkpart ADD PRIMARY KEY (a); CREATE TABLE fkpart7.fk (a int REFERENCES fkpart7.pkpart); DROP SCHEMA fkpart7 CASCADE; + +-- ensure we check partitions are "not used" when dropping constraints +CREATE SCHEMA fkpart8 + CREATE TABLE tbl1(f1 int PRIMARY KEY) + CREATE TABLE tbl2(f1 int REFERENCES tbl1 DEFERRABLE INITIALLY DEFERRED) PARTITION BY RANGE(f1) + CREATE TABLE tbl2_p1 PARTITION OF tbl2 FOR VALUES FROM (minvalue) TO (maxvalue); +INSERT INTO fkpart8.tbl1 VALUES(1); +BEGIN; +INSERT INTO fkpart8.tbl2 VALUES(1); +ALTER TABLE fkpart8.tbl2 DROP CONSTRAINT tbl2_f1_fkey; +COMMIT; +DROP SCHEMA fkpart8 CASCADE; diff --git a/src/test/regress/sql/geometry.sql b/src/test/regress/sql/geometry.sql index 6e15d6bc494a..ee864850ffaf 100644 --- a/src/test/regress/sql/geometry.sql +++ b/src/test/regress/sql/geometry.sql @@ -72,19 +72,19 @@ SELECT p1.f1, p2.f1, p1.f1 / p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p2.f1 ~ AND p2.f1[0] = 0; -- Distance to line -SELECT p.f1, l.s, p.f1 <-> l.s FROM POINT_TBL p, LINE_TBL l; +SELECT p.f1, l.s, p.f1 <-> l.s AS dist_pl, l.s <-> p.f1 AS dist_lp FROM POINT_TBL p, LINE_TBL l; -- Distance to line segment -SELECT p.f1, l.s, p.f1 <-> l.s FROM POINT_TBL p, LSEG_TBL l; +SELECT p.f1, l.s, p.f1 <-> l.s AS dist_ps, l.s <-> p.f1 AS dist_sp FROM POINT_TBL p, LSEG_TBL l; -- Distance to box -SELECT p.f1, b.f1, p.f1 <-> b.f1 FROM POINT_TBL p, BOX_TBL b; +SELECT p.f1, b.f1, p.f1 <-> b.f1 AS dist_pb, b.f1 <-> p.f1 AS dist_bp FROM POINT_TBL p, BOX_TBL b; -- Distance to path -SELECT p.f1, p1.f1, p.f1 <-> p1.f1 FROM POINT_TBL p, PATH_TBL p1; +SELECT p.f1, p1.f1, p.f1 <-> p1.f1 AS dist_ppath, p1.f1 <-> p.f1 AS dist_pathp FROM POINT_TBL p, PATH_TBL p1; -- Distance to polygon -SELECT p.f1, p1.f1, p.f1 <-> p1.f1 FROM POINT_TBL p, POLYGON_TBL p1; +SELECT p.f1, p1.f1, p.f1 <-> p1.f1 AS dist_ppoly, p1.f1 <-> p.f1 AS dist_polyp FROM POINT_TBL p, POLYGON_TBL p1; -- Closest point to line SELECT p.f1, l.s, p.f1 ## l.s FROM POINT_TBL p, LINE_TBL l; @@ -128,6 +128,7 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2; -- Distance to box SELECT l.s, b.f1, l.s <-> b.f1 FROM LINE_TBL l, BOX_TBL b; +SELECT l.s, b.f1, b.f1 <-> l.s FROM LINE_TBL l, BOX_TBL b; -- Intersect with line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?# l2.s; @@ -192,13 +193,13 @@ SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s ?|| l2.s; SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s ?-| l2.s; -- Distance to line -SELECT l.s, l1.s, l.s <-> l1.s FROM LSEG_TBL l, LINE_TBL l1; +SELECT l.s, l1.s, l.s <-> l1.s AS dist_sl, l1.s <-> l.s AS dist_ls FROM LSEG_TBL l, LINE_TBL l1; -- Distance to line segment SELECT l1.s, l2.s, l1.s <-> l2.s FROM LSEG_TBL l1, LSEG_TBL l2; -- Distance to box -SELECT l.s, b.f1, l.s <-> b.f1 FROM LSEG_TBL l, BOX_TBL b; +SELECT l.s, b.f1, l.s <-> b.f1 AS dist_sb, b.f1 <-> l.s AS dist_bs FROM LSEG_TBL l, BOX_TBL b; -- Intersect with line segment SELECT l.s, l1.s FROM LSEG_TBL l, LINE_TBL l1 WHERE l.s ?# l1.s; diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql index 603fda5da264..0e2f77df706c 100644 --- a/src/test/regress/sql/gist.sql +++ b/src/test/regress/sql/gist.sql @@ -117,6 +117,22 @@ select b from gist_tbl where b <@ box(point(5,5), point(6,6)); -- execute the same select b from gist_tbl where b <@ box(point(5,5), point(6,6)); +-- Also test an index-only knn-search +explain (costs off) +select b from gist_tbl where b <@ box(point(5,5), point(6,6)) +order by b <-> point(5.2, 5.91); + +select b from gist_tbl where b <@ box(point(5,5), point(6,6)) +order by b <-> point(5.2, 5.91); + +-- Check commuted case as well +explain (costs off) +select b from gist_tbl where b <@ box(point(5,5), point(6,6)) +order by point(5.2, 5.91) <-> b; + +select b from gist_tbl where b <@ box(point(5,5), point(6,6)) +order by point(5.2, 5.91) <-> b; + drop index gist_tbl_box_index; -- Test that an index-only scan is not chosen, when the query involves the diff --git a/src/test/regress/sql/groupingsets.sql b/src/test/regress/sql/groupingsets.sql index c1d2f406bfff..9e1192eaf953 100644 --- a/src/test/regress/sql/groupingsets.sql +++ b/src/test/regress/sql/groupingsets.sql @@ -486,6 +486,18 @@ explain (costs off) from (values (1),(2)) v(x), gstest_data(v.x) group by cube (a,b) order by a,b; +-- Verify that we correctly handle the child node returning a +-- non-minimal slot, which happens if the input is pre-sorted, +-- e.g. due to an index scan. +BEGIN; +SET LOCAL enable_hashagg = false; +EXPLAIN (COSTS OFF) SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; +SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; +SET LOCAL enable_seqscan = false; +EXPLAIN (COSTS OFF) SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; +SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; +COMMIT; + -- More rescan tests -- start_ignore -- GPDB_95_MERGE_FIXME: the lateral query with grouping sets do not make right plans diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql index b11ba3d1b732..c5d8dcb338b5 100644 --- a/src/test/regress/sql/identity.sql +++ b/src/test/regress/sql/identity.sql @@ -254,3 +254,11 @@ CREATE TABLE itest_child PARTITION OF itest_parent ( f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY ) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error DROP TABLE itest_parent; + + +-- test that sequence of half-dropped serial column is properly ignored + +CREATE TABLE itest14 (id serial); +ALTER TABLE itest14 ALTER id DROP DEFAULT; +ALTER TABLE itest14 ALTER id ADD GENERATED BY DEFAULT AS IDENTITY; +INSERT INTO itest14 (id) VALUES (DEFAULT); diff --git a/src/test/regress/sql/join_hash.sql b/src/test/regress/sql/join_hash.sql index 13e95a5e91a9..35278f347d8b 100644 --- a/src/test/regress/sql/join_hash.sql +++ b/src/test/regress/sql/join_hash.sql @@ -12,6 +12,7 @@ set allow_system_table_mods=on; set local min_parallel_table_scan_size = 0; set local parallel_setup_cost = 0; +set local enable_hashjoin = on; -- Extract bucket and batch counts from an explain analyze plan. In -- general we can't make assertions about how many batches (or diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql index 37b9ecce1fc4..5e746286c907 100644 --- a/src/test/regress/sql/money.sql +++ b/src/test/regress/sql/money.sql @@ -1,6 +1,8 @@ -- -- MONEY -- +-- Note that we assume lc_monetary has been set to C. +-- CREATE TABLE money_data (m money); @@ -122,6 +124,8 @@ SELECT (-1234567890)::int4::money; SELECT (-12345678901234567)::int8::money; SELECT (-12345678901234567)::numeric::money; --- Cast from money +-- Cast from money to numeric SELECT '12345678901234567'::money::numeric; SELECT '-12345678901234567'::money::numeric; +SELECT '92233720368547758.07'::money::numeric; +SELECT '-92233720368547758.08'::money::numeric; diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql index bd94cd654b5d..8e06248eb5e5 100644 --- a/src/test/regress/sql/object_address.sql +++ b/src/test/regress/sql/object_address.sql @@ -45,7 +45,10 @@ ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM CREATE TRANSFORM FOR int LANGUAGE SQL ( FROM SQL WITH FUNCTION prsd_lextype(internal), TO SQL WITH FUNCTION int4recv(internal)); +-- suppress warning that depends on wal_level +SET client_min_messages = 'ERROR'; CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; +RESET client_min_messages; CREATE SUBSCRIPTION regress_addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable; @@ -164,7 +167,7 @@ WITH objects (type, name, args) AS (VALUES ('collation', '{default}', '{}'), ('table constraint', '{addr_nsp, gentable, a_chk}', '{}'), ('domain constraint', '{addr_nsp.gendomain}', '{domconstr}'), - ('conversion', '{pg_catalog, ascii_to_mic}', '{}'), + ('conversion', '{pg_catalog, koi8_r_to_mic}', '{}'), ('default value', '{addr_nsp, gentable, b}', '{}'), ('language', '{plpgsql}', '{}'), -- large object diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index 004859425c50..0425ae812b3e 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -197,6 +197,15 @@ explain (costs off) select * from boolpart where a is not true and a is not fals explain (costs off) select * from boolpart where a is unknown; explain (costs off) select * from boolpart where a is not unknown; +create table boolrangep (a bool, b bool, c int) partition by range (a,b,c); +create table boolrangep_tf partition of boolrangep for values from ('true', 'false', 0) to ('true', 'false', 100); +create table boolrangep_ft partition of boolrangep for values from ('false', 'true', 0) to ('false', 'true', 100); +create table boolrangep_ff1 partition of boolrangep for values from ('false', 'false', 0) to ('false', 'false', 50); +create table boolrangep_ff2 partition of boolrangep for values from ('false', 'false', 50) to ('false', 'false', 100); + +-- try a more complex case that's been known to trip up pruning in the past +explain (costs off) select * from boolrangep where not a and not b and c = 25; + -- test scalar-to-array operators create table coercepart (a varchar) partition by list (a); create table coercepart_ab partition of coercepart for values in ('ab'); @@ -302,7 +311,7 @@ create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values fr -- all partitions but rparted_by_int2_maxvalue pruned explain (costs off) select * from rparted_by_int2 where a > 100000000000000; -drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; +drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; -- -- Test Partition pruning for HASH partitioning diff --git a/src/test/regress/sql/pg_lsn.sql b/src/test/regress/sql/pg_lsn.sql index 746f720d6901..2c143c82ffe7 100644 --- a/src/test/regress/sql/pg_lsn.sql +++ b/src/test/regress/sql/pg_lsn.sql @@ -14,6 +14,10 @@ INSERT INTO PG_LSN_TBL VALUES ('-1/0'); INSERT INTO PG_LSN_TBL VALUES (' 0/12345678'); INSERT INTO PG_LSN_TBL VALUES ('ABCD/'); INSERT INTO PG_LSN_TBL VALUES ('/ABCD'); + +-- Min/Max aggregation +SELECT MIN(f1), MAX(f1) FROM PG_LSN_TBL; + DROP TABLE PG_LSN_TBL; -- Operators diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql index 9546ff09a80e..1af93399e1ae 100644 --- a/src/test/regress/sql/psql.sql +++ b/src/test/regress/sql/psql.sql @@ -771,6 +771,22 @@ drop table psql_serial_tab; \pset expanded off \pset border 1 +-- \echo and allied features + +\echo this is a test +\echo -n without newline +\echo with -n newline +\echo '-n' with newline + +\set foo bar +\echo foo = :foo + +\qecho this is a test +\qecho foo = :foo + +\warn this is a test +\warn foo = :foo + -- tests for \if ... \endif \if true @@ -1119,3 +1135,6 @@ set search_path to default; set role to default; drop role regress_partitioning_role; + +-- \d on toast table (use pg_statistic's toast table, which has a known name) +\d pg_toast.pg_toast_2619 diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 815410b3c5a8..5773a755cf3a 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -6,12 +6,17 @@ CREATE ROLE regress_publication_user2; CREATE ROLE regress_publication_user_dummy LOGIN NOSUPERUSER; SET SESSION AUTHORIZATION 'regress_publication_user'; +-- suppress warning that depends on wal_level +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_default; +RESET client_min_messages; COMMENT ON PUBLICATION testpub_default IS 'test publication'; SELECT obj_description(p.oid, 'pg_publication') FROM pg_publication p; +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpib_ins_trunct WITH (publish = insert); +RESET client_min_messages; ALTER PUBLICATION testpub_default SET (publish = update); @@ -32,7 +37,9 @@ CREATE TABLE pub_test.testpub_nopk (foo int, bar int); CREATE VIEW testpub_view AS SELECT 1; CREATE TABLE testpub_parted (a int) PARTITION BY LIST (a); +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_foralltables FOR ALL TABLES WITH (publish = 'insert'); +RESET client_min_messages; ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update'); CREATE TABLE testpub_tbl2 (id serial primary key, data text); @@ -52,8 +59,10 @@ DROP PUBLICATION testpub_foralltables; CREATE TABLE testpub_tbl3 (a int); CREATE TABLE testpub_tbl3a (b text) INHERITS (testpub_tbl3); +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3; CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3; +RESET client_min_messages; \dRp+ testpub3 \dRp+ testpub4 @@ -62,7 +71,9 @@ DROP PUBLICATION testpub3, testpub4; -- fail - view CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_view; +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1, pub_test.testpub_nopk; +RESET client_min_messages; -- fail - already added ALTER PUBLICATION testpub_fortbl ADD TABLE testpub_tbl1; -- fail - already added @@ -98,7 +109,9 @@ CREATE PUBLICATION testpub2; -- fail SET ROLE regress_publication_user; GRANT CREATE ON DATABASE regression TO regress_publication_user2; SET ROLE regress_publication_user2; +SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub2; -- ok +RESET client_min_messages; ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1; -- fail diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql index 0648299e4f1d..effab32bae34 100644 --- a/src/test/regress/sql/rangetypes.sql +++ b/src/test/regress/sql/rangetypes.sql @@ -171,6 +171,10 @@ select daterange('2000-01-10'::date, '2000-01-20'::date, '(]'); select daterange('2000-01-10'::date, '2000-01-20'::date, '()'); select daterange('2000-01-10'::date, '2000-01-11'::date, '()'); select daterange('2000-01-10'::date, '2000-01-11'::date, '(]'); +select daterange('-infinity'::date, '2000-01-01'::date, '()'); +select daterange('-infinity'::date, '2000-01-01'::date, '[)'); +select daterange('2000-01-01'::date, 'infinity'::date, '[)'); +select daterange('2000-01-01'::date, 'infinity'::date, '[]'); -- test GiST index that's been built incrementally create table test_range_gist(ir int4range); diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql index cef5a759bdd1..bc5ba4352d51 100644 --- a/src/test/regress/sql/stats_ext.sql +++ b/src/test/regress/sql/stats_ext.sql @@ -120,6 +120,7 @@ SELECT 1 FROM pg_statistic_ext WHERE stxrelid = 'stxdinp'::regclass; SELECT * FROM check_estimated_rows('SELECT a, b FROM stxdinp GROUP BY 1, 2'); DROP TABLE stxdinp; + -- Verify supported object types for extended statistics CREATE schema tststats; @@ -443,6 +444,29 @@ SELECT m.* WHERE s.stxname = 'mcv_lists_stats' AND d.stxoid = s.oid; +-- 2 distinct combinations with NULL values, all in the MCV list +TRUNCATE mcv_lists; +DROP STATISTICS mcv_lists_stats; + +INSERT INTO mcv_lists (a, b, c, d) + SELECT + (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 0 END), + (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 'x' END), + (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 0 END), + (CASE WHEN mod(i,2) = 0 THEN NULL ELSE 'x' END) + FROM generate_series(1,5000) s(i); + +ANALYZE mcv_lists; + +SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x'''); + +-- create statistics +CREATE STATISTICS mcv_lists_stats (mcv) ON b, d FROM mcv_lists; + +ANALYZE mcv_lists; + +SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x'''); + -- mcv with arrays CREATE TABLE mcv_lists_arrays ( a TEXT[], diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql index f76343e421a5..0b6959db9158 100644 --- a/src/test/regress/sql/triggers.sql +++ b/src/test/regress/sql/triggers.sql @@ -1466,6 +1466,29 @@ create trigger qqq after insert on parted_trig_1_1 for each row execute procedur insert into parted_trig values (50), (1500); drop table parted_trig; +-- Verify propagation of trigger arguments to partitions +create table parted_trig (a int) partition by list (a); +create table parted_trig1 partition of parted_trig for values in (1); +create or replace function trigger_notice() returns trigger as $$ + declare + arg1 text = TG_ARGV[0]; + arg2 integer = TG_ARGV[1]; + begin + raise notice 'trigger % on % % % for % args % %', + TG_NAME, TG_TABLE_NAME, TG_WHEN, TG_OP, TG_LEVEL, arg1, arg2; + return null; + end; + $$ language plpgsql; +create trigger aaa after insert on parted_trig + for each row execute procedure trigger_notice('quirky', 1); + +-- Verify propagation of trigger arguments to partitions attached after creating trigger +create table parted_trig2 partition of parted_trig for values in (2); +create table parted_trig3 (like parted_trig); +alter table parted_trig attach partition parted_trig3 for values in (3); +insert into parted_trig values (1), (2), (3); +drop table parted_trig; + -- test irregular partitions (i.e., different column definitions), -- including that the WHEN clause works create function bark(text) returns bool language plpgsql immutable diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql index 51da75e5c232..a82f99e163da 100644 --- a/src/test/regress/sql/update.sql +++ b/src/test/regress/sql/update.sql @@ -114,6 +114,21 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) RETURNING *; +-- ON CONFLICT using system attributes in RETURNING, testing both the +-- inserting and updating paths. See bug report at: +-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au +CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$; +INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a) + DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) + RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct; +-- currently xmax is set after a conflict - that's probably not good, +-- but it seems worthwhile to have to be explicit if that changes. +INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a) + DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) + RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct; + + +DROP FUNCTION xid_current(); DROP TABLE update_test; DROP TABLE upsert_test; diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql index 518d2b75c00f..3bd3b357c77c 100644 --- a/src/test/regress/sql/uuid.sql +++ b/src/test/regress/sql/uuid.sql @@ -75,5 +75,11 @@ INSERT INTO guid2(guid_field) VALUES('3f3e3c3b3a3039383736353433a2313e'); SELECT COUNT(*) FROM guid1 g1 INNER JOIN guid2 g2 ON g1.guid_field = g2.guid_field; SELECT COUNT(*) FROM guid1 g1 LEFT JOIN guid2 g2 ON g1.guid_field = g2.guid_field WHERE g2.guid_field IS NULL; +-- generation test +TRUNCATE guid1; +INSERT INTO guid1 (guid_field) VALUES (gen_random_uuid()); +INSERT INTO guid1 (guid_field) VALUES (gen_random_uuid()); +SELECT count(DISTINCT guid_field) FROM guid1; + -- clean up DROP TABLE guid1, guid2 CASCADE; diff --git a/src/tools/FAQ2txt b/src/tools/FAQ2txt deleted file mode 100755 index ab61d4d5f8cb..000000000000 --- a/src/tools/FAQ2txt +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -# src/tools/FAQ2txt: - -# Converts doc/src/FAQ/FAQ.html to text file doc/FAQ - -lynx -force_html -dont_wrap_pre -dump -hiddenlinks=ignore -nolist "$@" diff --git a/src/tools/git_changelog b/src/tools/git_changelog index 0a714e27b324..aaf9b7c19543 100755 --- a/src/tools/git_changelog +++ b/src/tools/git_changelog @@ -57,7 +57,7 @@ require IPC::Open2; # (We could get this from "git branches", but not worth the trouble.) # NB: master must be first! my @BRANCHES = qw(master - REL_11_STABLE REL_10_STABLE REL9_6_STABLE REL9_5_STABLE + REL_12_STABLE REL_11_STABLE REL_10_STABLE REL9_6_STABLE REL9_5_STABLE REL9_4_STABLE REL9_3_STABLE REL9_2_STABLE REL9_1_STABLE REL9_0_STABLE REL8_4_STABLE REL8_3_STABLE REL8_2_STABLE REL8_1_STABLE REL8_0_STABLE REL7_4_STABLE REL7_3_STABLE REL7_2_STABLE REL7_1_STABLE REL7_0_PATCHES diff --git a/src/tools/msvc/MSBuildProject.pm b/src/tools/msvc/MSBuildProject.pm index 3538ad84ecbb..0d41a72c31ed 100644 --- a/src/tools/msvc/MSBuildProject.pm +++ b/src/tools/msvc/MSBuildProject.pm @@ -45,7 +45,19 @@ EOF $self->{guid} - $sdkversion +EOF + # Check whether WindowsSDKVersion env variable is present. + # Add WindowsTargetPlatformVersion node if so. + my $sdkVersion = $ENV{'WindowsSDKVersion'}; + if (defined($sdkVersion)) + { + # remove trailing backslash if necessary. + $sdkVersion =~ s/\\$//; + print $f <$sdkVersion +EOF + } + print $f < EOF @@ -473,4 +485,29 @@ sub new return $self; } +package VC2019Project; + +# +# Package that encapsulates a Visual C++ 2019 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '16.00'; + $self->{PlatformToolset} = 'v142'; + $self->{ToolsVersion} = '16.0'; + + return $self; +} + 1; diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index a01a3fc0eeca..594f4ca68c2a 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -319,7 +319,6 @@ sub mkvcbuild $ecpg->AddIncludeDir('src/interfaces/libpq'); $ecpg->AddPrefixInclude('src/interfaces/ecpg/preproc'); $ecpg->AddFiles('src/interfaces/ecpg/preproc', 'pgc.l', 'preproc.y'); - $ecpg->AddDefine('ECPG_COMPILE'); $ecpg->AddReference($libpgcommon, $libpgport); my $pgregress_ecpg = diff --git a/src/tools/msvc/README b/src/tools/msvc/README index 4ab81d3402fc..d22fff331d67 100644 --- a/src/tools/msvc/README +++ b/src/tools/msvc/README @@ -4,7 +4,7 @@ MSVC build ========== This directory contains the tools required to build PostgreSQL using -Microsoft Visual Studio 2013 - 2017. This builds the whole backend, not just +Microsoft Visual Studio 2013 - 2019. This builds the whole backend, not just the libpq frontend library. For more information, see the documentation chapter "Installation on Windows" and the description below. @@ -16,7 +16,7 @@ has to be installed. Since this is not included in the product originally, extra steps are needed to make it work. First, download and install a supported version of the Microsoft Windows SDK -from www.microsoft.com (v6.0 or greater). +from www.microsoft.com (v8.1a or greater). Locate the files vcprojectengine.dll.express.config and vcprojectengine.dll.config in the vc\vcpackages directory of @@ -88,11 +88,11 @@ config_default.pl to create the configuration arguments. These configuration arguments are passed over to Mkvcbuild::mkvcbuild (Mkvcbuild.pm) which creates the Visual Studio project and solution files. It does this by using VSObjectFactory::CreateSolution to create an object -implementing the Solution interface (this could be either a VS2013Solution, -or a VS2015Solution or a VS2017Solution, all in Solution.pm, depending on -the user's build environment) and adding objects implementing the corresponding -Project interface (VC2013Project or VC2015Project or VC2017Project from -MSBuildProject.pm) to it. +implementing the Solution interface (this could be either VS2013Solution, +VS2015Solution, VS2017Solution or VS2019Solution, all in Solution.pm, +depending on the user's build environment) and adding objects implementing +the corresponding Project interface (VC2013Project, VC2015Project, +VC2017Project or VC2019Project from MSBuildProject.pm) to it. When Solution::Save is called, the implementations of Solution and Project save their content in the appropriate format. The final step of starting the appropriate build program (msbuild) is diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index 9204396dc2d0..a295a7186f6b 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -891,7 +891,7 @@ sub GetFakeConfigure $cfg .= ' --without-zlib' unless ($self->{options}->{zlib}); $cfg .= ' --with-extra-version' if ($self->{options}->{extraver}); $cfg .= ' --with-openssl' if ($self->{options}->{openssl}); - $cfg .= ' --with-ossp-uuid' if ($self->{options}->{uuid}); + $cfg .= ' --with-uuid' if ($self->{options}->{uuid}); $cfg .= ' --with-libxml' if ($self->{options}->{xml}); $cfg .= ' --with-libxslt' if ($self->{options}->{xslt}); $cfg .= ' --with-gssapi' if ($self->{options}->{gss}); @@ -987,6 +987,34 @@ sub new return $self; } +package VS2019Solution; + +# +# Package that encapsulates a Visual Studio 2019 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '16.00'; + $self->{visualStudioName} = 'Visual Studio 2019'; + $self->{VisualStudioVersion} = '16.0.28729.10'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + sub GetAdditionalHeaders { my ($self, $f) = @_; diff --git a/src/tools/msvc/VSObjectFactory.pm b/src/tools/msvc/VSObjectFactory.pm index 1a94cd866eee..610dc6128669 100644 --- a/src/tools/msvc/VSObjectFactory.pm +++ b/src/tools/msvc/VSObjectFactory.pm @@ -39,16 +39,29 @@ sub CreateSolution return new VS2015Solution(@_); } - # visual 2017 hasn't changed the nmake version to 15, so adjust the check to support it. - elsif (($visualStudioVersion ge '14.10') - or ($visualStudioVersion eq '15.00')) + # The version of nmake bundled in Visual Studio 2017 is greater + # than 14.10 and less than 14.20. And the version number is + # actually 15.00. + elsif ( + ($visualStudioVersion ge '14.10' && $visualStudioVersion lt '14.20') + || $visualStudioVersion eq '15.00') { return new VS2017Solution(@_); } + + # The version of nmake bundled in Visual Studio 2019 is greater + # than 14.20 and less than 14.30. And the version number is + # actually 16.00. + elsif ( + ($visualStudioVersion ge '14.20' && $visualStudioVersion lt '14.30') + || $visualStudioVersion eq '16.00') + { + return new VS2019Solution(@_); + } else { - croak $visualStudioVersion; - croak "The requested Visual Studio version is not supported."; + croak + "The requested Visual Studio version $visualStudioVersion is not supported."; } } @@ -70,16 +83,29 @@ sub CreateProject return new VC2015Project(@_); } - # visual 2017 hasn't changed the nmake version to 15, so adjust the check to support it. - elsif (($visualStudioVersion ge '14.10') - or ($visualStudioVersion eq '15.00')) + # The version of nmake bundled in Visual Studio 2017 is greater + # than 14.10 and less than 14.20. And the version number is + # actually 15.00. + elsif ( + ($visualStudioVersion ge '14.10' && $visualStudioVersion lt '14.20') + || $visualStudioVersion eq '15.00') { return new VC2017Project(@_); } + + # The version of nmake bundled in Visual Studio 2019 is greater + # than 14.20 and less than 14.30. And the version number is + # actually 16.00. + elsif ( + ($visualStudioVersion ge '14.20' && $visualStudioVersion lt '14.30') + || $visualStudioVersion eq '16.00') + { + return new VC2019Project(@_); + } else { - croak $visualStudioVersion; - croak "The requested Visual Studio version is not supported."; + croak + "The requested Visual Studio version $visualStudioVersion is not supported."; } } @@ -106,17 +132,19 @@ sub _GetVisualStudioVersion { my ($major, $minor) = @_; - # visual 2017 hasn't changed the nmake version to 15, so still using the older version for comparison. - if ($major > 14) + # The major visual studio that is supported has nmake + # version <= 14.30, so stick with it as the latest version + # if bumping on something even newer. + if ($major >= 14 && $minor >= 30) { carp "The determined version of Visual Studio is newer than the latest supported version. Returning the latest supported version instead."; - return '14.00'; + return '14.20'; } - elsif ($major < 6) + elsif ($major < 12) { croak - "Unable to determine Visual Studio version: Visual Studio versions before 6.0 aren't supported."; + "Unable to determine Visual Studio version: Visual Studio versions before 12.0 aren't supported."; } return "$major.$minor"; } diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl index 2553636dc140..043df4c5e871 100644 --- a/src/tools/msvc/config_default.pl +++ b/src/tools/msvc/config_default.pl @@ -18,10 +18,10 @@ nls => undef, # --enable-nls= tap_tests => undef, # --enable-tap-tests tcl => undef, # --with-tcl= - perl => undef, # --with-perl + perl => undef, # --with-perl= python => undef, # --with-python= openssl => undef, # --with-openssl= - uuid => undef, # --with-ossp-uuid + uuid => undef, # --with-uuid= xml => undef, # --with-libxml= xslt => undef, # --with-libxslt= iconv => undef, # (not in configure, path to iconv) diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 076d66adb408..3de1aaee4f21 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -613,7 +613,7 @@ sub upgradecheck print "\nRunning pg_upgrade\n\n"; @args = ( 'pg_upgrade', '-d', "$data.old", '-D', $data, '-b', - $bindir, '-B', $bindir); + $bindir); system(@args) == 0 or exit 1; print "\nStarting new cluster\n\n"; @args = ('pg_ctl', '-l', "$logdir/postmaster2.log", 'start'); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 8d028bf94f01..a61b9173af06 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1983,6 +1983,7 @@ RegisteredBgWorker ReindexErrorInfo ReindexObjectType ReindexStmt +ReindexType RelFileNode RelFileNodeBackend RelIdCacheEnt diff --git a/src/tools/version_stamp.pl b/src/tools/version_stamp.pl index 1c09deba4ecc..10ddc79d49da 100755 --- a/src/tools/version_stamp.pl +++ b/src/tools/version_stamp.pl @@ -24,7 +24,7 @@ # Major version is hard-wired into the script. We update it when we branch # a new development version. -my $majorversion = 12; +my $majorversion = 13; # Validate argument and compute derived variables my $minor = shift;